1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 07:19:54 +00:00

Merge branch 'next' into HEAD

This commit is contained in:
scrawl 2013-03-26 17:44:53 +01:00
commit 2ecd2f4ecd
424 changed files with 12278 additions and 8149 deletions

1
.gitignore vendored
View file

@ -17,3 +17,4 @@ data
CMakeLists.txt.user
*.swp
*.swo
*.kate-swp

View file

@ -15,7 +15,7 @@ include (OpenMWMacros)
# Version
set (OPENMW_VERSION_MAJOR 0)
set (OPENMW_VERSION_MINOR 21)
set (OPENMW_VERSION_MINOR 22)
set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
@ -29,6 +29,7 @@ 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)
# Apps and tools
option(BUILD_BSATOOL "build BSA extractor" OFF)
option(BUILD_ESMTOOL "build ESM inspector" ON)
option(BUILD_LAUNCHER "build Launcher" ON)
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
@ -74,7 +75,6 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
set(OENGINE_OGRE
${LIBDIR}/openengine/ogre/renderer.cpp
${LIBDIR}/openengine/ogre/fader.cpp
${LIBDIR}/openengine/ogre/imagerotate.cpp
${LIBDIR}/openengine/ogre/selectionbuffer.cpp
)
set(OENGINE_GUI
@ -183,7 +183,7 @@ endif()
# find boost without components so we can use Boost_VERSION
find_package(Boost REQUIRED)
set(BOOST_COMPONENTS system filesystem program_options thread)
set(BOOST_COMPONENTS system filesystem program_options thread date_time)
if (Boost_VERSION LESS 104900)
set(SHINY_USE_WAVE_SYSTEM_INSTALL "TRUE")
@ -257,10 +257,10 @@ set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1)
# Set up Ogre plugin folder & debug suffix
if (APPLE)
# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt)
add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="")
# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt)
add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="")
else ()
add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d")
add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d")
endif()
add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}")
@ -278,9 +278,9 @@ add_subdirectory(files/mygui)
# Specify build paths
if (APPLE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS")
else (APPLE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
endif (APPLE)
# Other files
@ -300,6 +300,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
"${OpenMW_BINARY_DIR}/openmw.desktop")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.desktop
"${OpenMW_BINARY_DIR}/opencs.desktop")
endif()
# Compiler settings
@ -332,7 +334,7 @@ if(DPKG_PROGRAM)
#Install icon and desktop file
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "share/applications/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "share/pixmaps/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "share/pixmaps/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
#Install global configuration files
INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
@ -353,7 +355,7 @@ if(DPKG_PROGRAM)
Data files from the original game is required to run it.")
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW 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_SECTION "Games")
@ -380,9 +382,9 @@ if(WIN32)
"${OpenMW_SOURCE_DIR}/OFL.txt"
"${OpenMW_SOURCE_DIR}/DejaVu Font License.txt"
"${OpenMW_SOURCE_DIR}/Daedric Font License.txt"
"${OpenMW_BINARY_DIR}/launcher.qss"
"${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg"
"${OpenMW_BINARY_DIR}/Release/mwiniimport.exe"
"${OpenMW_BINARY_DIR}/Release/omwlauncher.exe"
"${OpenMW_BINARY_DIR}/Release/openmw.exe"
DESTINATION ".")
@ -408,8 +410,8 @@ if(WIN32)
SET(CPACK_NSIS_HELP_LINK "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_MUI_ICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico")
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico")
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.ico")
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.ico")
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
@ -448,6 +450,10 @@ add_subdirectory (components)
# Apps and tools
add_subdirectory( apps/openmw )
if (BUILD_BSATOOL)
add_subdirectory( apps/bsatool )
endif()
if (BUILD_ESMTOOL)
add_subdirectory( apps/esmtool )
endif()
@ -505,10 +511,10 @@ if (WIN32)
4986 # Undocumented warning that occurs in the crtdbg.h file
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'
# 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
4099 # Type mismatch, declared class or struct is defined with other type
@ -520,7 +526,7 @@ if (WIN32)
4309 # Variable overflow, trying to store 128 in a signed char for example
4355 # Using 'this' in member initialization list
4701 # Potentially uninitialized local variable used
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
)
foreach(d ${WARNINGS_DISABLE})
@ -531,9 +537,12 @@ if (WIN32)
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${WARNINGS})
set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS})
if (BUILD_LAUNCHER)
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
endif (BUILD_LAUNCHER)
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
endif (BUILD_LAUNCHER)
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})
endif (BUILD_ESMTOOL)
@ -567,8 +576,6 @@ if (APPLE)
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(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}/transparency-overrides.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
@ -660,6 +667,9 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
IF(BUILD_LAUNCHER)
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}" )
ENDIF(BUILD_ESMTOOL)
@ -671,7 +681,7 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
ENDIF(BUILD_OPENCS)
# Install icon and .desktop
INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "${ICONDIR}")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications")
# Install global configuration files
@ -682,7 +692,4 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
# Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" )
IF(BUILD_LAUNCHER)
INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" )
ENDIF(BUILD_LAUNCHER)
endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)

View 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
View 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;
}

View file

@ -23,8 +23,7 @@ struct ESMData
std::string author;
std::string description;
int version;
int type;
ESM::ESMReader::MasterList masters;
std::vector<ESM::Header::MasterData> masters;
std::deque<EsmTool::RecordBase *> mRecords;
std::map<ESM::Cell *, std::deque<ESM::CellRef> > mCellRefs;
@ -228,7 +227,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
std::cout << " Refnum: " << ref.mRefnum << std::endl;
std::cout << " ID: '" << ref.mRefID << "'\n";
std::cout << " Owner: '" << ref.mOwner << "'\n";
std::cout << " INTV: " << ref.mIntv << " NAM9: " << ref.mIntv << std::endl;
std::cout << " Uses/health: " << ref.mCharge << " NAM9: " << ref.mNam9 << std::endl;
}
}
@ -284,16 +283,13 @@ int load(Arguments& info)
info.data.author = esm.getAuthor();
info.data.description = esm.getDesc();
info.data.masters = esm.getMasters();
info.data.version = esm.getVer();
info.data.type = esm.getType();
if (!quiet)
{
std::cout << "Author: " << esm.getAuthor() << std::endl
<< "Description: " << esm.getDesc() << std::endl
<< "File format version: " << esm.getFVer() << std::endl
<< "Special flag: " << esm.getSpecial() << std::endl;
ESM::ESMReader::MasterList m = esm.getMasters();
<< "File format version: " << esm.getFVer() << std::endl;
std::vector<ESM::Header::MasterData> m = esm.getMasters();
if (!m.empty())
{
std::cout << "Masters:" << std::endl;
@ -430,9 +426,9 @@ int clone(Arguments& info)
esm.setAuthor(info.data.author);
esm.setDescription(info.data.description);
esm.setVersion(info.data.version);
esm.setType(info.data.type);
esm.setRecordCount (recordCount);
for (ESM::ESMReader::MasterList::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it)
for (std::vector<ESM::Header::MasterData>::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it)
esm.addMaster(it->name, it->size);
std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary);

View file

@ -112,14 +112,11 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
case '5': oper_str = ">="; break;
}
std::string value_str = "??";
if (ss.mType == ESM::VT_Int)
value_str = str(boost::format("%d") % ss.mI);
else if (ss.mType == ESM::VT_Float)
value_str = str(boost::format("%f") % ss.mF);
std::ostringstream stream;
stream << ss.mValue;
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;
}
@ -278,7 +275,7 @@ RecordBase::create(ESM::NAME type)
}
case ESM::REC_LOCK:
{
record = new EsmTool::Record<ESM::Tool>;
record = new EsmTool::Record<ESM::Lockpick>;
break;
}
case ESM::REC_LTEX:
@ -713,31 +710,13 @@ void Record<ESM::Faction>::print()
template<>
void Record<ESM::Global>::print()
{
// nothing to print (well, nothing that's correct anyway)
std::cout << " Type: " << mData.mType << std::endl;
std::cout << " Value: " << mData.mValue << std::endl;
std::cout << " " << mData.mValue << std::endl;
}
template<>
void Record<ESM::GameSetting>::print()
{
std::cout << " Value: ";
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 << " " << mData.mValue << std::endl;
}
template<>
@ -885,14 +864,13 @@ void Record<ESM::Light>::print()
}
template<>
void Record<ESM::Tool>::print()
void Record<ESM::Lockpick>::print()
{
std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Model: " << mData.mModel << std::endl;
std::cout << " Icon: " << mData.mIcon << std::endl;
if (mData.mScript != "")
std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Type: " << mData.mType << std::endl;
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
@ -907,8 +885,6 @@ void Record<ESM::Probe>::print()
std::cout << " Icon: " << mData.mIcon << std::endl;
if (mData.mScript != "")
std::cout << " Script: " << mData.mScript << std::endl;
// BUG? No Type Label?
std::cout << " Type: " << mData.mType << std::endl;
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
@ -923,7 +899,6 @@ void Record<ESM::Repair>::print()
std::cout << " Icon: " << mData.mIcon << std::endl;
if (mData.mScript != "")
std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Type: " << mData.mType << std::endl;
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " Quality: " << mData.mData.mQuality << std::endl;

View file

@ -104,7 +104,7 @@ namespace EsmTool
template<> void Record<ESM::CreatureLevList>::print();
template<> void Record<ESM::ItemLevList>::print();
template<> void Record<ESM::Light>::print();
template<> void Record<ESM::Tool>::print();
template<> void Record<ESM::Lockpick>::print();
template<> void Record<ESM::Probe>::print();
template<> void Record<ESM::Repair>::print();
template<> void Record<ESM::LandTexture>::print();

View file

@ -1,35 +1,55 @@
set(LAUNCHER
datafilespage.cpp
graphicspage.cpp
main.cpp
maindialog.cpp
playpage.cpp
datafilespage.cpp
utils/profilescombobox.cpp
settings/gamesettings.cpp
settings/graphicssettings.cpp
settings/launchersettings.cpp
utils/checkablemessagebox.cpp
utils/textinputdialog.cpp
launcher.rc
${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc
)
set(LAUNCHER_HEADER
datafilespage.hpp
graphicspage.hpp
maindialog.hpp
playpage.hpp
datafilespage.hpp
utils/profilescombobox.hpp
settings/gamesettings.hpp
settings/graphicssettings.hpp
settings/launchersettings.hpp
settings/settingsbase.hpp
utils/checkablemessagebox.hpp
utils/textinputdialog.hpp
)
# Headers that must be pre-processed
set(LAUNCHER_HEADER_MOC
datafilespage.hpp
graphicspage.hpp
maindialog.hpp
playpage.hpp
datafilespage.hpp
utils/profilescombobox.hpp
utils/checkablemessagebox.hpp
utils/textinputdialog.hpp
)
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC})
set(LAUNCHER_UI
${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui
${CMAKE_SOURCE_DIR}/files/ui/playpage.ui
)
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
find_package(Qt4 REQUIRED)
set(QT_USE_QTGUI 1)
@ -40,10 +60,12 @@ if(WIN32)
set(QT_USE_QTMAIN TRUE)
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_UI(UI_HDRS ${LAUNCHER_UI})
include(${QT_USE_FILE})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# Main executable
IF(OGRE_STATIC)
@ -58,8 +80,10 @@ ENDIF(OGRE_STATIC)
add_executable(omwlauncher
${GUI_TYPE}
${LAUNCHER}
${LAUNCHER_HEADER}
${RCC_SRCS}
${MOC_SRCS}
${UI_HDRS}
)
target_link_libraries(omwlauncher
@ -74,18 +98,6 @@ if(DPKG_PROGRAM)
INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher)
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)
add_definitions (--coverage)
target_link_libraries(omwlauncher gcov)

View file

@ -1,445 +1,311 @@
#include <QtGui>
#include <components/esm/esmreader.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/fileorderlist/datafileslist.hpp>
#include <components/fileorderlist/utils/lineedit.hpp>
#include <components/fileorderlist/utils/naturalsort.hpp>
#include <components/fileorderlist/utils/filedialog.hpp>
////#include "model/datafilesmodel.hpp"
////#include "model/esm/esmfile.hpp"
#include "utils/profilescombobox.hpp"
////#include "utils/filedialog.hpp"
////#include "utils/naturalsort.hpp"
#include "utils/textinputdialog.hpp"
#include "datafilespage.hpp"
#include <boost/version.hpp>
/**
* Workaround for problems with whitespaces in paths in older versions of Boost library
*/
#if (BOOST_VERSION <= 104600)
namespace boost
#include <QPushButton>
#include <QMessageBox>
#include <QCheckBox>
#include <QMenu>
#include <components/files/configurationmanager.hpp>
#include <components/fileorderlist/model/datafilesmodel.hpp>
#include <components/fileorderlist/model/pluginsproxymodel.hpp>
#include <components/fileorderlist/model/esm/esmfile.hpp>
#include <components/fileorderlist/utils/lineedit.hpp>
#include <components/fileorderlist/utils/naturalsort.hpp>
#include <components/fileorderlist/utils/profilescombobox.hpp>
#include "settings/gamesettings.hpp"
#include "settings/launchersettings.hpp"
#include "utils/textinputdialog.hpp"
DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent)
: mCfgMgr(cfg)
, mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings)
, QWidget(parent)
{
setupUi(this);
template<>
inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
{
return boost::filesystem::path(arg);
}
// Models
mDataFilesModel = new DataFilesModel(this);
} /* namespace boost */
#endif /* (BOOST_VERSION <= 104600) */
mMastersProxyModel = new QSortFilterProxyModel();
mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm"));
mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
mMastersProxyModel->setSourceModel(mDataFilesModel);
using namespace ESM;
using namespace std;
mPluginsProxyModel = new PluginsProxyModel();
mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp"));
mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
mPluginsProxyModel->setSourceModel(mDataFilesModel);
DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent)
: QWidget(parent)
, mCfgMgr(cfg)
{
mDataFilesList = new DataFilesList(mCfgMgr, this);
mFilterProxyModel = new QSortFilterProxyModel();
mFilterProxyModel->setDynamicSortFilter(true);
mFilterProxyModel->setSourceModel(mPluginsProxyModel);
// Bottom part with profile options
QLabel *profileLabel = new QLabel(tr("Current Profile: "), this);
QCheckBox checkBox;
unsigned int height = checkBox.sizeHint().height() + 4;
mProfilesComboBox = new ProfilesComboBox(this);
mProfilesComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
mProfilesComboBox->setInsertPolicy(QComboBox::NoInsert);
mProfilesComboBox->setDuplicatesEnabled(false);
mProfilesComboBox->setEditEnabled(false);
mastersTable->setModel(mMastersProxyModel);
mastersTable->setObjectName("MastersTable");
mastersTable->setContextMenuPolicy(Qt::CustomContextMenu);
mastersTable->setSortingEnabled(false);
mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows);
mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
mastersTable->setAlternatingRowColors(true);
mastersTable->horizontalHeader()->setStretchLastSection(true);
mastersTable->horizontalHeader()->hide();
mProfileToolBar = new QToolBar(this);
mProfileToolBar->setMovable(false);
mProfileToolBar->setIconSize(QSize(16, 16));
// Set the row height to the size of the checkboxes
mastersTable->verticalHeader()->setDefaultSectionSize(height);
mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
mastersTable->verticalHeader()->hide();
mProfileToolBar->addWidget(profileLabel);
mProfileToolBar->addWidget(mProfilesComboBox);
pluginsTable->setModel(mFilterProxyModel);
pluginsTable->setObjectName("PluginsTable");
pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu);
pluginsTable->setSortingEnabled(false);
pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
pluginsTable->setAlternatingRowColors(true);
pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
pluginsTable->horizontalHeader()->setStretchLastSection(true);
pluginsTable->horizontalHeader()->hide();
QVBoxLayout *pageLayout = new QVBoxLayout(this);
pluginsTable->verticalHeader()->setDefaultSectionSize(height);
pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
pageLayout->addWidget(mDataFilesList);
pageLayout->addWidget(mProfileToolBar);
// Adjust the tableview widths inside the splitter
QList<int> sizeList;
sizeList << mLauncherSettings.value(QString("General/MastersTable/width"), QString("200")).toInt();
sizeList << mLauncherSettings.value(QString("General/PluginTable/width"), QString("340")).toInt();
splitter->setSizes(sizeList);
// Create a dialog for the new profile name input
mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString)));
connect(mProfilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString)));
connect(mProfilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString)));
connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
connect(pluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
connect(mastersTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews()));
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter()));
createActions();
setupConfig();
setupDataFiles();
}
void DataFilesPage::createActions()
{
// Refresh the plugins
QAction *refreshAction = new QAction(tr("Refresh"), this);
refreshAction->setShortcut(QKeySequence(tr("F5")));
connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh()));
// Profile actions
mNewProfileAction = new QAction(QIcon::fromTheme("document-new"), tr("&New Profile"), this);
mNewProfileAction->setToolTip(tr("New Profile"));
mNewProfileAction->setShortcut(QKeySequence(tr("Ctrl+N")));
connect(mNewProfileAction, SIGNAL(triggered()), this, SLOT(newProfile()));
// Add the actions to the toolbuttons
newProfileButton->setDefaultAction(newProfileAction);
deleteProfileButton->setDefaultAction(deleteProfileAction);
mDeleteProfileAction = new QAction(QIcon::fromTheme("edit-delete"), tr("Delete Profile"), this);
mDeleteProfileAction->setToolTip(tr("Delete Profile"));
mDeleteProfileAction->setShortcut(QKeySequence(tr("Delete")));
connect(mDeleteProfileAction, SIGNAL(triggered()), this, SLOT(deleteProfile()));
// Add the newly created actions to the toolbar
mProfileToolBar->addSeparator();
mProfileToolBar->addAction(mNewProfileAction);
mProfileToolBar->addAction(mDeleteProfileAction);
// Context menu actions
mContextMenu = new QMenu(this);
mContextMenu->addAction(checkAction);
mContextMenu->addAction(uncheckAction);
}
void DataFilesPage::setupConfig()
void DataFilesPage::setupDataFiles()
{
// Open our config file
QString config = QString::fromStdString((mCfgMgr.getUserPath() / "launcher.cfg").string());
mLauncherConfig = new QSettings(config, QSettings::IniFormat);
// Set the encoding to the one found in openmw.cfg or the default
mDataFilesModel->setEncoding(mGameSettings.value(QString("encoding"), QString("win1252")));
// Make sure we have no groups open
while (!mLauncherConfig->group().isEmpty()) {
mLauncherConfig->endGroup();
QStringList paths = mGameSettings.getDataDirs();
foreach (const QString &path, paths) {
mDataFilesModel->addFiles(path);
}
mLauncherConfig->beginGroup("Profiles");
QStringList profiles = mLauncherConfig->childGroups();
QString dataLocal = mGameSettings.getDataLocal();
if (!dataLocal.isEmpty())
mDataFilesModel->addFiles(dataLocal);
// Add the profiles to the combobox
foreach (const QString &profile, profiles) {
// Sort by date accessed for now
mDataFilesModel->sort(3);
if (profile.contains(QRegExp("[^a-zA-Z0-9_]")))
continue; // Profile name contains garbage
QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/"));
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
if (!profiles.isEmpty())
profilesComboBox->addItems(profiles);
qDebug() << "adding " << profile;
mProfilesComboBox->addItem(profile);
}
// Add the current profile if empty
if (profilesComboBox->findText(profile) == -1 && !profile.isEmpty())
profilesComboBox->addItem(profile);
// Add a default profile
if (mProfilesComboBox->findText(QString("Default")) == -1) {
mProfilesComboBox->addItem(QString("Default"));
}
QString currentProfile = mLauncherConfig->value("CurrentProfile").toString();
if (currentProfile.isEmpty()) {
// No current profile selected
currentProfile = "Default";
}
const int currentIndex = mProfilesComboBox->findText(currentProfile);
if (currentIndex != -1) {
// Profile is found
mProfilesComboBox->setCurrentIndex(currentIndex);
}
mLauncherConfig->endGroup();
}
void DataFilesPage::readConfig()
{
QString profile = mProfilesComboBox->currentText();
// Make sure we have no groups open
while (!mLauncherConfig->group().isEmpty()) {
mLauncherConfig->endGroup();
}
mLauncherConfig->beginGroup("Profiles");
mLauncherConfig->beginGroup(profile);
QStringList childKeys = mLauncherConfig->childKeys();
QStringList plugins;
// Sort the child keys numerical instead of alphabetically
// i.e. Plugin1, Plugin2 instead of Plugin1, Plugin10
qSort(childKeys.begin(), childKeys.end(), naturalSortLessThanCI);
foreach (const QString &key, childKeys) {
const QString keyValue = mLauncherConfig->value(key).toString();
mDataFilesList->setCheckState(keyValue, Qt::Checked);
}
qDebug() << plugins;
}
bool DataFilesPage::showDataFilesWarning()
{
QMessageBox msgBox;
msgBox.setWindowTitle("Error detecting Morrowind installation");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setText(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(tr("B&rowse..."), QMessageBox::ActionRole);
msgBox.exec();
if (msgBox.clickedButton() == dirSelectButton) {
// Show a custom dir selection dialog which only accepts valid dirs
QString selectedDir = FileDialog::getExistingDirectory(
this, tr("Select Data Files Directory"),
QDir::currentPath(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
// Add the user selected data directory
if (!selectedDir.isEmpty()) {
mDataDirs.push_back(Files::PathContainer::value_type(selectedDir.toStdString()));
mCfgMgr.processPaths(mDataDirs);
} else {
// Cancel from within the dir selection dialog
return false;
}
if (profilesComboBox->findText(QString("Default")) == -1)
profilesComboBox->addItem(QString("Default"));
if (profile.isEmpty() || profile == QLatin1String("Default")) {
deleteProfileAction->setEnabled(false);
profilesComboBox->setEditEnabled(false);
profilesComboBox->setCurrentIndex(profilesComboBox->findText(QString("Default")));
} else {
// Cancel
return false;
profilesComboBox->setEditEnabled(true);
profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile));
}
return true;
// We do this here to prevent deletion of profiles when initializing the combobox
connect(profilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString)));
connect(profilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString)));
loadSettings();
}
bool DataFilesPage::setupDataFiles()
void DataFilesPage::loadSettings()
{
// 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);
if (variables["data"].empty()) {
if (!showDataFilesWarning())
return false;
} else {
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);
// Second chance to display the warning, the data= entries are invalid
while (mDataDirs.empty()) {
if (!showDataFilesWarning())
return false;
}
// Set the charset for reading the esm/esp files
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
Files::PathContainer paths;
paths.insert(paths.end(), mDataDirs.begin(), mDataDirs.end());
paths.insert(paths.end(), mDataLocal.begin(), mDataLocal.end());
mDataFilesList->setupDataFiles(paths, encoding);
readConfig();
return true;
}
void DataFilesPage::writeConfig(QString profile)
{
QString pathStr = QString::fromStdString(mCfgMgr.getUserPath().string());
QDir userPath(pathStr);
if (!userPath.exists()) {
if (!userPath.mkpath(pathStr)) {
QMessageBox msgBox;
msgBox.setWindowTitle("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(pathStr));
msgBox.exec();
qApp->quit();
return;
}
}
// Open the OpenMW config as a QFile
QFile file(pathStr.append("openmw.cfg"));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle("Error writing OpenMW configuration file");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
msgBox.exec();
qApp->quit();
return;
}
QTextStream in(&file);
QByteArray buffer;
// Remove all previous entries from config
while (!in.atEnd()) {
QString line = in.readLine();
if (!line.startsWith("master") &&
!line.startsWith("plugin") &&
!line.startsWith("data") &&
!line.startsWith("data-local"))
{
buffer += line += "\n";
}
}
file.close();
// Now we write back the other config entries
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error writing OpenMW configuration file");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not write to %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
msgBox.exec();
qApp->quit();
return;
}
if (!buffer.isEmpty()) {
file.write(buffer);
}
QTextStream gameConfig(&file);
// First write the list of data dirs
mCfgMgr.processPaths(mDataDirs);
mCfgMgr.processPaths(mDataLocal);
QString path;
// data= directories
for (Files::PathContainer::iterator it = mDataDirs.begin(); it != mDataDirs.end(); ++it) {
path = QString::fromStdString(it->string());
path.remove(QChar('\"'));
// Make sure the string is quoted when it contains spaces
if (path.contains(" ")) {
gameConfig << "data=\"" << path << "\"" << endl;
} else {
gameConfig << "data=" << path << endl;
}
}
// data-local directory
if (!mDataLocal.empty()) {
path = QString::fromStdString(mDataLocal.front().string());
path.remove(QChar('\"'));
if (path.contains(" ")) {
gameConfig << "data-local=\"" << path << "\"" << endl;
} else {
gameConfig << "data-local=" << path << endl;
}
}
if (profile.isEmpty())
profile = mProfilesComboBox->currentText();
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
if (profile.isEmpty())
return;
// Make sure we have no groups open
while (!mLauncherConfig->group().isEmpty()) {
mLauncherConfig->endGroup();
mDataFilesModel->uncheckAll();
QStringList masters = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly);
QStringList plugins = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly);
foreach (const QString &master, masters) {
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(master));
if (index.isValid())
mDataFilesModel->setCheckState(index, Qt::Checked);
}
mLauncherConfig->beginGroup("Profiles");
mLauncherConfig->setValue("CurrentProfile", profile);
// Open the profile-name subgroup
mLauncherConfig->beginGroup(profile);
mLauncherConfig->remove(""); // Clear the subgroup
// Now write the masters to the configs
const QStringList checkedFiles = mDataFilesList->checkedFiles();
for(int i=0; i < checkedFiles.size(); i++)
{
if (checkedFiles.at(i).lastIndexOf("esm") != -1)
{
mLauncherConfig->setValue(QString("Master%0").arg(i), checkedFiles.at(i));
gameConfig << "master=" << checkedFiles.at(i) << endl;
}
else
{
mLauncherConfig->setValue(QString("Plugin%1").arg(i), checkedFiles.at(i));
gameConfig << "plugin=" << checkedFiles.at(i) << endl;
}
foreach (const QString &plugin, plugins) {
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(plugin));
if (index.isValid())
mDataFilesModel->setCheckState(index, Qt::Checked);
}
file.close();
mLauncherConfig->endGroup();
mLauncherConfig->endGroup();
mLauncherConfig->sync();
}
void DataFilesPage::newProfile()
void DataFilesPage::saveSettings()
{
if (mNewProfileDialog->exec() == QDialog::Accepted) {
if (mDataFilesModel->rowCount() < 1)
return;
const QString text = mNewProfileDialog->lineEdit()->text();
mProfilesComboBox->addItem(text);
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
// Copy the currently checked items to cfg
writeConfig(text);
mLauncherConfig->sync();
mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(text));
if (profile.isEmpty()) {
profile = profilesComboBox->currentText();
mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile);
}
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
mGameSettings.remove(QString("master"));
mGameSettings.remove(QString("plugin"));
QStringList items = mDataFilesModel->checkedItems();
foreach(const QString &item, items) {
if (item.endsWith(QString(".esm"), Qt::CaseInsensitive)) {
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item);
mGameSettings.setMultiValue(QString("master"), item);
} else if (item.endsWith(QString(".esp"), Qt::CaseInsensitive)) {
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item);
mGameSettings.setMultiValue(QString("plugin"), item);
}
}
}
void DataFilesPage::updateOkButton(const QString &text)
{
// We do this here because we need the profiles combobox text
if (text.isEmpty()) {
mNewProfileDialog->setOkButtonEnabled(false);
return;
}
(mProfilesComboBox->findText(text) == -1)
(profilesComboBox->findText(text) == -1)
? mNewProfileDialog->setOkButtonEnabled(true)
: mNewProfileDialog->setOkButtonEnabled(false);
}
void DataFilesPage::deleteProfile()
void DataFilesPage::updateSplitter()
{
QString profile = mProfilesComboBox->currentText();
// Sigh, update the saved splitter size in settings only when moved
// Since getting mSplitter->sizes() if page is hidden returns invalid values
QList<int> sizes = splitter->sizes();
mLauncherSettings.setValue(QString("General/MastersTable/width"), QString::number(sizes.at(0)));
mLauncherSettings.setValue(QString("General/PluginsTable/width"), QString::number(sizes.at(1)));
}
void DataFilesPage::updateViews()
{
// Ensure the columns are hidden because sort() re-enables them
mastersTable->setColumnHidden(1, true);
mastersTable->setColumnHidden(2, true);
mastersTable->setColumnHidden(3, true);
mastersTable->setColumnHidden(4, true);
mastersTable->setColumnHidden(5, true);
mastersTable->setColumnHidden(6, true);
mastersTable->setColumnHidden(7, true);
mastersTable->setColumnHidden(8, true);
pluginsTable->setColumnHidden(1, true);
pluginsTable->setColumnHidden(2, true);
pluginsTable->setColumnHidden(3, true);
pluginsTable->setColumnHidden(4, true);
pluginsTable->setColumnHidden(5, true);
pluginsTable->setColumnHidden(6, true);
pluginsTable->setColumnHidden(7, true);
pluginsTable->setColumnHidden(8, true);
}
void DataFilesPage::setProfilesComboBoxIndex(int index)
{
profilesComboBox->setCurrentIndex(index);
}
void DataFilesPage::slotCurrentIndexChanged(int index)
{
emit profileChanged(index);
}
QAbstractItemModel* DataFilesPage::profilesComboBoxModel()
{
return profilesComboBox->model();
}
int DataFilesPage::profilesComboBoxIndex()
{
return profilesComboBox->currentIndex();
}
void DataFilesPage::on_newProfileAction_triggered()
{
if (mNewProfileDialog->exec() == QDialog::Accepted) {
QString profile = mNewProfileDialog->lineEdit()->text();
profilesComboBox->addItem(profile);
profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile));
}
}
void DataFilesPage::on_deleteProfileAction_triggered()
{
QString profile = profilesComboBox->currentText();
if (profile.isEmpty())
return;
@ -456,49 +322,143 @@ void DataFilesPage::deleteProfile()
msgBox.exec();
if (msgBox.clickedButton() == deleteButton) {
// Make sure we have no groups open
while (!mLauncherConfig->group().isEmpty()) {
mLauncherConfig->endGroup();
}
mLauncherConfig->beginGroup("Profiles");
// Open the profile-name subgroup
mLauncherConfig->beginGroup(profile);
mLauncherConfig->remove(""); // Clear the subgroup
mLauncherConfig->endGroup();
mLauncherConfig->endGroup();
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
// Remove the profile from the combobox
mProfilesComboBox->removeItem(mProfilesComboBox->findText(profile));
profilesComboBox->removeItem(profilesComboBox->findText(profile));
}
}
void DataFilesPage::on_checkAction_triggered()
{
if (pluginsTable->hasFocus())
setPluginsCheckstates(Qt::Checked);
if (mastersTable->hasFocus())
setMastersCheckstates(Qt::Checked);
}
void DataFilesPage::on_uncheckAction_triggered()
{
if (pluginsTable->hasFocus())
setPluginsCheckstates(Qt::Unchecked);
if (mastersTable->hasFocus())
setMastersCheckstates(Qt::Unchecked);
}
void DataFilesPage::setMastersCheckstates(Qt::CheckState state)
{
if (!mastersTable->selectionModel()->hasSelection()) {
return;
}
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes();
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
if (!sourceIndex.isValid())
return;
mDataFilesModel->setCheckState(sourceIndex, state);
}
}
void DataFilesPage::setPluginsCheckstates(Qt::CheckState state)
{
if (!pluginsTable->selectionModel()->hasSelection()) {
return;
}
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes();
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
mFilterProxyModel->mapToSource(index));
if (!sourceIndex.isValid())
return;
mDataFilesModel->setCheckState(sourceIndex, state);
}
}
void DataFilesPage::setCheckState(QModelIndex index)
{
if (!index.isValid())
return;
QObject *object = QObject::sender();
// Not a signal-slot call
if (!object)
return;
if (object->objectName() == QLatin1String("PluginsTable")) {
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
mFilterProxyModel->mapToSource(index));
if (sourceIndex.isValid()) {
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
}
}
if (object->objectName() == QLatin1String("MastersTable")) {
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
if (sourceIndex.isValid()) {
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
}
}
return;
}
void DataFilesPage::filterChanged(const QString filter)
{
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
mFilterProxyModel->setFilterRegExp(regExp);
}
void DataFilesPage::profileChanged(const QString &previous, const QString &current)
{
qDebug() << "Profile is changed from: " << previous << " to " << current;
// Prevent the deletion of the default profile
if (current == QLatin1String("Default")) {
mDeleteProfileAction->setEnabled(false);
mProfilesComboBox->setEditEnabled(false);
deleteProfileAction->setEnabled(false);
profilesComboBox->setEditEnabled(false);
} else {
mDeleteProfileAction->setEnabled(true);
mProfilesComboBox->setEditEnabled(true);
deleteProfileAction->setEnabled(true);
profilesComboBox->setEditEnabled(true);
}
if (!previous.isEmpty()) {
writeConfig(previous);
mLauncherConfig->sync();
if (mProfilesComboBox->currentIndex() == -1)
return;
} else {
if (previous.isEmpty())
return;
}
mDataFilesList->uncheckAll();
readConfig();
if (profilesComboBox->findText(previous) == -1)
return; // Profile was deleted
// Store the previous profile
mLauncherSettings.setValue(QString("Profiles/currentprofile"), previous);
saveSettings();
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
loadSettings();
}
void DataFilesPage::profileRenamed(const QString &previous, const QString &current)
@ -507,27 +467,85 @@ void DataFilesPage::profileRenamed(const QString &previous, const QString &curre
return;
// Save the new profile name
writeConfig(current);
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
saveSettings();
// Make sure we have no groups open
while (!mLauncherConfig->group().isEmpty()) {
mLauncherConfig->endGroup();
}
// Remove the old one
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master"));
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin"));
mLauncherConfig->beginGroup("Profiles");
// Remove the profile from the combobox
profilesComboBox->removeItem(profilesComboBox->findText(previous));
// Open the profile-name subgroup
mLauncherConfig->beginGroup(previous);
mLauncherConfig->remove(""); // Clear the subgroup
mLauncherConfig->endGroup();
mLauncherConfig->endGroup();
mLauncherConfig->sync();
loadSettings();
// Remove the profile from the combobox
mProfilesComboBox->removeItem(mProfilesComboBox->findText(previous));
mDataFilesList->uncheckAll();
////mMastersModel->uncheckAll();
////mPluginsModel->uncheckAll();
readConfig();
}
void DataFilesPage::showContextMenu(const QPoint &point)
{
QObject *object = QObject::sender();
// Not a signal-slot call
if (!object)
return;
if (object->objectName() == QLatin1String("PluginsTable")) {
if (!pluginsTable->selectionModel()->hasSelection())
return;
QPoint globalPos = pluginsTable->mapToGlobal(point);
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes();
// Show the check/uncheck actions depending on the state of the selected items
uncheckAction->setEnabled(false);
checkAction->setEnabled(false);
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
mFilterProxyModel->mapToSource(index));
if (!sourceIndex.isValid())
return;
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
? uncheckAction->setEnabled(true)
: checkAction->setEnabled(true);
}
// Show menu
mContextMenu->exec(globalPos);
}
if (object->objectName() == QLatin1String("MastersTable")) {
if (!mastersTable->selectionModel()->hasSelection())
return;
QPoint globalPos = mastersTable->mapToGlobal(point);
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes();
// Show the check/uncheck actions depending on the state of the selected items
uncheckAction->setEnabled(false);
checkAction->setEnabled(false);
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
if (!sourceIndex.isValid())
return;
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
? uncheckAction->setEnabled(true)
: checkAction->setEnabled(true);
}
mContextMenu->exec(globalPos);
}
}

View file

@ -3,78 +3,86 @@
#include <QWidget>
#include <QModelIndex>
#include "utils/profilescombobox.hpp"
#include <components/files/collections.hpp>
#include "ui_datafilespage.h"
class QTableView;
class QSortFilterProxyModel;
class QSettings;
class QAbstractItemModel;
class QAction;
class QToolBar;
class QMenu;
class ProfilesComboBox;
class DataFilesModel;
class DataFilesModel;
class TextInputDialog;
class DataFilesList;
class GameSettings;
class LauncherSettings;
class PluginsProxyModel;
namespace Files { struct ConfigurationManager; }
class DataFilesPage : public QWidget
class DataFilesPage : public QWidget, private Ui::DataFilesPage
{
Q_OBJECT
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());
bool showDataFilesWarning();
bool setupDataFiles();
void saveSettings();
signals:
void profileChanged(int index);
public slots:
void setCheckState(QModelIndex index);
void setProfilesComboBoxIndex(int index);
void filterChanged(const QString filter);
void showContextMenu(const QPoint &point);
void profileChanged(const QString &previous, const QString &current);
void profileRenamed(const QString &previous, const QString &current);
void updateOkButton(const QString &text);
void updateSplitter();
void updateViews();
// Action slots
void newProfile();
void deleteProfile();
// void moveUp();
// void moveDown();
// void moveTop();
// void moveBottom();
void on_newProfileAction_triggered();
void on_deleteProfileAction_triggered();
void on_checkAction_triggered();
void on_uncheckAction_triggered();
private slots:
void slotCurrentIndexChanged(int index);
private:
DataFilesList *mDataFilesList;
DataFilesModel *mDataFilesModel;
QToolBar *mProfileToolBar;
PluginsProxyModel *mPluginsProxyModel;
QSortFilterProxyModel *mMastersProxyModel;
QAction *mNewProfileAction;
QAction *mDeleteProfileAction;
QSortFilterProxyModel *mFilterProxyModel;
// QAction *mMoveUpAction;
// QAction *mMoveDownAction;
// QAction *mMoveTopAction;
// QAction *mMoveBottomAction;
QMenu *mContextMenu;
Files::ConfigurationManager &mCfgMgr;
Files::PathContainer mDataDirs;
Files::PathContainer mDataLocal;
QSettings *mLauncherConfig;
GameSettings &mGameSettings;
LauncherSettings &mLauncherSettings;
TextInputDialog *mNewProfileDialog;
// const QStringList checkedPlugins();
// const QStringList selectedMasters();
void setMastersCheckstates(Qt::CheckState state);
void setPluginsCheckstates(Qt::CheckState state);
void createActions();
void setupDataFiles();
void setupConfig();
void readConfig();
void loadSettings();
};
#endif

View file

@ -1,16 +1,19 @@
#include <QtGui>
#include "graphicspage.hpp"
#include <QDesktopWidget>
#include <QMessageBox>
#include <QDir>
#include <cstdlib>
#include <boost/math/common_factor.hpp>
#include <boost/filesystem.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/ogreplugin.hpp>
#include <components/settings/settings.hpp>
#include <components/fileorderlist/utils/naturalsort.hpp>
#include "graphicspage.hpp"
#include "settings/graphicssettings.hpp"
QString getAspect(int x, int y)
{
@ -24,52 +27,22 @@ QString getAspect(int x, int y)
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
}
GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent)
: QWidget(parent)
, mCfgMgr(cfg)
GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent)
: mCfgMgr(cfg)
, mGraphicsSettings(graphicsSetting)
, QWidget(parent)
{
QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this);
setupUi(this);
QLabel *rendererLabel = new QLabel(tr("Rendering Subsystem:"), rendererGroup);
mRendererComboBox = new QComboBox(rendererGroup);
// Set the maximum res we can set in windowed mode
QRect res = QApplication::desktop()->screenGeometry();
customWidthSpinBox->setMaximum(res.width());
customHeightSpinBox->setMaximum(res.height());
// Layout for the combobox and label
QGridLayout *renderSystemLayout = new QGridLayout();
renderSystemLayout->addWidget(rendererLabel, 0, 0, 1, 1);
renderSystemLayout->addWidget(mRendererComboBox, 0, 1, 1, 1);
connect(rendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int)));
connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool)));
// 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()
@ -116,11 +89,11 @@ bool GraphicsPage::setupOgre()
#endif
}
boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir));
pluginDir = absPluginPath.string();
QDir dir(QString::fromStdString(pluginDir));
pluginDir = dir.absolutePath().toStdString();
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre);
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mOgre);
Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre);
#ifdef ENABLE_PLUGIN_GL
@ -137,7 +110,7 @@ bool GraphicsPage::setupOgre()
for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) {
mSelectedRenderSystem = *r;
mRendererComboBox->addItem((*r)->getName().c_str());
rendererComboBox->addItem((*r)->getName().c_str());
}
QString openGLName = QString("OpenGL Rendering Subsystem");
@ -153,71 +126,85 @@ bool GraphicsPage::setupOgre()
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not select a valid render system</b><br><br> \
Please make sure the plugins.cfg file exists and contains a valid rendering plugin.<br>"));
Please make sure the plugins.cfg file exists and contains a valid rendering plugin.<br>"));
msgBox.exec();
return false;
}
// 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) {
mRendererComboBox->setCurrentIndex(index);
}
else
{
rendererComboBox->setCurrentIndex(index);
} else {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
mRendererComboBox->setCurrentIndex(mRendererComboBox->findText(direct3DName));
rendererComboBox->setCurrentIndex(rendererComboBox->findText(direct3DName));
#else
mRendererComboBox->setCurrentIndex(mRendererComboBox->findText(openGLName));
rendererComboBox->setCurrentIndex(rendererComboBox->findText(openGLName));
#endif
}
mAntiAliasingComboBox->clear();
mResolutionComboBox->clear();
mAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
mResolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
antiAliasingComboBox->clear();
resolutionComboBox->clear();
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
readConfig();
// Load the rest of the values
loadSettings();
return true;
}
void GraphicsPage::readConfig()
void GraphicsPage::loadSettings()
{
if (Settings::Manager::getBool("vsync", "Video"))
mVSyncCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true"))
vSyncCheckBox->setCheckState(Qt::Checked);
if (Settings::Manager::getBool("fullscreen", "Video"))
mFullScreenCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true"))
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)
mAntiAliasingComboBox->setCurrentIndex(aaIndex);
antiAliasingComboBox->setCurrentIndex(aaIndex);
QString resolution = QString::number(Settings::Manager::getInt("resolution x", "Video"));
resolution.append(" x " + QString::number(Settings::Manager::getInt("resolution y", "Video")));
QString width = mGraphicsSettings.value(QString("Video/resolution x"));
QString height = mGraphicsSettings.value(QString("Video/resolution y"));
QString resolution = width + QString(" x ") + height;
int resIndex = mResolutionComboBox->findText(resolution, Qt::MatchStartsWith);
if (resIndex != -1)
mResolutionComboBox->setCurrentIndex(resIndex);
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
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());
Settings::Manager::setBool("fullscreen", "Video", mFullScreenCheckBox->checkState());
Settings::Manager::setString("antialiasing", "Video", mAntiAliasingComboBox->currentText().toStdString());
Settings::Manager::setString("render system", "Video", mRendererComboBox->currentText().toStdString());
vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/vsync"), QString("false"));
// Get the current resolution, but with the tabs replaced with a single space
QString resolution = mResolutionComboBox->currentText().simplified();
QStringList tokens = resolution.split(" ", QString::SkipEmptyParts);
fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false"));
int resX = tokens.at(0).toInt();
int resY = tokens.at(2).toInt();
Settings::Manager::setInt("resolution x", "Video", resX);
Settings::Manager::setInt("resolution y", "Video", resY);
mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText());
mGraphicsSettings.setValue(QString("Video/render system"), rendererComboBox->currentText());
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)
@ -231,16 +218,14 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy
{
Ogre::StringVector::iterator opt_it;
uint idx = 0;
for (opt_it = i->second.possibleValues.begin ();
opt_it != i->second.possibleValues.end (); opt_it++, idx++)
{
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0)
{
for (opt_it = i->second.possibleValues.begin();
opt_it != i->second.possibleValues.end(); opt_it++, idx++)
{
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) {
result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified();
}
}
}
// Sort ascending
@ -257,7 +242,7 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy
QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer)
{
QString key ("Video Mode");
QString key("Video Mode");
QStringList result;
uint row = 0;
@ -274,24 +259,27 @@ QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer)
for (opt_it = i->second.possibleValues.begin ();
opt_it != i->second.possibleValues.end (); opt_it++, idx++)
{
QString qval = QString::fromStdString(*opt_it).simplified();
// remove extra tokens after the resolution (for example bpp, can be there or not depending on rendersystem)
QStringList tokens = qval.split(" ", QString::SkipEmptyParts);
assert (tokens.size() >= 3);
QString resolutionStr = tokens.at(0) + QString(" x ") + tokens.at(2);
QRegExp resolutionRe(QString("(\\d+) x (\\d+).*"));
QString resolution = QString::fromStdString(*opt_it).simplified();
QString aspect = getAspect(tokens.at(0).toInt(),tokens.at(2).toInt());
if (resolutionRe.exactMatch(resolution)) {
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
resolutionStr.append(tr("\t(Widescreen ") + aspect + ")");
int width = resolutionRe.cap(1).toInt();
int height = resolutionRe.cap(2).toInt();
} else if (aspect == QLatin1String("4:3")) {
resolutionStr.append(tr("\t(Standard 4:3)"));
QString aspect = getAspect(width, height);
QString cleanRes = resolutionRe.cap(1) + QString(" x ") + resolutionRe.cap(2);
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
cleanRes.append(tr("\t(Wide ") + aspect + ")");
} else if (aspect == QLatin1String("4:3")) {
cleanRes.append(tr("\t(Standard 4:3)"));
}
// do not add duplicate resolutions
if (!result.contains(cleanRes))
result.append(cleanRes);
}
// do not add duplicate resolutions
if (!result.contains(resolutionStr))
result << resolutionStr;
}
}
@ -305,9 +293,36 @@ void GraphicsPage::rendererChanged(const QString &renderer)
{
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
mAntiAliasingComboBox->clear();
mResolutionComboBox->clear();
antiAliasingComboBox->clear();
resolutionComboBox->clear();
mAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
mResolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), 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);
}
}

View file

@ -5,8 +5,8 @@
#include <OgreRoot.h>
#include <OgreRenderSystem.h>
#include <OgreConfigFile.h>
#include <OgreConfigDialog.h>
//#include <OgreConfigFile.h>
//#include <OgreConfigDialog.h>
// Static plugin headers
#ifdef ENABLE_PLUGIN_GL
@ -16,26 +16,29 @@
# include "OgreD3D9Plugin.h"
#endif
class QComboBox;
class QCheckBox;
class QStackedWidget;
class QSettings;
#include "ui_graphicspage.h"
class GraphicsSettings;
namespace Files { struct ConfigurationManager; }
class GraphicsPage : public QWidget
class GraphicsPage : public QWidget, private Ui::GraphicsPage
{
Q_OBJECT
public:
GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent = 0);
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
void saveSettings();
bool setupOgre();
void writeConfig();
public slots:
void rendererChanged(const QString &renderer);
private slots:
void slotFullScreenChanged(int state);
void slotStandardToggled(bool checked);
private:
Ogre::Root *mOgre;
Ogre::RenderSystem *mSelectedRenderSystem;
@ -48,22 +51,14 @@ private:
Ogre::D3D9Plugin* mD3D9Plugin;
#endif
QComboBox *mRendererComboBox;
QStackedWidget *mDisplayStackedWidget;
QComboBox *mAntiAliasingComboBox;
QComboBox *mResolutionComboBox;
QCheckBox *mVSyncCheckBox;
QCheckBox *mFullScreenCheckBox;
Files::ConfigurationManager &mCfgMgr;
GraphicsSettings &mGraphicsSettings;
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
QStringList getAvailableResolutions(Ogre::RenderSystem *renderer);
void createPages();
void readConfig();
void loadSettings();
};
#endif

View file

@ -1 +0,0 @@
IDI_ICON1 ICON DISCARDABLE "resources/images/openmw.ico"

View file

@ -1,6 +1,6 @@
#include <QApplication>
#include <QTextCodec>
#include <QDir>
#include <QFile>
#include "maindialog.hpp"
@ -30,14 +30,17 @@ int main(int argc, char *argv[])
QDir::setCurrent(dir.absolutePath());
// Support non-latin characters
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
MainDialog mainWin;
if (mainWin.setup()) {
mainWin.show();
return app.exec();
} else {
return 0;
}
return 0;
return app.exec();
}

View file

@ -1,52 +1,25 @@
#include <QtGui>
#include "maindialog.hpp"
#include <QFontDatabase>
#include <QInputDialog>
#include <QFileDialog>
#include <QCloseEvent>
#include <QTextCodec>
#include <QProcess>
#include <QFile>
#include <QDir>
#include <QDebug>
#include "utils/checkablemessagebox.hpp"
#include "playpage.hpp"
#include "graphicspage.hpp"
#include "datafilespage.hpp"
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
QFile file;
QFontDatabase fontDatabase;
@ -56,64 +29,67 @@ MainDialog::MainDialog()
// Check if the font is installed
if (!fonts.contains("EB Garamond")) {
QString font = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
file.setFileName(font);
QString font = QString::fromStdString(mCfgMgr.getGlobalDataPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf");
file.setFileName(font);
if (!file.exists()) {
font = QString::fromStdString((mCfgMgr.getLocalPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
}
if (!file.exists()) {
font = QString::fromStdString(mCfgMgr.getLocalPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf");
}
fontDatabase.addApplicationFont(font);
fontDatabase.addApplicationFont(font);
}
// Load the stylesheet
QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/launcher.qss").string());
file.setFileName(config);
setupUi(this);
if (!file.exists()) {
file.setFileName(QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.qss").string()));
}
iconWidget->setViewMode(QListView::IconMode);
iconWidget->setWrapping(false);
iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
iconWidget->setIconSize(QSize(48, 48));
iconWidget->setMovement(QListView::Static);
file.open(QFile::ReadOnly);
QString styleSheet = QLatin1String(file.readAll());
qApp->setStyleSheet(styleSheet);
file.close();
iconWidget->setSpacing(4);
iconWidget->setCurrentRow(0);
iconWidget->setFlow(QListView::LeftToRight);
QPushButton *playButton = new QPushButton(tr("Play"));
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(play()));
// Remove what's this? button
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
createIcons();
createPages();
}
void MainDialog::createIcons()
{
if (!QIcon::hasThemeIcon("document-new")) {
if (!QIcon::hasThemeIcon("document-new"))
QIcon::setThemeName("tango");
}
// We create a fallback icon because the default fallback doesn't work
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->setText(tr("Play"));
playButton->setTextAlignment(Qt::AlignCenter);
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *graphicsButton = new QListWidgetItem(mIconWidget);
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon));
graphicsButton->setText(tr("Graphics"));
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *dataFilesButton = new QListWidgetItem(mIconWidget);
QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget);
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
dataFilesButton->setText(tr("Data Files"));
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
connect(mIconWidget,
connect(iconWidget,
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
@ -122,77 +98,205 @@ void MainDialog::createIcons()
void MainDialog::createPages()
{
mPlayPage = new PlayPage(this);
mGraphicsPage = new GraphicsPage(mCfgMgr, this);
mDataFilesPage = new DataFilesPage(mCfgMgr, this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
// Set the combobox of the play page to imitate the combobox on the datafilespage
mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model());
mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex());
mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel());
mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex());
// Add the pages to the stacked widget
mPagesWidget->addWidget(mPlayPage);
mPagesWidget->addWidget(mGraphicsPage);
mPagesWidget->addWidget(mDataFilesPage);
pagesWidget->addWidget(mPlayPage);
pagesWidget->addWidget(mGraphicsPage);
pagesWidget->addWidget(mDataFilesPage);
// 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,
SIGNAL(currentIndexChanged(int)),
mDataFilesPage->mProfilesComboBox, SLOT(setCurrentIndex(int)));
connect(mDataFilesPage->mProfilesComboBox,
SIGNAL(currentIndexChanged(int)),
mPlayPage->mProfilesComboBox, SLOT(setCurrentIndex(int)));
connect(mPlayPage, SIGNAL(profileChanged(int)), mDataFilesPage, SLOT(setProfilesComboBoxIndex(int)));
connect(mDataFilesPage, SIGNAL(profileChanged(int)), mPlayPage, SLOT(setProfilesComboBoxIndex(int)));
}
bool MainDialog::showFirstRunDialog()
{
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")));
}
// Ask the user where the Morrowind.ini is
if (iniPaths.empty()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error detecting Morrowind configuration"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setText(QObject::tr("<br><b>Could not find Morrowind.ini</b><br><br> \
OpenMW needs to import settings from this file.<br><br> \
Press \"Browse...\" to specify the location manually.<br>"));
QAbstractButton *dirSelectButton =
msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole);
msgBox.exec();
QString iniFile;
if (msgBox.clickedButton() == dirSelectButton) {
iniFile = QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select configuration file"),
QDir::currentPath(),
QString(tr("Morrowind configuration file (*.ini)")));
}
if (iniFile.isEmpty())
return false; // Cancel was clicked;
QFileInfo info(iniFile);
iniPaths.clear();
iniPaths.append(info.absoluteFilePath());
}
CheckableMessageBox msgBox(this);
msgBox.setWindowTitle(tr("Morrowind installation detected"));
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxQuestion);
int size = QApplication::style()->pixelMetric(QStyle::PM_MessageBoxIconSize);
msgBox.setIconPixmap(icon.pixmap(size, size));
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 configuration was detected</b><br> \
<br>Would you like to import settings from Morrowind.ini?<br> \
<br><b>Warning: In most cases OpenMW needs these settings to run properly</b><br>"));
msgBox.setCheckBoxText(tr("Include selected masters and plugins (creates a new profile)"));
msgBox.exec();
if (msgBox.clickedButton() == importerButton) {
if (iniPaths.count() > 1) {
// 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
return false;
}
}
// Create the file if it doesn't already exist, else the importer will fail
QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg");
QFile file(path);
if (!file.exists()) {
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;
}
file.close();
}
// Construct the arguments to run the importer
QStringList arguments;
if (msgBox.isChecked())
arguments.append(QString("--game-files"));
arguments.append(QString("--encoding"));
arguments.append(mGameSettings.value(QString("encoding"), QString("win1252")));
arguments.append(QString("--ini"));
arguments.append(iniPaths.first());
arguments.append(QString("--cfg"));
arguments.append(path);
if (!startProgram(QString("mwiniimport"), arguments, false))
return false;
// Re-read the game settings
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()
{
// Create the settings manager and load default settings file
const std::string localdefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string();
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string();
// prefer local
if (boost::filesystem::exists(localdefault)) {
mSettings.loadDefault(localdefault);
} else if (boost::filesystem::exists(globaldefault)) {
mSettings.loadDefault(globaldefault);
} else {
QMessageBox msgBox;
msgBox.setWindowTitle("Error reading OpenMW configuration file");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not find %0</b><br><br> \
The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem.").arg(QString::fromStdString(globaldefault)));
msgBox.exec();
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;
}
// load user settings if they exist, otherwise just load the default settings as user settings
const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string();
// Now create the pages as they need the settings
createPages();
if (boost::filesystem::exists(settingspath))
mSettings.loadUser(settingspath);
else if (boost::filesystem::exists(localdefault))
mSettings.loadUser(localdefault);
else if (boost::filesystem::exists(globaldefault))
mSettings.loadUser(globaldefault);
// Setup the Graphics page
if (!mGraphicsPage->setupOgre()) {
// Call this so we can exit on Ogre errors before mainwindow is shown
if (!mGraphicsPage->setupOgre())
return false;
}
// Setup the Data Files page
if (!mDataFilesPage->setupDataFiles()) {
return false;
}
loadSettings();
return true;
}
@ -201,89 +305,410 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
if (!current)
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.setMultiValue(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"));
}
bool 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 false;
}
}
// 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 false;
}
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 false;
}
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 false;
}
stream.setDevice(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mLauncherSettings.writeFile(stream);
file.close();
return true;
}
void MainDialog::closeEvent(QCloseEvent *event)
{
// Now write all config files
mDataFilesPage->writeConfig();
mGraphicsPage->writeConfig();
// Save user settings
const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string();
mSettings.saveUser(settingspath);
writeSettings();
event->accept();
}
void MainDialog::play()
{
// First do a write of all the configs, just to be sure
mDataFilesPage->writeConfig();
mGraphicsPage->writeConfig();
if (!writeSettings())
qApp->quit();
// Save user settings
const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string();
mSettings.saveUser(settingspath);
// Launch the game detached
startProgram(QString("openmw"), true);
qApp->quit();
}
bool MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached)
{
QString path = name;
#ifdef Q_OS_WIN
QString game = "./openmw.exe";
QFile file(game);
path.append(QString(".exe"));
#elif defined(Q_OS_MAC)
QDir dir(QCoreApplication::applicationDirPath());
QString game = dir.absoluteFilePath("openmw");
QFile file(game);
game = "\"" + game + "\"";
path = dir.absoluteFilePath(name);
#else
QString game = "./openmw";
QFile file(game);
path.prepend(QString("./"));
#endif
QFile file(path);
QProcess process;
QFileInfo info(file);
if (!file.exists()) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error starting OpenMW");
msgBox.setWindowTitle(tr("Error starting executable"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not find OpenMW</b><br><br> \
The OpenMW application is not found.<br> \
Please make sure OpenMW is installed correctly and try again.<br>"));
msgBox.setText(tr("<br><b>Could not find %1</b><br><br> \
The application is not found.<br> \
Please make sure OpenMW is installed correctly and try again.<br>").arg(info.fileName()));
msgBox.exec();
return;
return false;
}
if (!info.isExecutable()) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error starting OpenMW");
msgBox.setWindowTitle(tr("Error starting executable"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
The OpenMW application is not executable.<br> \
Please make sure you have the right permissions and try again.<br>"));
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
The application is not executable.<br> \
Please make sure you have the right permissions and try again.<br>").arg(info.fileName()));
msgBox.exec();
return;
return false;
}
// Start the game
if (!process.startDetached(game)) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error starting OpenMW");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
An error occurred while starting OpenMW.<br><br> \
Press \"Show Details...\" for more information.<br>"));
msgBox.setDetailedText(process.errorString());
msgBox.exec();
// Start the executable
if (detached) {
if (!process.startDetached(path, arguments)) {
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;
return false;
}
} 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 || process.exitStatus() == QProcess::CrashExit) {
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;
}

View file

@ -4,7 +4,12 @@
#include <QMainWindow>
#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 QListWidgetItem;
@ -17,32 +22,46 @@ class PlayPage;
class GraphicsPage;
class DataFilesPage;
class MainDialog : public QMainWindow
class MainDialog : public QMainWindow, private Ui::MainWindow
{
Q_OBJECT
public:
MainDialog();
bool setup();
bool showFirstRunDialog();
public slots:
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
void play();
bool setup();
private:
void createIcons();
void createPages();
void closeEvent(QCloseEvent *event);
QListWidget *mIconWidget;
QStackedWidget *mPagesWidget;
bool setupLauncherSettings();
bool setupGameSettings();
bool setupGraphicsSettings();
void loadSettings();
void saveSettings();
bool writeSettings();
inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); }
bool startProgram(const QString &name, const QStringList &arguments, bool detached = false);
void closeEvent(QCloseEvent *event);
PlayPage *mPlayPage;
GraphicsPage *mGraphicsPage;
DataFilesPage *mDataFilesPage;
Files::ConfigurationManager mCfgMgr;
Settings::Manager mSettings;
GameSettings mGameSettings;
GraphicsSettings mGraphicsSettings;
LauncherSettings mLauncherSettings;
};
#endif

View file

@ -1,43 +1,43 @@
#include <QtGui>
#include "playpage.hpp"
#include <QListView>
#ifdef Q_OS_MAC
#include <QPlastiqueStyle>
#endif
PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
{
QWidget *playWidget = new QWidget(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");
setupUi(this);
// Hacks to get the stylesheet look properly
#ifdef Q_OS_MAC
QPlastiqueStyle *style = new QPlastiqueStyle;
mProfilesComboBox = new QComboBox(playWidget);
mProfilesComboBox->setObjectName("ProfilesComboBox");
mProfilesComboBox->setStyle(style);
profilesComboBox->setStyle(style);
#endif
profilesComboBox->setView(new QListView());
QGridLayout *playLayout = new QGridLayout(playWidget);
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
QSpacerItem *hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
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);
connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked()));
}
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();
}

View file

@ -3,18 +3,32 @@
#include <QWidget>
#include "ui_playpage.h"
class QComboBox;
class QPushButton;
class QAbstractItemModel;
class PlayPage : public QWidget
class PlayPage : public QWidget, private Ui::PlayPage
{
Q_OBJECT
public:
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;
};

View file

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 B

View 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;
}

View file

@ -0,0 +1,61 @@
#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 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

View 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;
}

View 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

View 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 &currentKey, 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 &currentKey, 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;
}

View 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

View 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

View 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));
}

View 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

View file

@ -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);
}

View file

@ -1,14 +1,14 @@
#include "textinputdialog.hpp"
#include <QDialogButtonBox>
#include <QApplication>
#include <QPushButton>
#include <QDebug>
#include <QLabel>
#include <QVBoxLayout>
#include <QValidator>
#include <QLabel>
#include <components/fileorderlist/utils/lineedit.hpp>
#include "textinputdialog.hpp"
TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) :
QDialog(parent)
{
@ -17,9 +17,19 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid
mButtonBox->addButton(QDialogButtonBox::Ok);
mButtonBox->addButton(QDialogButtonBox::Cancel);
setMaximumHeight(height());
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);
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
#ifndef Q_OS_MAC
@ -28,22 +38,12 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid
Q_UNUSED(title);
#endif
QLabel *label = new QLabel(this);
label->setText(text);
// 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);
setOkButtonEnabled(false);
setModal(true);
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
}
int TextInputDialog::exec()
@ -55,7 +55,17 @@ int TextInputDialog::exec()
void TextInputDialog::setOkButtonEnabled(bool enabled)
{
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);
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);
}
}

View file

@ -22,3 +22,8 @@ if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(mwiniimport gcov)
endif()
if(DPKG_PROGRAM)
INSTALL(TARGETS mwiniimport RUNTIME DESTINATION games COMPONENT mwiniimport)
endif()

View file

@ -1,4 +1,3 @@
set (OPENCS_SRC main.cpp)
opencs_units (. editor)
@ -36,12 +35,12 @@ opencs_units (model/tools
)
opencs_units_noqt (model/tools
stage verifier mandatoryid
stage verifier mandatoryid skillcheck
)
opencs_units (view/doc
viewmanager view operations operation subview startup opendialog
viewmanager view operations operation subview startup filedialog
)
@ -76,6 +75,10 @@ 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})

View file

@ -1,14 +1,12 @@
#include "editor.hpp"
#include <sstream>
#include <QtGui/QApplication>
#include "model/doc/document.hpp"
#include "model/world/data.hpp"
CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0)
CS::Editor::Editor() : mViewManager (mDocumentManager)
{
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
@ -16,47 +14,106 @@ CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0)
connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ()));
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
connect (&mOpenDialog, SIGNAL(accepted()), this, SLOT(openFiles()));
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();
/// \todo open the ESX picker instead
std::ostringstream stream;
stream << "NewDocument" << (++mNewDocumentIndex);
std::vector<boost::filesystem::path> files;
files.push_back (stream.str());
CSMDoc::Document *document = mDocumentManager.addDocument (files, true);
mViewManager.addView (document);
mFileDialog.newFile();
}
void CS::Editor::loadDocument()
{
mStartup.hide();
mOpenDialog.show();
mOpenDialog.raise();
mOpenDialog.activateWindow();
mFileDialog.openFile();
}
void CS::Editor::openFiles()
{
std::vector<boost::filesystem::path> paths;
mOpenDialog.getFileList(paths);
CSMDoc::Document *document = mDocumentManager.addDocument(paths, false);
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();
QApplication::setQuitOnLastWindowClosed (true);
return QApplication::exec();
}

View file

@ -3,11 +3,13 @@
#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/opendialog.hpp"
#include "view/doc/filedialog.hpp"
namespace CS
{
@ -15,12 +17,14 @@ namespace CS
{
Q_OBJECT
int mNewDocumentIndex; ///< \todo remove when the proper new document dialogue is implemented.
CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup;
OpenDialog mOpenDialog;
FileDialog mFileDialog;
Files::ConfigurationManager mCfgMgr;
void setupDataFiles();
// not implemented
Editor (const Editor&);
@ -39,6 +43,7 @@ namespace CS
void loadDocument();
void openFiles();
void createNewFile();
};
}

View file

@ -18,9 +18,6 @@ void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_i
if (lastAsModified)
getData().loadFile (*end2, false);
addOptionalGmsts();
addOptionalGlobals();
}
void CSMDoc::Document::addOptionalGmsts()
@ -116,8 +113,7 @@ void CSMDoc::Document::addOptionalGmsts()
{
ESM::GameSetting gmst;
gmst.mId = sFloats[i];
gmst.mF = 0;
gmst.mType = ESM::VT_Float;
gmst.mValue.setType (ESM::VT_Float);
addOptionalGmst (gmst);
}
@ -125,8 +121,7 @@ void CSMDoc::Document::addOptionalGmsts()
{
ESM::GameSetting gmst;
gmst.mId = sIntegers[i];
gmst.mI = 0;
gmst.mType = ESM::VT_Long;
gmst.mValue.setType (ESM::VT_Int);
addOptionalGmst (gmst);
}
@ -134,8 +129,8 @@ void CSMDoc::Document::addOptionalGmsts()
{
ESM::GameSetting gmst;
gmst.mId = sStrings[i];
gmst.mStr = "<no text>";
gmst.mType = ESM::VT_String;
gmst.mValue.setType (ESM::VT_String);
gmst.mValue.setString ("<no text>");
addOptionalGmst (gmst);
}
}
@ -154,8 +149,11 @@ void CSMDoc::Document::addOptionalGlobals()
{
ESM::Global global;
global.mId = sGlobals[i];
global.mType = ESM::VT_Int;
global.mValue = 0;
global.mValue.setType (ESM::VT_Long);
if (i==0)
global.mValue.setInteger (1); // dayspassed starts counting at 1
addOptionalGlobal (global);
}
}
@ -192,11 +190,28 @@ void CSMDoc::Document::createBase()
for (int i=0; sGlobals[i]; ++i)
{
ESM::Global record;
record.mId = sGlobals[i];
record.mValue = i==0 ? 1 : 0;
record.mType = ESM::VT_Float;
record.mValue.setType (i==2 ? ESM::VT_Float : ESM::VT_Long);
if (i==0 || i==1)
record.mValue.setInteger (1);
getData().getGlobals().add (record);
}
/// \todo add GMSTs
for (int i=0; i<27; ++i)
{
ESM::Skill record;
record.mIndex = i;
record.mId = ESM::Skill::getIndexToId (record.mIndex);
record.blank();
getData().getSkills().add (record);
}
}
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, bool new_)
@ -211,7 +226,9 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, b
mName = files.back().filename().string();
if (files.size()>1 || !new_)
if (new_ && files.size()==1)
createBase();
else
{
std::vector<boost::filesystem::path>::const_iterator end = files.end();
@ -221,8 +238,8 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, b
load (files.begin(), end, !new_);
}
if (new_ && files.size()==1)
createBase();
addOptionalGmsts();
addOptionalGlobals();
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));
@ -234,6 +251,9 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, b
connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving()));
}
CSMDoc::Document::~Document()
{}
QUndoStack& CSMDoc::Document::getUndoStack()
{
return mUndoStack;
@ -287,11 +307,13 @@ void CSMDoc::Document::abortOperation (int type)
}
}
void CSMDoc::Document::modificationStateChanged (bool clean)
{
emit stateChanged (getState(), this);
}
void CSMDoc::Document::operationDone (int type)
{
emit stateChanged (getState(), this);
@ -305,9 +327,12 @@ void CSMDoc::Document::saving()
if (mSaveCount>15)
{
//clear the stack before resetting the save state
//to avoid emitting incorrect states
mUndoStack.setClean();
mSaveCount = 0;
mSaveTimer.stop();
mUndoStack.setClean();
emit stateChanged (getState(), this);
}
}

View file

@ -63,6 +63,7 @@ namespace CSMDoc
public:
Document (const std::vector<boost::filesystem::path>& files, bool new_);
~Document();
QUndoStack& getUndoStack();

View file

@ -0,0 +1,37 @@
#include "skillcheck.hpp"
#include <sstream>
#include <components/esm/loadskil.hpp>
#include "../world/universalid.hpp"
CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills)
: mSkills (skills)
{}
int CSMTools::SkillCheckStage::setup()
{
return mSkills.getSize();
}
void CSMTools::SkillCheckStage::perform (int stage, std::vector<std::string>& messages)
{
const ESM::Skill& skill = mSkills.getRecord (stage).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId);
for (int i=0; i<4; ++i)
if (skill.mData.mUseValue[i]<0)
{
std::ostringstream stream;
stream << id.toString() << "|Use value #" << i << " of " << skill.mId << " is negative";
messages.push_back (stream.str());
}
if (skill.mDescription.empty())
messages.push_back (id.toString() + "|" + skill.mId + " has an empty description");
}

View file

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_SKILLCHECK_H
#define CSM_TOOLS_SKILLCHECK_H
#include <components/esm/loadskil.hpp>
#include "../world/idcollection.hpp"
#include "stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that skill records are internally consistent
class SkillCheckStage : public Stage
{
const CSMWorld::IdCollection<ESM::Skill>& mSkills;
public:
SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills);
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

View file

@ -12,6 +12,7 @@
#include "reportmodel.hpp"
#include "mandatoryid.hpp"
#include "skillcheck.hpp"
CSMTools::Operation *CSMTools::Tools::get (int type)
{
@ -51,6 +52,8 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier()
mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(),
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds));
mVerifier->appendStage (new SkillCheckStage (mData.getSkills()));
}
return mVerifier;

View file

@ -30,7 +30,10 @@ namespace CSMWorld
Display_Integer,
Display_Float,
Display_Var,
Display_VarType
Display_GmstVarType,
Display_GlobalVarType,
Display_Specialisation,
Display_Attribute
};
std::string mTitle;

View file

@ -1,6 +1,8 @@
#ifndef CSM_WOLRD_COLUMNS_H
#define CSM_WOLRD_COLUMNS_H
#include <boost/lexical_cast.hpp>
#include "columnbase.hpp"
namespace CSMWorld
@ -12,13 +14,13 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mValue;
return record.get().mValue.getFloat();
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mValue = data.toFloat();
record2.mValue.setFloat (data.toFloat());
record.setModified (record2);
}
@ -35,7 +37,7 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mId.c_str();
return QString::fromUtf8 (record.get().mId.c_str());
}
virtual bool isEditable() const
@ -96,17 +98,17 @@ namespace CSMWorld
template<typename ESXRecordT>
struct VarTypeColumn : public Column<ESXRecordT>
{
VarTypeColumn() : Column<ESXRecordT> ("Type", ColumnBase::Display_VarType) {}
VarTypeColumn (ColumnBase::Display display) : Column<ESXRecordT> ("Type", display) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mType);
return static_cast<int> (record.get().mValue.getType());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mType = static_cast<ESM::VarType> (data.toInt());
record2.mValue.setType (static_cast<ESM::VarType> (data.toInt()));
record.setModified (record2);
}
@ -123,11 +125,21 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const
{
switch (record.get().mType)
switch (record.get().mValue.getType())
{
case ESM::VT_String: return record.get().mStr.c_str(); break;
case ESM::VT_Int: return record.get().mI; break;
case ESM::VT_Float: return record.get().mF; break;
case ESM::VT_String:
return QString::fromUtf8 (record.get().mValue.getString().c_str());
case ESM::VT_Int:
case ESM::VT_Short:
case ESM::VT_Long:
return record.get().mValue.getInteger();
case ESM::VT_Float:
return record.get().mValue.getFloat();
default: return QVariant();
}
@ -137,11 +149,24 @@ namespace CSMWorld
{
ESXRecordT record2 = record.get();
switch (record2.mType)
switch (record2.mValue.getType())
{
case ESM::VT_String: record2.mStr = data.toString().toUtf8().constData(); break;
case ESM::VT_Int: record2.mI = data.toInt(); break;
case ESM::VT_Float: record2.mF = data.toFloat(); break;
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;
}
@ -154,6 +179,112 @@ namespace CSMWorld
return true;
}
};
template<typename ESXRecordT>
struct DescriptionColumn : public Column<ESXRecordT>
{
DescriptionColumn() : Column<ESXRecordT> ("Description", ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mDescription.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mDescription = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct SpecialisationColumn : public Column<ESXRecordT>
{
SpecialisationColumn() : Column<ESXRecordT> ("Specialisation", ColumnBase::Display_Specialisation) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mData.mSpecialization;
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mData.mSpecialization = data.toInt();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct UseValueColumn : public Column<ESXRecordT>
{
int mIndex;
UseValueColumn (int index)
: Column<ESXRecordT> ("Use value #" + boost::lexical_cast<std::string> (index),
ColumnBase::Display_Float), mIndex (index)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mData.mUseValue[mIndex];
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mData.mUseValue[mIndex] = data.toInt();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct AttributeColumn : public Column<ESXRecordT>
{
AttributeColumn() : Column<ESXRecordT> ("Attribute", ColumnBase::Display_Attribute) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mData.mAttribute;
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mData.mAttribute = data.toInt();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
}
#endif

View file

@ -1,7 +1,7 @@
#include "commands.hpp"
#include <QAbstractTableModel>
#include <QAbstractItemModel>
#include "idtableproxymodel.hpp"
#include "idtable.hpp"

View file

@ -3,15 +3,16 @@
#include <stdexcept>
#include <QAbstractTableModel>
#include <QAbstractItemModel>
#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,
void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1,
UniversalId::Type type2)
{
mModels.push_back (model);
@ -26,21 +27,31 @@ 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 FloatValueColumn<ESM::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>);
mGmsts.addColumn (new VarTypeColumn<ESM::GameSetting> (ColumnBase::Display_GmstVarType));
mGmsts.addColumn (new VarValueColumn<ESM::GameSetting>);
mSkills.addColumn (new StringIdColumn<ESM::Skill>);
mSkills.addColumn (new RecordStateColumn<ESM::Skill>);
mSkills.addColumn (new AttributeColumn<ESM::Skill>);
mSkills.addColumn (new SpecialisationColumn<ESM::Skill>);
for (int i=0; i<4; ++i)
mSkills.addColumn (new UseValueColumn<ESM::Skill> (i));
mSkills.addColumn (new DescriptionColumn<ESM::Skill>);
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill);
}
CSMWorld::Data::~Data()
{
for (std::vector<QAbstractTableModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
delete *iter;
}
@ -64,9 +75,19 @@ CSMWorld::IdCollection<ESM::GameSetting>& CSMWorld::Data::getGmsts()
return mGmsts;
}
QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id)
const CSMWorld::IdCollection<ESM::Skill>& CSMWorld::Data::getSkills() const
{
std::map<UniversalId::Type, QAbstractTableModel *>::iterator iter = mModelIndex.find (id.getType());
return mSkills;
}
CSMWorld::IdCollection<ESM::Skill>& CSMWorld::Data::getSkills()
{
return mSkills;
}
QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id)
{
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());
if (iter==mModelIndex.end())
throw std::logic_error ("No table model available for " + id.toString());
@ -100,7 +121,7 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
{
case ESM::REC_GLOB: mGlobals.load (reader, base); break;
case ESM::REC_GMST: mGmsts.load (reader, base); break;
case ESM::REC_SKIL: mSkills.load (reader, base); break;
default:

View file

@ -8,11 +8,12 @@
#include <components/esm/loadglob.hpp>
#include <components/esm/loadgmst.hpp>
#include <components/esm/loadskil.hpp>
#include "idcollection.hpp"
#include "universalid.hpp"
class QAbstractTableModel;
class QAbstractItemModel;
namespace CSMWorld
{
@ -20,14 +21,15 @@ namespace CSMWorld
{
IdCollection<ESM::Global> mGlobals;
IdCollection<ESM::GameSetting> mGmsts;
std::vector<QAbstractTableModel *> mModels;
std::map<UniversalId::Type, QAbstractTableModel *> mModelIndex;
IdCollection<ESM::Skill> mSkills;
std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
// not implemented
Data (const Data&);
Data& operator= (const Data&);
void addModel (QAbstractTableModel *model, UniversalId::Type type1,
void addModel (QAbstractItemModel *model, UniversalId::Type type1,
UniversalId::Type type2 = UniversalId::Type_None);
public:
@ -44,7 +46,11 @@ namespace CSMWorld
IdCollection<ESM::GameSetting>& getGmsts();
QAbstractTableModel *getTableModel (const UniversalId& id);
const IdCollection<ESM::Skill>& getSkills() const;
IdCollection<ESM::Skill>& getSkills();
QAbstractItemModel *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

View file

@ -74,6 +74,8 @@ namespace CSMWorld
virtual const RecordBase& getRecord (const std::string& id) const = 0;
virtual const RecordBase& getRecord (int index) const = 0;
virtual void load (ESM::ESMReader& reader, bool base) = 0;
};
@ -139,7 +141,9 @@ namespace CSMWorld
///
/// \attention Throw san exception, if the type of \a record does not match.
virtual const RecordBase& getRecord (const std::string& id) const;
virtual const Record<ESXRecordT>& getRecord (const std::string& id) const;
virtual const Record<ESXRecordT>& getRecord (int index) const;
virtual void load (ESM::ESMReader& reader, bool base);
@ -373,11 +377,18 @@ namespace CSMWorld
}
template<typename ESXRecordT>
const RecordBase& IdCollection<ESXRecordT>::getRecord (const std::string& id) const
const Record<ESXRecordT>& IdCollection<ESXRecordT>::getRecord (const std::string& id) const
{
int index = getIndex (id);
return mRecords.at (index);
}
template<typename ESXRecordT>
const Record<ESXRecordT>& IdCollection<ESXRecordT>::getRecord (int index) const
{
return mRecords.at (index);
}
}
#endif

View file

@ -96,6 +96,25 @@ bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& paren
return true;
}
QModelIndex CSMWorld::IdTable::index (int row, int column, const QModelIndex& parent) const
{
if (parent.isValid())
return QModelIndex();
if (row<0 || row>=mIdCollection->getSize())
return QModelIndex();
if (column<0 || column>=mIdCollection->getColumns())
return QModelIndex();
return createIndex (row, column);
}
QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const
{
return QModelIndex();
}
void CSMWorld::IdTable::addRecord (const std::string& id)
{
int index = mIdCollection->getSize();

View file

@ -1,14 +1,14 @@
#ifndef CSM_WOLRD_IDTABLE_H
#define CSM_WOLRD_IDTABLE_H
#include <QAbstractTableModel>
#include <QAbstractItemModel>
namespace CSMWorld
{
class IdCollectionBase;
class RecordBase;
class IdTable : public QAbstractTableModel
class IdTable : public QAbstractItemModel
{
Q_OBJECT
@ -39,6 +39,11 @@ namespace CSMWorld
virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex());
virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex())
const;
virtual QModelIndex parent (const QModelIndex& index) const;
void addRecord (const std::string& id);
QModelIndex getModelIndex (const std::string& id, int column) const;

View file

@ -62,7 +62,7 @@ namespace CSMWorld
if (mState==State_Erased)
throw std::logic_error ("attempt to access a deleted record");
return mState==State_BaseOnly ? mBase : mModified;
return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified;
}
template <typename ESXRecordT>
@ -81,9 +81,7 @@ namespace CSMWorld
throw std::logic_error ("attempt to modify a deleted record");
mModified = modified;
if (mState!=State_ModifiedOnly)
mState = mBase==mModified ? State_BaseOnly : State_Modified;
mState = State_Modified;
}
template <typename ESXRecordT>

View file

@ -19,6 +19,7 @@ namespace
{ 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_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills" },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
};
@ -27,6 +28,7 @@ namespace
{
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill" },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
};
@ -43,7 +45,7 @@ CSMWorld::UniversalId::UniversalId (const std::string& universalId)
{
std::string::size_type index = universalId.find (':');
if (index==std::string::npos)
if (index!=std::string::npos)
{
std::string type = universalId.substr (0, index);

View file

@ -37,8 +37,9 @@ namespace CSMWorld
Type_Global,
Type_VerificationResults,
Type_Gmsts,
Type_Gmst
Type_Gmst,
Type_Skills,
Type_Skill
};
private:

View 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();
}

View 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

View file

@ -56,7 +56,7 @@ void CSVDoc::Operations::quitOperation (int type)
mLayout->removeItem ((*iter)->getLayout());
delete *iter;
(*iter)->deleteLater();
mOperations.erase (iter);
if (oldCount > 1)

View file

@ -7,6 +7,7 @@
#include <QMenuBar>
#include <QMdiArea>
#include <QDockWidget>
#include <QtGui/QApplication>
#include "../../model/doc/document.hpp"
@ -39,6 +40,16 @@ void CSVDoc::View::setupFileMenu()
mSave = new QAction (tr ("&Save"), this);
connect (mSave, SIGNAL (triggered()), this, SLOT (save()));
file->addAction (mSave);
QAction *close = new QAction (tr ("&Close"), this);
connect (close, SIGNAL (triggered()), this, SLOT (close()));
file->addAction(close);
QAction *exit = new QAction (tr ("&Exit"), this);
connect (exit, SIGNAL (triggered()), this, SLOT (exit()));
connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *)));
file->addAction(exit);
}
void CSVDoc::View::setupEditMenu()
@ -75,6 +86,10 @@ void CSVDoc::View::setupWorldMenu()
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
world->addAction (gmsts);
QAction *skills = new QAction (tr ("Skills"), this);
connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView()));
world->addAction (skills);
mVerify = new QAction (tr ("&Verify"), this);
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
world->addAction (mVerify);
@ -117,15 +132,15 @@ void CSVDoc::View::updateActions()
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)
CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews)
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),
mViewTotal (totalViews)
{
setDockOptions (QMainWindow::AllowNestedDocks);
resize (300, 300); /// \todo get default size from settings and set reasonable minimal size
mSubViewWindow = new QMainWindow();
setCentralWidget (mSubViewWindow);
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);
setCentralWidget (&mSubViewWindow);
mOperations = new Operations;
addDockWidget (Qt::BottomDockWidgetArea, mOperations);
@ -200,7 +215,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id)
/// \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);
mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this,
SLOT (addSubView (const CSMWorld::UniversalId&)));
@ -233,13 +248,23 @@ void CSVDoc::View::addGmstsSubView()
addSubView (CSMWorld::UniversalId::Type_Gmsts);
}
void CSVDoc::View::addSkillsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Skills);
}
void CSVDoc::View::abortOperation (int type)
{
mDocument->abortOperation (type);
updateActions();
}
QDockWidget *CSVDoc::View::getOperations() const
CSVDoc::Operations *CSVDoc::View::getOperations() const
{
return mOperations;
}
void CSVDoc::View::exit()
{
emit exitApplicationRequest (this);
}

View file

@ -41,7 +41,8 @@ namespace CSVDoc
std::vector<QAction *> mEditingActions;
Operations *mOperations;
SubViewFactoryManager mSubViewFactory;
QMainWindow* mSubViewWindow;
QMainWindow mSubViewWindow;
// not implemented
View (const View&);
@ -65,9 +66,12 @@ namespace CSVDoc
void updateActions();
void exitApplication();
public:
View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent);
View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews);
///< The ownership of \a document is not transferred to *this.
virtual ~View();
@ -82,7 +86,7 @@ namespace CSVDoc
void updateProgress (int current, int max, int type, int threads);
QDockWidget *getOperations() const;
Operations *getOperations() const;
signals:
@ -90,23 +94,29 @@ namespace CSVDoc
void loadDocumentRequest();
void exitApplicationRequest (CSVDoc::View *view);
public slots:
void addSubView (const CSMWorld::UniversalId& id);
void abortOperation (int type);
private slots:
void newView();
void save();
void exit();
void verify();
void addGlobalsSubView();
void addGmstsSubView();
void abortOperation (int type);
void addSkillsSubView();
};
}

View file

@ -12,6 +12,11 @@
#include "view.hpp"
#include <QMessageBox>
#include <QPushButton>
#include <QtGui/QApplication>
#include <QDebug>
void CSVDoc::ViewManager::updateIndices()
{
std::map<CSMDoc::Document *, std::pair<int, int> > documents;
@ -31,12 +36,32 @@ void CSVDoc::ViewManager::updateIndices()
}
CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
: mDocumentManager (documentManager)
: mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false)
{
static const char *sSpecialisations[] =
{
"Combat", "Magic", "Stealth", 0
};
static const char *sAttributes[] =
{
"Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality",
"Luck", 0
};
mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection;
mDelegateFactories->add (CSMWorld::ColumnBase::Display_VarType,
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));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_Specialisation,
new CSVWorld::EnumDelegateFactory (sSpecialisations));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_Attribute,
new CSVWorld::EnumDelegateFactory (sAttributes));
}
CSVDoc::ViewManager::~ViewManager()
@ -59,9 +84,8 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document)
this, SLOT (progress (int, int, int, int, CSMDoc::Document *)));
}
QMainWindow *mainWindow = new QMainWindow;
View *view = new View (*this, document, countViews (document)+1);
View *view = new View (*this, document, countViews (document)+1, mainWindow);
mViews.push_back (view);
@ -90,23 +114,143 @@ bool CSVDoc::ViewManager::closeRequest (View *view)
{
std::vector<View *>::iterator iter = std::find (mViews.begin(), mViews.end(), view);
bool continueWithClose = true;
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());
continueWithClose = notifySaveOnClose (view);
else
{
(*iter)->deleteLater();
mViews.erase (iter);
updateIndices();
}
}
return true;
return continueWithClose;
}
bool CSVDoc::ViewManager::notifySaveOnClose (CSVDoc::View *view)
{
bool result = true;
CSMDoc::Document *document = view->getDocument();
//notify user of saving in progress
if ( (document->getState() & CSMDoc::State_Saving) )
result = showSaveInProgressMessageBox (view);
//notify user of unsaved changes and process response
else if ( document->getState() & CSMDoc::State_Modified)
result = showModifiedDocumentMessageBox (view);
return result;
}
bool CSVDoc::ViewManager::showModifiedDocumentMessageBox (CSVDoc::View *view)
{
QMessageBox messageBox;
CSMDoc::Document *document = view->getDocument();
messageBox.setText ("The document has been modified.");
messageBox.setInformativeText ("Do you want to save your changes?");
messageBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
messageBox.setDefaultButton (QMessageBox::Save);
bool retVal = true;
connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close()));
connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));
mUserWarned = true;
int response = messageBox.exec();
mUserWarned = false;
switch (response)
{
case QMessageBox::Save:
document->save();
mExitOnSaveStateChange = true;
retVal = false;
break;
case QMessageBox::Discard:
disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));
break;
case QMessageBox::Cancel:
//disconnect to prevent unintended view closures
disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));
retVal = false;
break;
default:
break;
}
return retVal;
}
bool CSVDoc::ViewManager::showSaveInProgressMessageBox (CSVDoc::View *view)
{
QMessageBox messageBox;
CSMDoc::Document *document = view->getDocument();
messageBox.setText ("The document is currently being saved.");
messageBox.setInformativeText("Do you want to close now and abort saving, or wait until saving has completed?");
QPushButton* waitButton = messageBox.addButton (tr("Wait"), QMessageBox::YesRole);
QPushButton* closeButton = messageBox.addButton (tr("Close Now"), QMessageBox::RejectRole);
QPushButton* cancelButton = messageBox.addButton (tr("Cancel"), QMessageBox::NoRole);
messageBox.setDefaultButton (waitButton);
bool retVal = true;
//Connections shut down message box if operation ends before user makes a decision.
connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));
connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close()));
//set / clear the user warned flag to indicate whether or not the message box is currently active.
mUserWarned = true;
messageBox.exec();
mUserWarned = false;
//if closed by the warning handler, defaults to the RejectRole button (closeButton)
if (messageBox.clickedButton() == waitButton)
{
//save the View iterator for shutdown after the save operation ends
mExitOnSaveStateChange = true;
retVal = false;
}
else if (messageBox.clickedButton() == closeButton)
{
//disconnect to avoid segmentation fault
disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));
view->abortOperation(CSMDoc::State_Saving);
mExitOnSaveStateChange = true;
}
else if (messageBox.clickedButton() == cancelButton)
{
//abort shutdown, allow save to complete
//disconnection to prevent unintended view closures
mExitOnSaveStateChange = false;
disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *)));
retVal = false;
}
return retVal;
}
void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document)
@ -122,3 +266,25 @@ void CSVDoc::ViewManager::progress (int current, int max, int type, int threads,
if ((*iter)->getDocument()==document)
(*iter)->updateProgress (current, max, type, threads);
}
void CSVDoc::ViewManager::onExitWarningHandler (int state, CSMDoc::Document *document)
{
if ( !(state & CSMDoc::State_Saving) )
{
//if the user is being warned (message box is active), shut down the message box,
//as there is no save operation currently running
if ( mUserWarned )
emit closeMessageBox();
//otherwise, the user has closed the message box before the save operation ended.
//exit the application
else if (mExitOnSaveStateChange)
QApplication::instance()->exit();
}
}
void CSVDoc::ViewManager::exitApplication (CSVDoc::View *view)
{
if (notifySaveOnClose (view))
QApplication::instance()->exit();
}

View file

@ -27,12 +27,17 @@ namespace CSVDoc
CSMDoc::DocumentManager& mDocumentManager;
std::vector<View *> mViews;
CSVWorld::CommandDelegateFactoryCollection *mDelegateFactories;
bool mExitOnSaveStateChange;
bool mUserWarned;
// not implemented
ViewManager (const ViewManager&);
ViewManager& operator= (const ViewManager&);
void updateIndices();
bool notifySaveOnClose (View *view = 0);
bool showModifiedDocumentMessageBox (View *view);
bool showSaveInProgressMessageBox (View *view);
public:
@ -54,11 +59,19 @@ namespace CSVDoc
void loadDocumentRequest();
void closeMessageBox();
public slots:
void exitApplication (CSVDoc::View *view);
private slots:
void documentStateChanged (int state, CSMDoc::Document *document);
void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
void onExitWarningHandler(int state, CSMDoc::Document* document);
};
}

View file

@ -3,7 +3,7 @@
#include <QGridLayout>
#include <QLabel>
#include <QAbstractTableModel>
#include <QAbstractItemModel>
#include <QDoubleSpinBox>
#include <QSpinBox>
#include <QLineEdit>
@ -24,7 +24,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
widget->setLayout (layout);
QAbstractTableModel *model = document.getData().getTableModel (id);
QAbstractItemModel *model = document.getData().getTableModel (id);
int columns = model->columnCount();

View file

@ -1,6 +1,7 @@
#include "enumdelegate.hpp"
#include <cassert>
#include <stdexcept>
#include <QComboBox>
@ -89,6 +90,16 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte
}
CSVWorld::EnumDelegateFactory::EnumDelegateFactory() {}
CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names)
{
assert (names);
for (int i=0; names[i]; ++i)
add (i, names[i]);
}
CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (QUndoStack& undoStack,
QObject *parent) const
{

View file

@ -45,6 +45,11 @@ namespace CSVWorld
public:
EnumDelegateFactory();
EnumDelegateFactory (const char **names);
///< \param names Array of char pointer with a 0-pointer as end mark
virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const;
///< The ownership of the returned CommandDelegate is transferred to the caller.

View file

@ -14,6 +14,9 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Gmsts,
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (false));
manager.add (CSMWorld::UniversalId::Type_Skills,
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (false));
manager.add (CSMWorld::UniversalId::Type_Global,
new CSVDoc::SubViewFactoryWithCreateFlag<DialogueSubView> (true));
}

View file

@ -85,7 +85,7 @@ void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type)
{
{ ESM::VT_None, "empty" },
{ ESM::VT_Short, "short" },
{ ESM::VT_Int, "long" },
{ ESM::VT_Int, "integer" },
{ ESM::VT_Long, "long" },
{ ESM::VT_Float, "float" },
{ ESM::VT_String, "string" },

View file

@ -1,6 +1,8 @@
#ifndef CSV_WORLD_VARTYPEDELEGATE_H
#define CSV_WORLD_VARTYPEDELEGATE_H
#include <components/esm/variant.hpp>
#include "enumdelegate.hpp"
namespace CSVWorld

View file

@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
compositors characterpreview externalrendering globalmap videoplayer
compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction
)
add_openmw_dir (mwinput
@ -26,11 +26,12 @@ add_openmw_dir (mwinput
add_openmw_dir (mwgui
text_input widgets race class birth review windowmanagerimp console dialogue
dialogue_history window_base stats_window messagebox journalwindow charactercreation
map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list
map_window window_pinnable_base tooltips scrollwindow bookwindow list
formatting inventorywindow container hud countdialog tradewindow settingswindow
confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu
itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog
enchantingdialog trainingwindow travelwindow imagebutton exposedwindow
enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons
merchantrepair repair
)
add_openmw_dir (mwdialogue
@ -53,7 +54,7 @@ add_openmw_dir (mwworld
containerstore actiontalk actiontake manualref player cellfunctors failedaction
cells localscripts customdata weather inventorystore ptr actionopen actionread
actionequip timestamp actionalchemy cellstore actionapply actioneat
esmstore store recordcmp
esmstore store recordcmp fallback actionrepair
)
add_openmw_dir (mwclass
@ -64,7 +65,7 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors activators
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate
aiescort aiactivate repair
)
add_openmw_dir (mwbase

View file

@ -9,16 +9,15 @@
#include <components/bsa/bsa_archive.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/translation/translation.hpp>
#include <components/nif/nif_file.hpp>
#include <components/nif/niffile.hpp>
#include <components/nifoverrides/nifoverrides.hpp>
#include <components/nifbullet/bullet_nif_loader.hpp>
#include <components/nifogre/ogre_nif_loader.hpp>
#include <components/nifbullet/bulletnifloader.hpp>
#include <components/nifogre/ogrenifloader.hpp>
#include "mwinput/inputmanagerimp.hpp"
#include "mwgui/windowmanagerimp.hpp"
#include "mwgui/cursorreplace.hpp"
#include "mwscript/scriptmanagerimp.hpp"
#include "mwscript/extensions.hpp"
@ -63,6 +62,13 @@ void OMW::Engine::setAnimationVerbose(bool animverbose)
{
}
bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt)
{
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame);
return true;
}
bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
{
try
@ -143,16 +149,22 @@ OMW::Engine::~Engine()
delete mOgre;
}
// Load all BSA files in data directory.
// Load BSA files
void OMW::Engine::loadBSA()
{
const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa");
for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter)
for (std::vector<std::string>::const_iterator archive = mArchives.begin(); archive != mArchives.end(); ++archive)
{
std::cout << "Adding " << iter->second.string() << std::endl;
Bsa::addBSA(iter->second.string());
if (mFileCollections.doesExist(*archive))
{
const std::string archivePath = mFileCollections.getPath(*archive).string();
std::cout << "Adding BSA archive " << archivePath << std::endl;
Bsa::addBSA(archivePath);
}
else
{
std::cout << "Archive " << *archive << " not found" << std::endl;
}
}
const Files::PathContainer& dataDirs = mFileCollections.getPaths();
@ -193,6 +205,11 @@ void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs)
mFileCollections = Files::Collections (dataDirs, !mFSStrict);
}
// Add BSA archive
void OMW::Engine::addArchive (const std::string& archive) {
mArchives.push_back(archive);
}
// Set resource dir
void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir)
{
@ -317,7 +334,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
addResourcesDirectory(mResDir / "mygui");
addResourcesDirectory(mResDir / "water");
addResourcesDirectory(mResDir / "gbuffer");
addResourcesDirectory(mResDir / "shadows");
addZipResource(mResDir / "mygui" / "Obliviontt.zip");
@ -333,9 +349,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
loadBSA();
// cursor replacer (converts the cursor from the bsa so they can be used by mygui)
MWGui::CursorReplace replacer;
// Create the world
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins,
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap,
@ -440,15 +453,13 @@ void OMW::Engine::go()
if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
std::cout << "\nPress Q/ESC or close window to exit.\n";
// Start the main rendering loop
mOgre->start();
// Save user settings
settings.saveUser(settingspath);
std::cout << "Quitting peacefully.\n";
std::cout << "Quitting peacefully." << std::endl;
}
void OMW::Engine::activate()

View file

@ -64,6 +64,7 @@ namespace OMW
ToUTF8::FromType mEncoding;
ToUTF8::Utf8Encoder* mEncoder;
Files::PathContainer mDataDirs;
std::vector<std::string> mArchives;
boost::filesystem::path mResDir;
OEngine::Render::OgreRenderer *mOgre;
std::string mCellName;
@ -99,12 +100,13 @@ namespace OMW
/// add a .zip resource
void addZipResource (const boost::filesystem::path& path);
/// Load all BSA files in data directory.
/// Load BSA files
void loadBSA();
void executeLocalScripts();
virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt);
virtual bool frameStarted (const Ogre::FrameEvent& evt);
/// Load settings from various files, returns the path to the user settings file
std::string loadSettings (Settings::Manager & settings);
@ -125,6 +127,9 @@ namespace OMW
/// Set data dirs
void setDataDirs(const Files::PathContainer& dataDirs);
/// Add BSA archive
void addArchive(const std::string& archive);
/// Set resource dir
void setResourceDir(const boost::filesystem::path& parResDir);

View file

@ -100,6 +100,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("data-local", bpo::value<std::string>()->default_value(""),
"set local data directory (highest priority)")
("fallback-archive", bpo::value<StringsVector>()->default_value(StringsVector(), "fallback-archive")
->multitoken(), "set fallback BSA archives (later archives have higher priority)")
("resources", bpo::value<std::string>()->default_value("resources"),
"set resources directory")
@ -201,6 +204,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setDataDirs(dataDirs);
// fallback archives
StringsVector archives = variables["fallback-archive"].as<StringsVector>();
for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); it++)
{
engine.addArchive(*it);
}
engine.setResourceDir(variables["resources"].as<std::string>());
// master and plugin

View file

@ -41,6 +41,8 @@ namespace MWBase
virtual void goodbyeSelected() = 0;
virtual void questionAnswered (const std::string& answer) = 0;
virtual bool checkServiceRefused () = 0;
virtual void persuade (int type) = 0;
virtual int getTemporaryDispositionChange () const = 0;
virtual void applyTemporaryDispositionChange (int delta) = 0;

View file

@ -91,6 +91,8 @@ namespace MWBase
virtual bool isGuiMode() const = 0;
virtual bool isConsoleMode() const = 0;
virtual void toggleVisible (MWGui::GuiWindow wnd) = 0;
/// Disallow all inventory mode windows
@ -236,6 +238,10 @@ namespace MWBase
virtual void startSpellMaking(MWWorld::Ptr actor) = 0;
virtual void startEnchanting(MWWorld::Ptr actor) = 0;
virtual void startTraining(MWWorld::Ptr actor) = 0;
virtual void startRepair(MWWorld::Ptr actor) = 0;
virtual void startRepairItem(MWWorld::Ptr item) = 0;
virtual void changePointer (const std::string& name) = 0;
virtual const Translation::Storage& getTranslationDataStorage() const = 0;
};

View file

@ -46,6 +46,7 @@ namespace MWRender
namespace MWWorld
{
class Fallback;
class CellStore;
class Player;
class LocalScripts;
@ -103,11 +104,7 @@ namespace MWBase
virtual void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches) = 0;
virtual void setFallbackValues (const std::map<std::string, std::string>& fallbackMap) = 0;
virtual std::string getFallback (const std::string& key) const = 0;
virtual std::string getFallback (const std::string& key, const std::string& def) const = 0;
virtual const MWWorld::Fallback *getFallback () const = 0;
virtual MWWorld::Player& getPlayer() = 0;
@ -296,6 +293,7 @@ namespace MWBase
virtual bool toggleVanityMode(bool enable, bool force) = 0;
virtual void allowVanityMode(bool allow) = 0;
virtual void togglePlayerLooking(bool enable) = 0;
virtual void changeVanityModeScale(float factor) = 0;
virtual void renderPlayer() = 0;
@ -314,6 +312,7 @@ namespace MWBase
/// \todo this does not belong here
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual void stopVideo() = 0;
virtual void frameStarted (float dt) = 0;
};
}

View file

@ -35,7 +35,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const

View file

@ -38,7 +38,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Armor::getModel(const MWWorld::Ptr &ptr) const
@ -247,8 +247,9 @@ namespace MWClass
text += "\n#{sArmorRating}: " + MWGui::ToolTips::toString(ref->mBase->mData.mArmor);
/// \todo store the current armor health somewhere
text += "\n#{sCondition}: " + MWGui::ToolTips::toString(ref->mBase->mData.mHealth);
int remainingHealth = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mHealth;
text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/"
+ MWGui::ToolTips::toString(ref->mBase->mData.mHealth);
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight) + " (" + typeText + ")";
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");

View file

@ -33,7 +33,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Book::getModel(const MWWorld::Ptr &ptr) const

View file

@ -36,7 +36,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Clothing::getModel(const MWWorld::Ptr &ptr) const

View file

@ -132,7 +132,7 @@ namespace MWClass
const MWWorld::Ptr& actor) const
{
if (MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead())
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionOpen(ptr));
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionOpen(ptr, true));
else
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionTalk (ptr));
}
@ -191,6 +191,12 @@ namespace MWClass
return info;
}
float Creature::getArmorRating (const MWWorld::Ptr& ptr) const
{
/// \todo add Shield magic effect magnitude here, controlled by a GMST (Vanilla vs MCP behaviour)
return 0.f;
}
float Creature::getCapacity (const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
@ -203,9 +209,9 @@ namespace MWClass
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (8)).mMagnitude; // feather
weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Feather)).mMagnitude;
weight += stats.getMagicEffects().get (MWMechanics::EffectKey (7)).mMagnitude; // burden
weight += stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Burden)).mMagnitude;
if (weight<0)
weight = 0;

View file

@ -54,6 +54,9 @@ namespace MWClass
///< Returns total weight of objects inside this object (including modifications from magic
/// effects). Throws an exception, if the object can't hold other objects.
virtual float getArmorRating (const MWWorld::Ptr& ptr) const;
///< @return combined armor rating of this actor
virtual bool isEssential (const MWWorld::Ptr& ptr) const;
///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable)

View file

@ -46,7 +46,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const

View file

@ -50,7 +50,7 @@ namespace MWClass
const std::string &model = ref->mBase->mModel;
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,ref->mBase->mData.mFlags & ESM::Light::Carry);
if (!ref->mBase->mSound.empty())
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, MWBase::SoundManager::Play_Loop);

View file

@ -1,7 +1,7 @@
#include "lockpick.hpp"
#include <components/esm/loadlocks.hpp>
#include <components/esm/loadlock.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -36,13 +36,13 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Tool> *ref =
ptr.get<ESM::Tool>();
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>();
assert(ref->mBase != NULL);
const std::string &model = ref->mBase->mModel;
@ -54,8 +54,8 @@ namespace MWClass
std::string Lockpick::getName (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Tool> *ref =
ptr.get<ESM::Tool>();
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>();
return ref->mBase->mName;
}
@ -75,8 +75,8 @@ namespace MWClass
std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Tool> *ref =
ptr.get<ESM::Tool>();
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>();
return ref->mBase->mScript;
}
@ -92,8 +92,8 @@ namespace MWClass
int Lockpick::getValue (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Tool> *ref =
ptr.get<ESM::Tool>();
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>();
return ref->mBase->mData.mValue;
}
@ -102,7 +102,7 @@ namespace MWClass
{
boost::shared_ptr<Class> instance (new Lockpick);
registerClass (typeid (ESM::Tool).name(), instance);
registerClass (typeid (ESM::Lockpick).name(), instance);
}
std::string Lockpick::getUpSoundId (const MWWorld::Ptr& ptr) const
@ -117,24 +117,24 @@ namespace MWClass
std::string Lockpick::getInventoryIcon (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Tool> *ref =
ptr.get<ESM::Tool>();
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>();
return ref->mBase->mIcon;
}
bool Lockpick::hasToolTip (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Tool> *ref =
ptr.get<ESM::Tool>();
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Tool> *ref =
ptr.get<ESM::Tool>();
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>();
MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount());
@ -142,9 +142,9 @@ namespace MWClass
std::string text;
/// \todo store remaining uses somewhere
int remainingUses = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mUses;
text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->mBase->mData.mUses);
text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses);
text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
@ -171,8 +171,8 @@ namespace MWClass
MWWorld::Ptr
Lockpick::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
{
MWWorld::LiveCellRef<ESM::Tool> *ref =
ptr.get<ESM::Tool>();
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>();
return MWWorld::Ptr(&cell.mLockpicks.insert(*ref), &cell);
}

View file

@ -39,7 +39,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const

View file

@ -183,13 +183,10 @@ namespace MWClass
std::string bodyRaceID = headID.substr(0, end);
std::string model = "meshes\\base_anim.nif";
if (bodyRaceID == "b_n_khajiit_m_" ||
bodyRaceID == "b_n_khajiit_f_" ||
bodyRaceID == "b_n_argonian_m_" ||
bodyRaceID == "b_n_argonian_f_")
{
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
if(race->mData.mFlags & ESM::Race::Beast)
model = "meshes\\base_animkna.nif";
}
return model;
}
@ -220,7 +217,9 @@ namespace MWClass
const MWWorld::Ptr& actor) const
{
if (MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead())
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionOpen(ptr));
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionOpen(ptr, true));
else if (MWWorld::Class::get(actor).getStance(actor, MWWorld::Class::Sneak))
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionOpen(ptr)); // stealing
else
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionTalk (ptr));
}
@ -365,11 +364,10 @@ namespace MWClass
fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed;
}
else if(Npc::getStance(ptr, Run, false))
else if(Npc::getStance(ptr, Run, false) && !Npc::getStance(ptr, Sneak, false))
moveSpeed = runSpeed;
else
moveSpeed = walkSpeed;
if(getMovementSettings(ptr).mLeftRight != 0 && getMovementSettings(ptr).mForwardBackward == 0)
moveSpeed *= 0.75f;
@ -473,9 +471,9 @@ namespace MWClass
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (8)).mMagnitude; // feather
weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Feather)).mMagnitude;
weight += stats.getMagicEffects().get (MWMechanics::EffectKey (7)).mMagnitude; // burden
weight += stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Burden)).mMagnitude;
if (weight<0)
weight = 0;
@ -507,12 +505,75 @@ namespace MWClass
stats.useSkill (skill, *class_, usageType);
}
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
{
MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr);
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
int ratings[MWWorld::InventoryStore::Slots];
int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt();
float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat();
float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat();
int unarmoredSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(ESM::Skill::Unarmored).getModified();
for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i)
{
MWWorld::ContainerStoreIterator it = invStore.getSlot(i);
if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name())
{
// unarmored
ratings[i] = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
}
else
{
MWWorld::LiveCellRef<ESM::Armor> *ref =
it->get<ESM::Armor>();
int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it);
int armorSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(armorSkillType).getModified();
if (ref->mBase->mData.mWeight == 0)
ratings[i] = ref->mBase->mData.mArmor;
else
ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill;
}
}
float shield = MWWorld::Class::get(ptr).getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude;
return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3
+ (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet]
+ ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots]
+ ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron]
) * 0.1
+ (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + MWWorld::InventoryStore::Slot_RightGauntlet)
* 0.05
+ shield;
}
void Npc::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const
{
y = 0;
x = 0;
}
void Npc::adjustScale(const MWWorld::Ptr &ptr, float &scale) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref =
ptr.get<ESM::NPC>();
const ESM::Race* race =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
if (ref->mBase->isMale())
scale *= race->mData.mHeight.mMale;
else
scale *= race->mData.mHeight.mFemale;
}
MWWorld::Ptr
Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
{

View file

@ -81,7 +81,7 @@ namespace MWClass
virtual bool getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce = false)
const;
////< Check if a stance is active or not.
///< Check if a stance is active or not.
virtual float getSpeed (const MWWorld::Ptr& ptr) const;
///< Return movement speed.
@ -104,12 +104,17 @@ namespace MWClass
///< Returns total weight of objects inside this object (including modifications from magic
/// effects). Throws an exception, if the object can't hold other objects.
virtual float getArmorRating (const MWWorld::Ptr& ptr) const;
///< @return combined armor rating of this actor
virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id,
const MWWorld::Ptr& actor) const;
///< Apply \a id on \a ptr.
/// \param actor Actor that is resposible for the ID being applied to \a ptr.
/// \return Any effect?
virtual void adjustScale (const MWWorld::Ptr &ptr, float &scale) const;
virtual void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const;
///< Inform actor \a ptr that a skill use has succeeded.

View file

@ -20,6 +20,8 @@
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwmechanics/npcstats.hpp"
namespace MWClass
{
void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -36,7 +38,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Potion::getModel(const MWWorld::Ptr &ptr) const
@ -138,6 +140,23 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects);
// hide effects the player doesnt know about
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer();
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player);
int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase();
int i=0;
for (MWGui::Widgets::SpellEffectList::iterator it = info.effects.begin(); it != info.effects.end(); ++it)
{
/// \todo this code is duplicated from mwclass/ingredient, put it in a helper function
it->mKnown = ( (i == 0 && alchemySkill >= 15)
|| (i == 1 && alchemySkill >= 30)
|| (i == 2 && alchemySkill >= 45)
|| (i == 3 && alchemySkill >= 60));
++i;
}
info.isPotion = true;
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {

View file

@ -1,7 +1,7 @@
#include "probe.hpp"
#include <components/esm/loadlocks.hpp>
#include <components/esm/loadprob.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -36,7 +36,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Probe::getModel(const MWWorld::Ptr &ptr) const
@ -141,9 +141,9 @@ namespace MWClass
std::string text;
/// \todo store remaining uses somewhere
int remainingUses = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mUses;
text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->mBase->mData.mUses);
text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses);
text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");

View file

@ -1,7 +1,7 @@
#include "repair.hpp"
#include <components/esm/loadlocks.hpp>
#include <components/esm/loadrepa.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -12,6 +12,7 @@
#include "../mwworld/cellstore.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/nullaction.hpp"
#include "../mwworld/actionrepair.hpp"
#include "../mwgui/tooltips.hpp"
@ -34,7 +35,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Repair::getModel(const MWWorld::Ptr &ptr) const
@ -120,6 +121,19 @@ namespace MWClass
return (ref->mBase->mName != "");
}
bool Repair::hasItemHealth (const MWWorld::Ptr& ptr) const
{
return true;
}
int Repair::getItemMaxHealth (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Repair> *ref =
ptr.get<ESM::Repair>();
return ref->mBase->mData.mUses;
}
MWGui::ToolTipInfo Repair::getToolTipInfo (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Repair> *ref =
@ -131,9 +145,9 @@ namespace MWClass
std::string text;
/// \todo store remaining uses somewhere
int remainingUses = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mUses;
text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->mBase->mData.mUses);
text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses);
text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
@ -156,4 +170,9 @@ namespace MWClass
return MWWorld::Ptr(&cell.mRepairs.insert(*ref), &cell);
}
boost::shared_ptr<MWWorld::Action> Repair::use (const MWWorld::Ptr& ptr) const
{
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionRepair(ptr));
}
}

View file

@ -49,6 +49,18 @@ namespace MWClass
///< Return name of inventory icon.
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual boost::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
const;
///< Generate action for using via inventory menu (default implementation: return a
/// null action).
virtual bool hasItemHealth (const MWWorld::Ptr& ptr) const;
///< \return Item health data available? (default implementation: false)
virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const;
///< Return item max health or throw an exception, if class does not have item health
/// (default implementation: throw an exceoption)
};
}

View file

@ -36,7 +36,7 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
physics.addObject(ptr,true);
}
std::string Weapon::getModel(const MWWorld::Ptr &ptr) const
@ -75,7 +75,10 @@ namespace MWClass
bool Weapon::hasItemHealth (const MWWorld::Ptr& ptr) const
{
return true;
MWWorld::LiveCellRef<ESM::Weapon> *ref =
ptr.get<ESM::Weapon>();
return (ref->mBase->mData.mType < 11); // thrown weapons and arrows/bolts don't have health, only quantity
}
int Weapon::getItemMaxHealth (const MWWorld::Ptr& ptr) const
@ -334,9 +337,12 @@ namespace MWClass
}
}
/// \todo store the current weapon health somewhere
if (ref->mBase->mData.mType < 11) // thrown weapons and arrows/bolts don't have health, only quantity
text += "\n#{sCondition}: " + MWGui::ToolTips::toString(ref->mBase->mData.mHealth);
{
int remainingHealth = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mHealth;
text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/"
+ MWGui::ToolTips::toString(ref->mBase->mData.mHealth);
}
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");

View file

@ -2,6 +2,7 @@
#include "dialoguemanagerimp.hpp"
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <iterator>
@ -240,7 +241,7 @@ namespace MWDialogue
}
}
void DialogueManager::executeTopic (const std::string& topic)
void DialogueManager::executeTopic (const std::string& topic, bool randomResponse)
{
Filter filter (mActor, mChoice, mTalkedTo);
@ -251,8 +252,12 @@ namespace MWDialogue
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
if (const ESM::DialInfo *info = filter.search (dialogue, true))
std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, true, true);
if (!infos.empty())
{
const ESM::DialInfo* info = infos[randomResponse ? std::rand() % infos.size() : 0];
parseText (info->mResponse);
if (dialogue.mType==ESM::Dialogue::Persuasion)
@ -362,6 +367,9 @@ namespace MWDialogue
if (services & ESM::NPC::Enchanting)
windowServices |= MWGui::DialogueWindow::Service_Enchant;
if (services & ESM::NPC::Repair)
windowServices |= MWGui::DialogueWindow::Service_Repair;
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->setServices (windowServices);
@ -500,7 +508,7 @@ namespace MWDialogue
text = "Bribe";
}
executeTopic (text + (success ? " Success" : " Fail"));
executeTopic (text + (success ? " Success" : " Fail"), true);
}
int DialogueManager::getTemporaryDispositionChange() const
@ -513,6 +521,37 @@ namespace MWDialogue
mTemporaryDispositionChange += delta;
}
bool DialogueManager::checkServiceRefused()
{
Filter filter (mActor, mChoice, mTalkedTo);
const MWWorld::Store<ESM::Dialogue> &dialogues =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal");
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, false, false, true);
if (!infos.empty())
{
const ESM::DialInfo* info = infos[0];
parseText (info->mResponse);
const MWWorld::Store<ESM::GameSetting>& gmsts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
win->addTitle (gmsts.find ("sServiceRefusal")->getString());
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript);
return true;
}
return false;
}
std::vector<HyperTextToken> ParseHyperText(const std::string& text)
{
std::vector<HyperTextToken> result;

View file

@ -48,7 +48,7 @@ namespace MWDialogue
void printError (const std::string& error);
void executeTopic (const std::string& topic);
void executeTopic (const std::string& topic, bool randomResponse=false);
public:
@ -65,6 +65,8 @@ namespace MWDialogue
virtual MWWorld::Ptr getActor() const;
///< Return the actor the player is currently talking to.
virtual bool checkServiceRefused ();
//calbacks for the GUI
virtual void keywordSelected (const std::string& keyword);
virtual void goodbyeSelected();

View file

@ -121,7 +121,7 @@ bool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const
return true;
}
bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info) const
bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert) const
{
bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name());
@ -129,14 +129,19 @@ bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info) const
return true;
int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor);
return actorDisposition >= info.mData.mDisposition;
// For service refusal, the disposition check is inverted. However, a value of 0 still means "always succeed".
return invert ? (info.mData.mDisposition == 0 || actorDisposition < info.mData.mDisposition)
: (actorDisposition >= info.mData.mDisposition);
}
bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const
{
if (select.isNpcOnly() && mActor.getTypeName()!=typeid (ESM::NPC).name())
return select.isInverted();
if (select.isNpcOnly() && (mActor.getTypeName() != typeid (ESM::NPC).name()))
// If the actor is a creature, we do not test the conditions applicable
// only to NPCs. Such conditions can never be satisfied, apart
// inverted ones (NotClass, NotRace, NotFaction return true
// because creatures are not of any race, class or faction).
return select.getType() == SelectWrapper::Type_Inverted;
switch (select.getType())
{
@ -144,6 +149,9 @@ bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const
case SelectWrapper::Type_Integer: return select.selectCompare (getSelectStructInteger (select));
case SelectWrapper::Type_Numeric: return testSelectStructNumeric (select);
case SelectWrapper::Type_Boolean: return select.selectCompare (getSelectStructBoolean (select));
// We must not do the comparison for inverted functions (eg. Function_NotClass)
case SelectWrapper::Type_Inverted: return getSelectStructBoolean (select);
}
return true;
@ -412,25 +420,49 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
return false;
case SelectWrapper::Function_Id:
case SelectWrapper::Function_NotId:
return select.getName()==Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor));
return select.getName()!=Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor));
case SelectWrapper::Function_Faction:
case SelectWrapper::Function_NotFaction:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mFaction)==select.getName();
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mFaction)!=select.getName();
case SelectWrapper::Function_Class:
case SelectWrapper::Function_NotClass:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mClass)==select.getName();
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mClass)!=select.getName();
case SelectWrapper::Function_Race:
case SelectWrapper::Function_NotRace:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mRace)==select.getName();
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mRace)!=select.getName();
case SelectWrapper::Function_Cell:
case SelectWrapper::Function_NotCell:
return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)==select.getName();
return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)!=select.getName();
case SelectWrapper::Function_NotLocal:
{
std::string scriptName = MWWorld::Class::get (mActor).getScript (mActor);
if (scriptName.empty())
// This actor has no attached script, so there is no local variable
return true;
const ESM::Script *script =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptName);
std::string name = select.getName();
int i = 0;
for (; i < static_cast<int> (script->mVarNames.size()); ++i)
if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name)
break;
if (i >= static_cast<int> (script->mVarNames.size()))
return true; // script does not have a variable of this name
return false;
}
case SelectWrapper::Function_SameGender:
@ -458,7 +490,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_PcCorprus:
return MWWorld::Class::get (player).getCreatureStats (player).
getMagicEffects().get (132).mMagnitude!=0;
getMagicEffects().get (ESM::MagicEffect::Corprus).mMagnitude!=0;
case SelectWrapper::Function_PcExpelled:
{
@ -559,8 +591,21 @@ MWDialogue::Filter::Filter (const MWWorld::Ptr& actor, int choice, bool talkedTo
: mActor (actor), mChoice (choice), mTalkedToPlayer (talkedToPlayer)
{}
const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const
const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const
{
std::vector<const ESM::DialInfo *> suitableInfos = list (dialogue, fallbackToInfoRefusal, false);
if (suitableInfos.empty())
return NULL;
else
return suitableInfos[0];
}
std::vector<const ESM::DialInfo *> MWDialogue::Filter::list (const ESM::Dialogue& dialogue,
bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition) const
{
std::vector<const ESM::DialInfo *> infos;
bool infoRefusal = false;
// Iterate over topic responses to find a matching one
@ -569,14 +614,17 @@ const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue,
{
if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter))
{
if (testDisposition (*iter))
return &*iter;
if (testDisposition (*iter, invertDisposition)) {
infos.push_back(&*iter);
if (!searchAll)
break;
}
else
infoRefusal = true;
}
}
if (infoRefusal && fallbackToInfoRefusal)
if (infos.empty() && infoRefusal && fallbackToInfoRefusal)
{
// No response is valid because of low NPC disposition,
// search a response in the topic "Info Refusal"
@ -588,11 +636,14 @@ const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue,
for (std::vector<ESM::DialInfo>::const_iterator iter = infoRefusalDialogue.mInfo.begin();
iter!=infoRefusalDialogue.mInfo.end(); ++iter)
if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter))
return &*iter;
if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter, invertDisposition)) {
infos.push_back(&*iter);
if (!searchAll)
break;
}
}
return 0;
return infos;
}
bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const

View file

@ -1,6 +1,8 @@
#ifndef GAME_MWDIALOGUE_FILTER_H
#define GAME_MWDIALOGUE_FILTER_H
#include <vector>
#include "../mwworld/ptr.hpp"
namespace ESM
@ -28,8 +30,8 @@ namespace MWDialogue
bool testSelectStructs (const ESM::DialInfo& info) const;
///< Are all select structs matching?
bool testDisposition (const ESM::DialInfo& info) const;
///< Is the actor disposition toward the player high enough?
bool testDisposition (const ESM::DialInfo& info, bool invert=false) const;
///< Is the actor disposition toward the player high enough (or low enough, if \a invert is true)?
bool testSelectStruct (const SelectWrapper& select) const;
@ -51,7 +53,10 @@ namespace MWDialogue
Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer);
const ESM::DialInfo *search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const;
std::vector<const ESM::DialInfo *> list (const ESM::Dialogue& dialogue,
bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition=false) const;
const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const;
///< Get a matching response for the requested dialogue.
/// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition.

View file

@ -31,14 +31,13 @@ namespace
template<typename T>
bool selectCompareImp (const ESM::DialInfo::SelectStruct& select, T value1)
{
if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int ||
select.mType==ESM::VT_Long)
if (select.mValue.getType()==ESM::VT_Int)
{
return selectCompareImp (select.mSelectRule[4], value1, select.mI);
return selectCompareImp (select.mSelectRule[4], value1, select.mValue.getInteger());
}
else if (select.mType==ESM::VT_Float)
else if (select.mValue.getType()==ESM::VT_Float)
{
return selectCompareImp (select.mSelectRule[4], value1, select.mF);
return selectCompareImp (select.mSelectRule[4], value1, select.mValue.getFloat());
}
else
throw std::runtime_error (
@ -113,12 +112,12 @@ MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::getFunction() con
case '4': return Function_Journal;
case '5': return Function_Item;
case '6': return Function_Dead;
case '7': return Function_Id;
case '8': return Function_Faction;
case '9': return Function_Class;
case 'A': return Function_Race;
case 'B': return Function_Cell;
case 'C': return Function_Local;
case '7': return Function_NotId;
case '8': return Function_NotFaction;
case '9': return Function_NotClass;
case 'A': return Function_NotRace;
case 'B': return Function_NotCell;
case 'C': return Function_NotLocal;
}
return Function_None;
@ -220,7 +219,6 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const
static const Function booleanFunctions[] =
{
Function_False,
Function_Id, Function_Faction, Function_Class, Function_Race, Function_Cell,
Function_SameGender, Function_SameRace, Function_SameFaction,
Function_PcCommonDisease, Function_PcBlightDisease, Function_PcCorprus,
Function_PcExpelled,
@ -232,6 +230,13 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const
Function_None // end marker
};
static const Function invertedBooleanFunctions[] =
{
Function_NotId, Function_NotFaction, Function_NotClass,
Function_NotRace, Function_NotCell, Function_NotLocal,
Function_None // end marker
};
Function function = getFunction();
for (int i=0; integerFunctions[i]!=Function_None; ++i)
@ -246,21 +251,18 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const
if (booleanFunctions[i]==function)
return Type_Boolean;
for (int i=0; invertedBooleanFunctions[i]!=Function_None; ++i)
if (invertedBooleanFunctions[i]==function)
return Type_Inverted;
return Type_None;
}
bool MWDialogue::SelectWrapper::isInverted() const
{
char type = mSelect.mSelectRule[1];
return type=='7' || type=='8' || type=='9' || type=='A' || type=='B' || type=='C';
}
bool MWDialogue::SelectWrapper::isNpcOnly() const
{
static const Function functions[] =
{
Function_Faction, SelectWrapper::Function_Class, SelectWrapper::Function_Race,
Function_NotFaction, Function_NotClass, Function_NotRace,
Function_SameGender, Function_SameRace, Function_SameFaction,
Function_PcSkill,
Function_PcExpelled,
@ -284,17 +286,17 @@ bool MWDialogue::SelectWrapper::isNpcOnly() const
bool MWDialogue::SelectWrapper::selectCompare (int value) const
{
return selectCompareImp (mSelect, value)!=isInverted(); // logic XOR
return selectCompareImp (mSelect, value);
}
bool MWDialogue::SelectWrapper::selectCompare (float value) const
{
return selectCompareImp (mSelect, value)!=isInverted(); // logic XOR
return selectCompareImp (mSelect, value);
}
bool MWDialogue::SelectWrapper::selectCompare (bool value) const
{
return selectCompareImp (mSelect, static_cast<int> (value))!=isInverted(); // logic XOR
return selectCompareImp (mSelect, static_cast<int> (value));
}
std::string MWDialogue::SelectWrapper::getName() const

View file

@ -17,11 +17,12 @@ namespace MWDialogue
Function_Journal,
Function_Item,
Function_Dead,
Function_Id,
Function_Faction,
Function_Class,
Function_Race,
Function_Cell,
Function_NotId,
Function_NotFaction,
Function_NotClass,
Function_NotRace,
Function_NotCell,
Function_NotLocal,
Function_Local,
Function_Global,
Function_SameGender, Function_SameRace, Function_SameFaction,
@ -50,7 +51,8 @@ namespace MWDialogue
Type_None,
Type_Integer,
Type_Numeric,
Type_Boolean
Type_Boolean,
Type_Inverted
};
private:
@ -67,8 +69,6 @@ namespace MWDialogue
Type getType() const;
bool isInverted() const;
bool isNpcOnly() const;
///< \attention Do not call any of the select functions for this select struct!

View file

@ -237,7 +237,7 @@ namespace MWGui
Widgets::SpellEffectList _list = Widgets::MWEffectList::effectListFromESM(&list);
effectsWidget->setEffectList(_list);
std::vector<MyGUI::WidgetPtr> effectItems;
std::vector<MyGUI::Widget*> effectItems;
effectsWidget->createEffectWidgets(effectItems, mEffectsBox, coord, false, 0);
effectsWidget->setCoord(coord);
}

View file

@ -40,11 +40,11 @@ BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager)
mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
MyGUI::ButtonPtr backButton;
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->setCaption(mWindowManager.getGameSettingString("sOK", ""));
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked);
@ -55,7 +55,7 @@ BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager)
void BirthDialog::setNextButtonShow(bool shown)
{
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
if (shown)
@ -82,7 +82,7 @@ void BirthDialog::setBirthId(const std::string &birthId)
if (boost::iequals(*mBirthList->getItemDataAt<std::string>(i), birthId))
{
mBirthList->setIndexSelected(i);
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
break;
}
@ -110,7 +110,7 @@ void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index)
if (_index == MyGUI::ITEM_NONE)
return;
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
const std::string *birthId = mBirthList->getItemDataAt<std::string>(_index);
@ -159,7 +159,7 @@ void BirthDialog::updateBirths()
void BirthDialog::updateSpells()
{
for (std::vector<MyGUI::WidgetPtr>::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it)
for (std::vector<MyGUI::Widget*>::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it)
{
MyGUI::Gui::getInstance().destroyWidget(*it);
}

View file

@ -46,9 +46,9 @@ namespace MWGui
void updateSpells();
MyGUI::ListBox* mBirthList;
MyGUI::WidgetPtr mSpellArea;
MyGUI::Widget* mSpellArea;
MyGUI::ImageBox* mBirthImage;
std::vector<MyGUI::WidgetPtr> mSpellItems;
std::vector<MyGUI::Widget*> mSpellItems;
std::string mCurrentBirthId;
};

View file

@ -8,103 +8,33 @@
#include "dialogue.hpp"
#include "mode.hpp"
#include "inventorywindow.hpp"
#include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/fallback.hpp"
namespace
{
struct Step
{
const char* mText;
const char* mButtons[3];
const char* mSound;
ESM::Class::Specialization mSpecializations[3]; // The specialization for each answer
const std::string mText;
const std::string mButtons[3];
const std::string mSound;
};
static boost::array<Step, 10> sGenerateClassSteps = { {
// Question 1
{"On a clear day you chance upon a strange animal, its legs trapped in a hunter's clawsnare. Judging from the bleeding, it will not survive long.",
{"Draw your dagger, mercifully endings its life with a single thrust.",
"Use herbs from your pack to put it to sleep.",
"Do not interfere in the natural evolution of events, but rather take the opportunity to learn more about a strange animal that you have never seen before."},
"vo\\misc\\chargen qa1.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
},
// Question 2
{"One Summer afternoon your father gives you a choice of chores.",
{"Work in the forge with him casting iron for a new plow.",
"Gather herbs for your mother who is preparing dinner.",
"Go catch fish at the stream using a net and line."},
"vo\\misc\\chargen qa2.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
},
// Question 3
{"Your cousin has given you a very embarrassing nickname and, even worse, likes to call you it in front of your friends. You asked him to stop, but he finds it very amusing to watch you blush.",
{"Beat up your cousin, then tell him that if he ever calls you that nickname again, you will bloody him worse than this time.",
"Make up a story that makes your nickname a badge of honor instead of something humiliating.",
"Make up an even more embarrassing nickname for him and use it constantly until he learns his lesson."},
"vo\\misc\\chargen qa3.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
},
// Question 4
{"There is a lot of heated discussion at the local tavern over a grouped of people called 'Telepaths'. They have been hired by certain City-State kings. Rumor has it these Telepaths read a person's mind and tell their lord whether a follower is telling the truth or not.",
{"This is a terrible practice. A person's thoughts are his own and no one, not even a king, has the right to make such an invasion into another human's mind.",
"Loyal followers to the king have nothing to fear from a Telepath. It is important to have a method of finding assassins and spies before it is too late.",
"In these times, it is a necessary evil. Although you do not necessarily like the idea, a Telepath could have certain advantages during a time of war or in finding someone innocent of a crime."},
"vo\\misc\\chargen qa4.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
},
// Question 5
{"Your mother sends you to the market with a list of goods to buy. After you finish you find that by mistake a shopkeeper has given you too much money back in exchange for one of the items.",
{"Return to the store and give the shopkeeper his hard-earned money, explaining to him the mistake?",
"Decide to put the extra money to good use and purchase items that would help your family?",
"Pocket the extra money, knowing that shopkeepers in general tend to overcharge customers anyway?"},
"vo\\misc\\chargen qa5.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
},
// Question 6
{"While in the market place you witness a thief cut a purse from a noble. Even as he does so, the noble notices and calls for the city guards. In his haste to get away, the thief drops the purse near you. Surprisingly no one seems to notice the bag of coins at your feet.",
{"Pick up the bag and signal to the guard, knowing that the only honorable thing to do is return the money to its rightful owner.",
"Leave the bag there, knowing that it is better not to get involved.",
"Pick up the bag and pocket it, knowing that the extra windfall will help your family in times of trouble."},
"vo\\misc\\chargen qa6.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
},
// Question 7
{"Your father sends you on a task which you loathe, cleaning the stables. On the way there, pitchfork in hand, you run into your friend from the homestead near your own. He offers to do it for you, in return for a future favor of his choosing.",
{"Decline his offer, knowing that your father expects you to do the work, and it is better not to be in debt.",
"Ask him to help you, knowing that two people can do the job faster than one, and agree to help him with one task of his choosing in the future.",
"Accept his offer, reasoning that as long as the stables are cleaned, it matters not who does the cleaning."},
"vo\\misc\\chargen qa7.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
},
// Question 8
{"Your mother asks you to help fix the stove. While you are working, a very hot pipe slips its mooring and falls towards her.",
{"Position yourself between the pipe and your mother.",
"Grab the hot pipe and try to push it away.",
"Push your mother out of the way."},
"vo\\misc\\chargen qa8.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
},
// Question 9
{"While in town the baker gives you a sweetroll. Delighted, you take it into an alley to enjoy only to be intercepted by a gang of three other kids your age. The leader demands the sweetroll, or else he and his friends will beat you and take it.",
{"Drop the sweetroll and step on it, then get ready for the fight.",
"Give him the sweetroll now without argument, knowing that later this afternoon you will have all your friends with you and can come and take whatever he owes you.",
"Act like you're going to give him the sweetroll, but at the last minute throw it in the air, hoping that they'll pay attention to it long enough for you to get a shot in on the leader."},
"vo\\misc\\chargen qa9.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
},
// Question 10
{"Entering town you find that you are witness to a very well-dressed man running from a crowd. He screams to you for help. The crowd behind him seem very angry.",
{"Rush to the town's aid immediately, despite your lack of knowledge of the circumstances.",
"Stand aside and allow the man and the mob to pass, realizing it is probably best not to get involved.",
"Rush to the man's aid immediately, despite your lack of knowledge of the circumstances."},
"vo\\misc\\chargen qa10.wav",
{ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}
}
} };
const ESM::Class::Specialization mSpecializations[3]={ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}; // The specialization for each answer
Step sGenerateClassSteps(int number) {
number++;
const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback();
Step step = {fallback->getFallbackString("Question_"+boost::lexical_cast<std::string>(number)+"_Question"),
{fallback->getFallbackString("Question_"+boost::lexical_cast<std::string>(number)+"_AnswerOne"),
fallback->getFallbackString("Question_"+boost::lexical_cast<std::string>(number)+"_AnswerTwo"),
fallback->getFallbackString("Question_"+boost::lexical_cast<std::string>(number)+"_AnswerThree")},
"vo\\misc\\chargen qa"+boost::lexical_cast<std::string>(number)+".wav"
};
return step;
}
struct ClassPoint
{
@ -206,7 +136,9 @@ void CharacterCreation::spawnDialog(const char id)
mRaceDialog->setRaceId(mPlayerRaceId);
mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone);
mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack);
mRaceDialog->setVisible(true);;
mRaceDialog->setVisible(true);
if (mCreationStage < CSE_NameChosen)
mCreationStage = CSE_NameChosen;
break;
case GM_Class:
@ -215,6 +147,8 @@ void CharacterCreation::spawnDialog(const char id)
mClassChoiceDialog = new ClassChoiceDialog(*mWM);
mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice);
mClassChoiceDialog->setVisible(true);
if (mCreationStage < CSE_RaceChosen)
mCreationStage = CSE_RaceChosen;
break;
case GM_ClassPick:
@ -226,6 +160,8 @@ void CharacterCreation::spawnDialog(const char id)
mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone);
mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack);
mPickClassDialog->setVisible(true);
if (mCreationStage < CSE_RaceChosen)
mCreationStage = CSE_RaceChosen;
break;
case GM_Birth:
@ -237,6 +173,8 @@ void CharacterCreation::spawnDialog(const char id)
mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone);
mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack);
mBirthSignDialog->setVisible(true);
if (mCreationStage < CSE_ClassChosen)
mCreationStage = CSE_ClassChosen;
break;
case GM_ClassCreate:
@ -247,6 +185,8 @@ void CharacterCreation::spawnDialog(const char id)
mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone);
mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack);
mCreateClassDialog->setVisible(true);
if (mCreationStage < CSE_RaceChosen)
mCreationStage = CSE_RaceChosen;
break;
case GM_ClassGenerate:
mGenerateClassStep = 0;
@ -255,6 +195,8 @@ void CharacterCreation::spawnDialog(const char id)
mGenerateClassSpecializations[1] = 0;
mGenerateClassSpecializations[2] = 0;
showClassQuestionDialog();
if (mCreationStage < CSE_RaceChosen)
mCreationStage = CSE_RaceChosen;
break;
case GM_Review:
mWM->removeDialog(mReviewDialog);
@ -292,6 +234,8 @@ void CharacterCreation::spawnDialog(const char id)
mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack);
mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog);
mReviewDialog->setVisible(true);
if (mCreationStage < CSE_BirthSignChosen)
mCreationStage = CSE_BirthSignChosen;
break;
}
}
@ -624,7 +568,7 @@ void CharacterCreation::onClassQuestionChosen(int _index)
return;
}
ESM::Class::Specialization specialization = sGenerateClassSteps[mGenerateClassStep].mSpecializations[_index];
ESM::Class::Specialization specialization = mSpecializations[_index];
if (specialization == ESM::Class::Stealth)
++mGenerateClassSpecializations[0];
else if (specialization == ESM::Class::Combat)
@ -637,7 +581,7 @@ void CharacterCreation::onClassQuestionChosen(int _index)
void CharacterCreation::showClassQuestionDialog()
{
if (mGenerateClassStep == sGenerateClassSteps.size())
if (mGenerateClassStep == 10)
{
static boost::array<ClassPoint, 23> classes = { {
{"Acrobat", {6, 2, 2}},
@ -704,7 +648,7 @@ void CharacterCreation::showClassQuestionDialog()
return;
}
if (mGenerateClassStep > sGenerateClassSteps.size())
if (mGenerateClassStep > 10)
{
mWM->popGuiMode();
mWM->pushGuiMode(GM_Class);
@ -717,22 +661,19 @@ void CharacterCreation::showClassQuestionDialog()
mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM);
InfoBoxDialog::ButtonList buttons;
mGenerateClassQuestionDialog->setText(sGenerateClassSteps[mGenerateClassStep].mText);
buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[0]);
buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[1]);
buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[2]);
mGenerateClassQuestionDialog->setText(sGenerateClassSteps(mGenerateClassStep).mText);
buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[0]);
buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[1]);
buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[2]);
mGenerateClassQuestionDialog->setButtons(buttons);
mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen);
mGenerateClassQuestionDialog->setVisible(true);
MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps[mGenerateClassStep].mSound);
MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound);
}
void CharacterCreation::onGenerateClassBack()
{
if(mCreationStage < CSE_ClassChosen)
mCreationStage = CSE_ClassChosen;
mWM->removeDialog(mGenerateClassResultDialog);
mGenerateClassResultDialog = 0;

View file

@ -31,11 +31,11 @@ GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parW
getWidget(mClassImage, "ClassImage");
getWidget(mClassName, "ClassName");
MyGUI::ButtonPtr backButton;
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->setCaption(mWindowManager.getGameSettingString("sOK", ""));
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked);
@ -97,11 +97,11 @@ PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager)
getWidget(mClassImage, "ClassImage");
MyGUI::ButtonPtr backButton;
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked);
@ -111,7 +111,7 @@ PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager)
void PickClassDialog::setNextButtonShow(bool shown)
{
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
if (shown)
@ -138,7 +138,7 @@ void PickClassDialog::setClassId(const std::string &classId)
if (boost::iequals(*mClassList->getItemDataAt<std::string>(i), classId))
{
mClassList->setIndexSelected(i);
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
break;
}
@ -166,7 +166,7 @@ void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index)
if (_index == MyGUI::ITEM_NONE)
return;
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
const std::string *classId = mClassList->getItemDataAt<std::string>(_index);
@ -256,7 +256,7 @@ void InfoBoxDialog::fitToText(MyGUI::TextBox* widget)
widget->setSize(size);
}
void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin)
void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin)
{
size_t count = widget->getChildCount();
int pos = 0;
@ -264,7 +264,7 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin)
int width = 0;
for (unsigned i = 0; i < count; ++i)
{
MyGUI::WidgetPtr child = widget->getChildAt(i);
MyGUI::Widget* child = widget->getChildAt(i);
if (!child->getVisible())
continue;
@ -302,7 +302,7 @@ std::string InfoBoxDialog::getText() const
void InfoBoxDialog::setButtons(ButtonList &buttons)
{
for (std::vector<MyGUI::ButtonPtr>::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it)
for (std::vector<MyGUI::Button*>::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it)
{
MyGUI::Gui::getInstance().destroyWidget(*it);
}
@ -310,7 +310,7 @@ void InfoBoxDialog::setButtons(ButtonList &buttons)
mCurrentButton = -1;
// TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget
MyGUI::ButtonPtr button;
MyGUI::Button* button;
MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10);
ButtonList::const_iterator end = buttons.end();
for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it)
@ -342,11 +342,11 @@ int InfoBoxDialog::getChosenButton() const
return mCurrentButton;
}
void InfoBoxDialog::onButtonClicked(MyGUI::WidgetPtr _sender)
void InfoBoxDialog::onButtonClicked(MyGUI::Widget* _sender)
{
std::vector<MyGUI::ButtonPtr>::const_iterator end = mButtons.end();
std::vector<MyGUI::Button*>::const_iterator end = mButtons.end();
int i = 0;
for (std::vector<MyGUI::ButtonPtr>::const_iterator it = mButtons.begin(); it != end; ++it)
for (std::vector<MyGUI::Button*>::const_iterator it = mButtons.begin(); it != end; ++it)
{
if (*it == _sender)
{
@ -376,10 +376,10 @@ ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager)
CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager)
: WindowModal("openmw_chargen_create_class.layout", parWindowManager)
, mSpecDialog(nullptr)
, mAttribDialog(nullptr)
, mSkillDialog(nullptr)
, mDescDialog(nullptr)
, mSpecDialog(NULL)
, mAttribDialog(NULL)
, mSkillDialog(NULL)
, mDescDialog(NULL)
{
// Centre dialog
center();
@ -420,15 +420,15 @@ CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager)
// Make sure the edit box has focus
MyGUI::InputManager::getInstance().setKeyFocusWidget(mEditName);
MyGUI::ButtonPtr descriptionButton;
MyGUI::Button* descriptionButton;
getWidget(descriptionButton, "DescriptionButton");
descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked);
MyGUI::ButtonPtr backButton;
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked);
@ -518,7 +518,7 @@ std::vector<ESM::Skill::SkillEnum> CreateClassDialog::getMinorSkills() const
void CreateClassDialog::setNextButtonShow(bool shown)
{
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
if (shown)
@ -544,7 +544,7 @@ void CreateClassDialog::onDialogCancel()
mDescDialog = 0;
}
void CreateClassDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender)
void CreateClassDialog::onSpecializationClicked(MyGUI::Widget* _sender)
{
delete mSpecDialog;
mSpecDialog = new SelectSpecializationDialog(mWindowManager);
@ -694,7 +694,7 @@ SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& pa
ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic);
ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth);
MyGUI::ButtonPtr cancelButton;
MyGUI::Button* cancelButton;
getWidget(cancelButton, "CancelButton");
cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", ""));
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked);
@ -706,7 +706,7 @@ SelectSpecializationDialog::~SelectSpecializationDialog()
// widget controls
void SelectSpecializationDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender)
void SelectSpecializationDialog::onSpecializationClicked(MyGUI::Widget* _sender)
{
if (_sender == mSpecialization0)
mSpecializationId = ESM::Class::Combat;
@ -747,7 +747,7 @@ SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowMan
ToolTips::createAttributeToolTip(attribute, attribute->getAttributeId());
}
MyGUI::ButtonPtr cancelButton;
MyGUI::Button* cancelButton;
getWidget(cancelButton, "CancelButton");
cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", ""));
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked);
@ -840,7 +840,7 @@ SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager)
}
}
MyGUI::ButtonPtr cancelButton;
MyGUI::Button* cancelButton;
getWidget(cancelButton, "CancelButton");
cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", ""));
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked);
@ -873,7 +873,7 @@ DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager)
getWidget(mTextEdit, "TextEdit");
MyGUI::ButtonPtr okButton;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked);
okButton->setCaption(mWindowManager.getGameSettingString("sInputMenu1", ""));

View file

@ -1,7 +1,7 @@
#ifndef MWGUI_CLASS_H
#define MWGUI_CLASS_H
#include <MyGUI.h>
#include "widgets.hpp"
#include "window_base.hpp"
@ -35,17 +35,17 @@ namespace MWGui
EventHandle_Int eventButtonSelected;
protected:
void onButtonClicked(MyGUI::WidgetPtr _sender);
void onButtonClicked(MyGUI::Widget* _sender);
private:
void fitToText(MyGUI::TextBox* widget);
void layoutVertically(MyGUI::WidgetPtr widget, int margin);
void layoutVertically(MyGUI::Widget* widget, int margin);
int mCurrentButton;
MyGUI::WidgetPtr mTextBox;
MyGUI::Widget* mTextBox;
MyGUI::TextBox* mText;
MyGUI::WidgetPtr mButtonBar;
std::vector<MyGUI::ButtonPtr> mButtons;
MyGUI::Widget* mButtonBar;
std::vector<MyGUI::Button*> mButtons;
};
// Lets the player choose between 3 ways of creating a class
@ -235,7 +235,7 @@ namespace MWGui
void onOkClicked(MyGUI::Widget* _sender);
private:
MyGUI::EditPtr mTextEdit;
MyGUI::EditBox* mTextEdit;
};
class CreateClassDialog : public WindowModal
@ -265,7 +265,7 @@ namespace MWGui
void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender);
void onSpecializationClicked(MyGUI::WidgetPtr _sender);
void onSpecializationClicked(MyGUI::Widget* _sender);
void onSpecializationSelected();
void onAttributeClicked(Widgets::MWAttributePtr _sender);
void onAttributeSelected();
@ -280,7 +280,7 @@ namespace MWGui
void update();
private:
MyGUI::EditPtr mEditName;
MyGUI::EditBox* mEditName;
MyGUI::TextBox* mSpecializationName;
Widgets::MWAttributePtr mFavoriteAttribute0, mFavoriteAttribute1;
Widgets::MWSkillPtr mMajorSkill[5];

Some files were not shown because too many files have changed in this diff Show more