Merge branch 'master' into videoplayback

Conflicts:
	apps/openmw/mwscript/docs/vmformat.txt
pull/16/head
scrawl 12 years ago
commit f1b138d0a8

@ -32,6 +32,7 @@ option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE)
option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_ESMTOOL "build ESM inspector" ON)
option(BUILD_LAUNCHER "build Launcher" ON) option(BUILD_LAUNCHER "build Launcher" ON)
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF)
@ -103,7 +104,6 @@ set(OENGINE_OGRE
${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/renderer.cpp
${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/fader.cpp
${LIBDIR}/openengine/ogre/imagerotate.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp
${LIBDIR}/openengine/ogre/atlas.cpp
${LIBDIR}/openengine/ogre/selectionbuffer.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp
) )
set(OENGINE_GUI set(OENGINE_GUI
@ -385,7 +385,7 @@ if(DPKG_PROGRAM)
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-filesystem1.46.1 (>= 1.46.1), libboost-program-options1.46.1 (>= 1.46.1), libboost-system1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)")
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
@ -491,6 +491,10 @@ if (BUILD_MWINIIMPORTER)
add_subdirectory( apps/mwiniimporter ) add_subdirectory( apps/mwiniimporter )
endif() endif()
if (BUILD_OPENCS)
add_subdirectory (apps/opencs)
endif()
# UnitTests # UnitTests
if (BUILD_UNITTESTS) if (BUILD_UNITTESTS)
add_subdirectory( apps/openmw_test_suite ) add_subdirectory( apps/openmw_test_suite )
@ -554,7 +558,9 @@ if (WIN32)
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
endif (BUILD_LAUNCHER) endif (BUILD_LAUNCHER)
set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS})
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS}) if (BUILD_ESMTOOL)
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS})
endif (BUILD_ESMTOOL)
endif(MSVC) endif(MSVC)
# Same for MinGW # Same for MinGW

@ -59,7 +59,7 @@ struct Arguments
std::string outname; std::string outname;
std::vector<std::string> types; std::vector<std::string> types;
ESMData data; ESMData data;
ESM::ESMReader reader; ESM::ESMReader reader;
ESM::ESMWriter writer; ESM::ESMWriter writer;
@ -74,7 +74,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
("version,v", "print version information and quit.") ("version,v", "print version information and quit.")
("raw,r", "Show an unformatted list of all records and subrecords.") ("raw,r", "Show an unformatted list of all records and subrecords.")
// The intention is that this option would interact better // The intention is that this option would interact better
// with other modes including clone, dump, and raw. // with other modes including clone, dump, and raw.
("type,t", bpo::value< std::vector<std::string> >(), ("type,t", bpo::value< std::vector<std::string> >(),
"Show only records of this type (four character record code). May " "Show only records of this type (four character record code). May "
"be specified multiple times. Only affects dump mode.") "be specified multiple times. Only affects dump mode.")
@ -165,23 +165,12 @@ bool parseOptions (int argc, char** argv, Arguments &info)
// Font encoding settings // Font encoding settings
info.encoding = variables["encoding"].as<std::string>(); info.encoding = variables["encoding"].as<std::string>();
if (info.encoding == "win1250") if(info.encoding != "win1250" && info.encoding != "win1251" && info.encoding != "win1252")
{
std::cout << "Using Central and Eastern European font encoding." << std::endl;
}
else if (info.encoding == "win1251")
{
std::cout << "Using Cyrillic font encoding." << std::endl;
}
else
{ {
if(info.encoding != "win1252") std::cout << info.encoding << " is not a valid encoding option." << std::endl;
{ info.encoding = "win1252";
std::cout << info.encoding << " is not a valid encoding option." << std::endl;
info.encoding = "win1252";
}
std::cout << "Using default (English) font encoding." << std::endl;
} }
std::cout << ToUTF8::encodingUsingMessage(info.encoding) << std::endl;
return true; return true;
} }
@ -262,7 +251,8 @@ void printRaw(ESM::ESMReader &esm)
int load(Arguments& info) int load(Arguments& info)
{ {
ESM::ESMReader& esm = info.reader; ESM::ESMReader& esm = info.reader;
esm.setEncoding(info.encoding); ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
esm.setEncoder(&encoder);
std::string filename = info.filename; std::string filename = info.filename;
std::cout << "Loading file: " << filename << std::endl; std::cout << "Loading file: " << filename << std::endl;
@ -321,7 +311,7 @@ int load(Arguments& info)
if (info.types.size() > 0) if (info.types.size() > 0)
{ {
std::vector<std::string>::iterator match; std::vector<std::string>::iterator match;
match = std::find(info.types.begin(), info.types.end(), match = std::find(info.types.begin(), info.types.end(),
n.toString()); n.toString());
if (match == info.types.end()) interested = false; if (match == info.types.end()) interested = false;
} }
@ -425,14 +415,15 @@ int clone(Arguments& info)
if (++i % 3 == 0) if (++i % 3 == 0)
std::cout << std::endl; std::cout << std::endl;
} }
if (i % 3 != 0) if (i % 3 != 0)
std::cout << std::endl; std::cout << std::endl;
std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl; std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl;
ESM::ESMWriter& esm = info.writer; ESM::ESMWriter& esm = info.writer;
esm.setEncoding(info.encoding); ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
esm.setEncoder(&encoder);
esm.setAuthor(info.data.author); esm.setAuthor(info.data.author);
esm.setDescription(info.data.description); esm.setDescription(info.data.description);
esm.setVersion(info.data.version); esm.setVersion(info.data.version);
@ -450,7 +441,7 @@ int clone(Arguments& info)
for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it) for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it)
{ {
EsmTool::RecordBase *record = *it; EsmTool::RecordBase *record = *it;
name.val = record->getType().val; name.val = record->getType().val;
esm.startRecord(name.toString(), record->getFlags()); esm.startRecord(name.toString(), record->getFlags());
@ -485,7 +476,7 @@ int clone(Arguments& info)
std::cerr << "\r" << perc << "%"; std::cerr << "\r" << perc << "%";
} }
} }
std::cout << "\rDone!" << std::endl; std::cout << "\rDone!" << std::endl;
esm.close(); esm.close();
@ -513,7 +504,7 @@ int comp(Arguments& info)
fileOne.encoding = info.encoding; fileOne.encoding = info.encoding;
fileTwo.encoding = info.encoding; fileTwo.encoding = info.encoding;
fileOne.filename = info.filename; fileOne.filename = info.filename;
fileTwo.filename = info.outname; fileTwo.filename = info.outname;
@ -534,9 +525,9 @@ int comp(Arguments& info)
std::cout << "Not equal, different amount of records." << std::endl; std::cout << "Not equal, different amount of records." << std::endl;
return 1; return 1;
} }
return 0; return 0;
} }

@ -738,7 +738,6 @@ void Record<ESM::GameSetting>::print()
default: default:
std::cout << "unknown type"; std::cout << "unknown type";
} }
std::cout << "\n Dirty: " << mData.mDirty << std::endl;
} }
template<> template<>

@ -272,7 +272,8 @@ void DataFilesModel::addMasters(const QString &path)
foreach (const QString &path, dir.entryList()) { foreach (const QString &path, dir.entryList()) {
try { try {
ESM::ESMReader fileReader; ESM::ESMReader fileReader;
fileReader.setEncoding(mEncoding.toStdString()); ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString()));
fileReader.setEncoder(&encoder);
fileReader.open(dir.absoluteFilePath(path).toStdString()); fileReader.open(dir.absoluteFilePath(path).toStdString());
ESM::ESMReader::MasterList mlist = fileReader.getMasters(); ESM::ESMReader::MasterList mlist = fileReader.getMasters();
@ -335,7 +336,8 @@ void DataFilesModel::addPlugins(const QString &path)
try { try {
ESM::ESMReader fileReader; ESM::ESMReader fileReader;
fileReader.setEncoding(mEncoding.toStdString()); ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString()));
fileReader.setEncoder(&encoder);
fileReader.open(dir.absoluteFilePath(path).toStdString()); fileReader.open(dir.absoluteFilePath(path).toStdString());
ESM::ESMReader::MasterList mlist = fileReader.getMasters(); ESM::ESMReader::MasterList mlist = fileReader.getMasters();

@ -18,8 +18,609 @@ MwIniImporter::MwIniImporter()
{ 0, 0 } { 0, 0 }
}; };
const char *fallback[] = { const char *fallback[] = {
// light
"LightAttenuation:UseConstant",
"LightAttenuation:ConstantValue",
"LightAttenuation:UseLinear",
"LightAttenuation:LinearMethod",
"LightAttenuation:LinearValue",
"LightAttenuation:LinearRadiusMult",
"LightAttenuation:UseQuadratic",
"LightAttenuation:QuadraticMethod",
"LightAttenuation:QuadraticValue",
"LightAttenuation:QuadraticRadiusMult",
"LightAttenuation:OutQuadInLin",
// inventory
"Inventory:DirectionalDiffuseR",
"Inventory:DirectionalDiffuseG",
"Inventory:DirectionalDiffuseB",
"Inventory:DirectionalAmbientR",
"Inventory:DirectionalAmbientG",
"Inventory:DirectionalAmbientB",
"Inventory:DirectionalRotationX",
"Inventory:DirectionalRotationY",
"Inventory:UniformScaling",
// map
"Map:Travel Siltstrider Red",
"Map:Travel Siltstrider Green",
"Map:Travel Siltstrider Blue",
"Map:Travel Boat Red",
"Map:Travel Boat Green",
"Map:Travel Boat Blue",
"Map:Travel Magic Red",
"Map:Travel Magic Green",
"Map:Travel Magic Blue",
"Map:Show Travel Lines",
// water
"Water:Map Alpha",
"Water:World Alpha",
"Water:SurfaceTextureSize",
"Water:SurfaceTileCount",
"Water:SurfaceFPS",
"Water:SurfaceTexture",
"Water:SurfaceFrameCount",
"Water:TileTextureDivisor",
"Water:RippleTexture",
"Water:RippleFrameCount",
"Water:RippleLifetime",
"Water:MaxNumberRipples",
"Water:RippleScale",
"Water:RippleRotSpeed",
"Water:RippleAlphas",
"Water:PSWaterReflectTerrain",
"Water:PSWaterReflectUpdate",
"Water:NearWaterRadius",
"Water:NearWaterPoints",
"Water:NearWaterUnderwaterFreq",
"Water:NearWaterUnderwaterVolume",
"Water:NearWaterIndoorTolerance",
"Water:NearWaterOutdoorTolerance",
"Water:NearWaterIndoorID",
"Water:NearWaterOutdoorID",
"Water:UnderwaterSunriseFog",
"Water:UnderwaterDayFog",
"Water:UnderwaterSunsetFog",
"Water:UnderwaterNightFog",
"Water:UnderwaterIndoorFog",
"Water:UnderwaterColor",
"Water:UnderwaterColorWeight",
// pixelwater
"PixelWater:SurfaceFPS",
"PixelWater:TileCount",
"PixelWater:Resolution",
// fonts
"Fonts:Font 0",
"Fonts:Font 1",
"Fonts:Font 2",
// UI colors
"FontColor:color_normal",
"FontColor:color_normal_over",
"FontColor:color_normal_pressed",
"FontColor:color_active",
"FontColor:color_active_over",
"FontColor:color_active_pressed",
"FontColor:color_disabled",
"FontColor:color_disabled_over",
"FontColor:color_disabled_pressed",
"FontColor:color_link",
"FontColor:color_link_over",
"FontColor:color_link_pressed",
"FontColor:color_journal_link",
"FontColor:color_journal_link_over",
"FontColor:color_journal_link_pressed",
"FontColor:color_journal_topic",
"FontColor:color_journal_topic_over",
"FontColor:color_journal_topic_pressed",
"FontColor:color_answer",
"FontColor:color_answer_over",
"FontColor:color_answer_pressed",
"FontColor:color_header",
"FontColor:color_notify",
"FontColor:color_big_normal",
"FontColor:color_big_normal_over",
"FontColor:color_big_normal_pressed",
"FontColor:color_big_link",
"FontColor:color_big_link_over",
"FontColor:color_big_link_pressed",
"FontColor:color_big_answer",
"FontColor:color_big_answer_over",
"FontColor:color_big_answer_pressed",
"FontColor:color_big_header",
"FontColor:color_big_notify",
"FontColor:color_background",
"FontColor:color_focus",
"FontColor:color_health",
"FontColor:color_magic",
"FontColor:color_fatigue",
"FontColor:color_misc",
"FontColor:color_weapon_fill",
"FontColor:color_magic_fill",
"FontColor:color_positive",
"FontColor:color_negative",
"FontColor:color_count",
// level up messages
"Level Up:Level2",
"Level Up:Level3",
"Level Up:Level4",
"Level Up:Level5",
"Level Up:Level6",
"Level Up:Level7",
"Level Up:Level8",
"Level Up:Level9",
"Level Up:Level10",
"Level Up:Level11",
"Level Up:Level12",
"Level Up:Level13",
"Level Up:Level14",
"Level Up:Level15",
"Level Up:Level16",
"Level Up:Level17",
"Level Up:Level18",
"Level Up:Level19",
"Level Up:Level20",
"Level Up:Default",
// character creation multiple choice test
"Question 1:Question",
"Question 1:AnswerOne",
"Question 1:AnswerTwo",
"Question 1:AnswerThree",
"Question 1:Sound",
"Question 2:Question",
"Question 2:AnswerOne",
"Question 2:AnswerTwo",
"Question 2:AnswerThree",
"Question 2:Sound",
"Question 3:Question",
"Question 3:AnswerOne",
"Question 3:AnswerTwo",
"Question 3:AnswerThree",
"Question 3:Sound",
"Question 4:Question",
"Question 4:AnswerOne",
"Question 4:AnswerTwo",
"Question 4:AnswerThree",
"Question 4:Sound",
"Question 5:Question",
"Question 5:AnswerOne",
"Question 5:AnswerTwo",
"Question 5:AnswerThree",
"Question 5:Sound",
"Question 6:Question",
"Question 6:AnswerOne",
"Question 6:AnswerTwo",
"Question 6:AnswerThree",
"Question 6:Sound",
"Question 7:Question",
"Question 7:AnswerOne",
"Question 7:AnswerTwo",
"Question 7:AnswerThree",
"Question 7:Sound",
"Question 8:Question",
"Question 8:AnswerOne",
"Question 8:AnswerTwo",
"Question 8:AnswerThree",
"Question 8:Sound",
"Question 9:Question",
"Question 9:AnswerOne",
"Question 9:AnswerTwo",
"Question 9:AnswerThree",
"Question 9:Sound",
"Question 10:Question",
"Question 10:AnswerOne",
"Question 10:AnswerTwo",
"Question 10:AnswerThree",
"Question 10:Sound",
// blood textures and models
"Blood:Model 0",
"Blood:Model 1",
"Blood:Model 2",
"Blood:Texture 0",
"Blood:Texture 1",
"Blood:Texture 2",
"Blood:Texture Name 0",
"Blood:Texture Name 1",
"Blood:Texture Name 2",
// movies
"Movies:Company Logo",
"Movies:Morrowind Logo",
"Movies:New Game",
"Movies:Loading",
"Movies:Options Menu",
// weather related values
"Weather Thunderstorm:Thunder Sound ID 0",
"Weather Thunderstorm:Thunder Sound ID 1",
"Weather Thunderstorm:Thunder Sound ID 2",
"Weather Thunderstorm:Thunder Sound ID 3",
"Weather:Sunrise Time", "Weather:Sunrise Time",
"Weather:Sunset Time", "Weather:Sunset Time",
"Weather:Sunrise Duration",
"Weather:Sunset Duration",
"Weather:Hours Between Weather Changes", // AKA weather update time
"Weather Thunderstorm:Thunder Frequency",
"Weather Thunderstorm:Thunder Threshold",
"Weather:EnvReduceColor",
"Weather:LerpCloseColor",
"Weather:BumpFadeColor",
"Weather:AlphaReduce",
"Weather:Minimum Time Between Environmental Sounds",
"Weather:Maximum Time Between Environmental Sounds",
"Weather:Sun Glare Fader Max",
"Weather:Sun Glare Fader Angle Max",
"Weather:Sun Glare Fader Color",
"Weather:Timescale Clouds",
"Weather:Precip Gravity",
"Weather:Rain Ripples",
"Weather:Rain Ripple Radius",
"Weather:Rain Ripples Per Drop",
"Weather:Rain Ripple Scale",
"Weather:Rain Ripple Speed",
"Weather:Fog Depth Change Speed",
"Weather:Sky Pre-Sunrise Time",
"Weather:Sky Post-Sunrise Time",
"Weather:Sky Pre-Sunset Time",
"Weather:Sky Post-Sunset Time",
"Weather:Ambient Pre-Sunrise Time",
"Weather:Ambient Post-Sunrise Time",
"Weather:Ambient Pre-Sunset Time",
"Weather:Ambient Post-Sunset Time",
"Weather:Fog Pre-Sunrise Time",
"Weather:Fog Post-Sunrise Time",
"Weather:Fog Pre-Sunset Time",
"Weather:Fog Post-Sunset Time",
"Weather:Sun Pre-Sunrise Time",
"Weather:Sun Post-Sunrise Time",
"Weather:Sun Pre-Sunset Time",
"Weather:Sun Post-Sunset Time",
"Weather:Stars Post-Sunset Start",
"Weather:Stars Pre-Sunrise Finish",
"Weather:Stars Fading Duration",
"Weather:Snow Ripples",
"Weather:Snow Ripple Radius",
"Weather:Snow Ripples Per Flake",
"Weather:Snow Ripple Scale",
"Weather:Snow Ripple Speed",
"Weather:Snow Gravity Scale",
"Weather:Snow High Kill",
"Weather:Snow Low Kill",
"Weather Clear:Cloud Texture",
"Weather Clear:Clouds Maximum Percent",
"Weather Clear:Transition Delta",
"Weather Clear:Sky Sunrise Color",
"Weather Clear:Sky Day Color",
"Weather Clear:Sky Sunset Color",
"Weather Clear:Sky Night Color",
"Weather Clear:Fog Sunrise Color",
"Weather Clear:Fog Day Color",
"Weather Clear:Fog Sunset Color",
"Weather Clear:Fog Night Color",
"Weather Clear:Ambient Sunrise Color",
"Weather Clear:Ambient Day Color",
"Weather Clear:Ambient Sunset Color",
"Weather Clear:Ambient Night Color",
"Weather Clear:Sun Sunrise Color",
"Weather Clear:Sun Day Color",
"Weather Clear:Sun Sunset Color",
"Weather Clear:Sun Night Color",
"Weather Clear:Sun Disc Sunset Color",
"Weather Clear:Land Fog Day Depth",
"Weather Clear:Land Fog Night Depth",
"Weather Clear:Wind Speed",
"Weather Clear:Cloud Speed",
"Weather Clear:Glare View",
"Weather Clear:Ambient Loop Sound ID",
"Weather Cloudy:Cloud Texture",
"Weather Cloudy:Clouds Maximum Percent",
"Weather Cloudy:Transition Delta",
"Weather Cloudy:Sky Sunrise Color",
"Weather Cloudy:Sky Day Color",
"Weather Cloudy:Sky Sunset Color",
"Weather Cloudy:Sky Night Color",
"Weather Cloudy:Fog Sunrise Color",
"Weather Cloudy:Fog Day Color",
"Weather Cloudy:Fog Sunset Color",
"Weather Cloudy:Fog Night Color",
"Weather Cloudy:Ambient Sunrise Color",
"Weather Cloudy:Ambient Day Color",
"Weather Cloudy:Ambient Sunset Color",
"Weather Cloudy:Ambient Night Color",
"Weather Cloudy:Sun Sunrise Color",
"Weather Cloudy:Sun Day Color",
"Weather Cloudy:Sun Sunset Color",
"Weather Cloudy:Sun Night Color",
"Weather Cloudy:Sun Disc Sunset Color",
"Weather Cloudy:Land Fog Day Depth",
"Weather Cloudy:Land Fog Night Depth",
"Weather Cloudy:Wind Speed",
"Weather Cloudy:Cloud Speed",
"Weather Cloudy:Glare View",
"Weather Cloudy:Ambient Loop Sound ID",
"Weather Foggy:Cloud Texture",
"Weather Foggy:Clouds Maximum Percent",
"Weather Foggy:Transition Delta",
"Weather Foggy:Sky Sunrise Color",
"Weather Foggy:Sky Day Color",
"Weather Foggy:Sky Sunset Color",
"Weather Foggy:Sky Night Color",
"Weather Foggy:Fog Sunrise Color",
"Weather Foggy:Fog Day Color",
"Weather Foggy:Fog Sunset Color",
"Weather Foggy:Fog Night Color",
"Weather Foggy:Ambient Sunrise Color",
"Weather Foggy:Ambient Day Color",
"Weather Foggy:Ambient Sunset Color",
"Weather Foggy:Ambient Night Color",
"Weather Foggy:Sun Sunrise Color",
"Weather Foggy:Sun Day Color",
"Weather Foggy:Sun Sunset Color",
"Weather Foggy:Sun Night Color",
"Weather Foggy:Sun Disc Sunset Color",
"Weather Foggy:Land Fog Day Depth",
"Weather Foggy:Land Fog Night Depth",
"Weather Foggy:Wind Speed",
"Weather Foggy:Cloud Speed",
"Weather Foggy:Glare View",
"Weather Foggy:Ambient Loop Sound ID",
"Weather Thunderstorm:Cloud Texture",
"Weather Thunderstorm:Clouds Maximum Percent",
"Weather Thunderstorm:Transition Delta",
"Weather Thunderstorm:Sky Sunrise Color",
"Weather Thunderstorm:Sky Day Color",
"Weather Thunderstorm:Sky Sunset Color",
"Weather Thunderstorm:Sky Night Color",
"Weather Thunderstorm:Fog Sunrise Color",
"Weather Thunderstorm:Fog Day Color",
"Weather Thunderstorm:Fog Sunset Color",
"Weather Thunderstorm:Fog Night Color",
"Weather Thunderstorm:Ambient Sunrise Color",
"Weather Thunderstorm:Ambient Day Color",
"Weather Thunderstorm:Ambient Sunset Color",
"Weather Thunderstorm:Ambient Night Color",
"Weather Thunderstorm:Sun Sunrise Color",
"Weather Thunderstorm:Sun Day Color",
"Weather Thunderstorm:Sun Sunset Color",
"Weather Thunderstorm:Sun Night Color",
"Weather Thunderstorm:Sun Disc Sunset Color",
"Weather Thunderstorm:Land Fog Day Depth",
"Weather Thunderstorm:Land Fog Night Depth",
"Weather Thunderstorm:Wind Speed",
"Weather Thunderstorm:Cloud Speed",
"Weather Thunderstorm:Glare View",
"Weather Thunderstorm:Rain Loop Sound ID",
"Weather Thunderstorm:Using Precip",
"Weather Thunderstorm:Rain Diameter",
"Weather Thunderstorm:Rain Height Min",
"Weather Thunderstorm:Rain Height Max",
"Weather Thunderstorm:Rain Threshold",
"Weather Thunderstorm:Max Raindrops",
"Weather Thunderstorm:Rain Entrance Speed",
"Weather Thunderstorm:Ambient Loop Sound ID",
"Weather Thunderstorm:Flash Decrement",
"Weather Rain:Cloud Texture",
"Weather Rain:Clouds Maximum Percent",
"Weather Rain:Transition Delta",
"Weather Rain:Sky Sunrise Color",
"Weather Rain:Sky Day Color",
"Weather Rain:Sky Sunset Color",
"Weather Rain:Sky Night Color",
"Weather Rain:Fog Sunrise Color",
"Weather Rain:Fog Day Color",
"Weather Rain:Fog Sunset Color",
"Weather Rain:Fog Night Color",
"Weather Rain:Ambient Sunrise Color",
"Weather Rain:Ambient Day Color",
"Weather Rain:Ambient Sunset Color",
"Weather Rain:Ambient Night Color",
"Weather Rain:Sun Sunrise Color",
"Weather Rain:Sun Day Color",
"Weather Rain:Sun Sunset Color",
"Weather Rain:Sun Night Color",
"Weather Rain:Sun Disc Sunset Color",
"Weather Rain:Land Fog Day Depth",
"Weather Rain:Land Fog Night Depth",
"Weather Rain:Wind Speed",
"Weather Rain:Cloud Speed",
"Weather Rain:Glare View",
"Weather Rain:Rain Loop Sound ID",
"Weather Rain:Using Precip",
"Weather Rain:Rain Diameter",
"Weather Rain:Rain Height Min",
"Weather Rain:Rain Height Max",
"Weather Rain:Rain Threshold",
"Weather Rain:Rain Entrance Speed",
"Weather Rain:Ambient Loop Sound ID",
"Weather Rain:Max Raindrops",
"Weather Overcast:Cloud Texture",
"Weather Overcast:Clouds Maximum Percent",
"Weather Overcast:Transition Delta",
"Weather Overcast:Sky Sunrise Color",
"Weather Overcast:Sky Day Color",
"Weather Overcast:Sky Sunset Color",
"Weather Overcast:Sky Night Color",
"Weather Overcast:Fog Sunrise Color",
"Weather Overcast:Fog Day Color",
"Weather Overcast:Fog Sunset Color",
"Weather Overcast:Fog Night Color",
"Weather Overcast:Ambient Sunrise Color",
"Weather Overcast:Ambient Day Color",
"Weather Overcast:Ambient Sunset Color",
"Weather Overcast:Ambient Night Color",
"Weather Overcast:Sun Sunrise Color",
"Weather Overcast:Sun Day Color",
"Weather Overcast:Sun Sunset Color",
"Weather Overcast:Sun Night Color",
"Weather Overcast:Sun Disc Sunset Color",
"Weather Overcast:Land Fog Day Depth",
"Weather Overcast:Land Fog Night Depth",
"Weather Overcast:Wind Speed",
"Weather Overcast:Cloud Speed",
"Weather Overcast:Glare View",
"Weather Overcast:Ambient Loop Sound ID",
"Weather Ashstorm:Cloud Texture",
"Weather Ashstorm:Clouds Maximum Percent",
"Weather Ashstorm:Transition Delta",
"Weather Ashstorm:Sky Sunrise Color",
"Weather Ashstorm:Sky Day Color",
"Weather Ashstorm:Sky Sunset Color",
"Weather Ashstorm:Sky Night Color",
"Weather Ashstorm:Fog Sunrise Color",
"Weather Ashstorm:Fog Day Color",
"Weather Ashstorm:Fog Sunset Color",
"Weather Ashstorm:Fog Night Color",
"Weather Ashstorm:Ambient Sunrise Color",
"Weather Ashstorm:Ambient Day Color",
"Weather Ashstorm:Ambient Sunset Color",
"Weather Ashstorm:Ambient Night Color",
"Weather Ashstorm:Sun Sunrise Color",
"Weather Ashstorm:Sun Day Color",
"Weather Ashstorm:Sun Sunset Color",
"Weather Ashstorm:Sun Night Color",
"Weather Ashstorm:Sun Disc Sunset Color",
"Weather Ashstorm:Land Fog Day Depth",
"Weather Ashstorm:Land Fog Night Depth",
"Weather Ashstorm:Wind Speed",
"Weather Ashstorm:Cloud Speed",
"Weather Ashstorm:Glare View",
"Weather Ashstorm:Ambient Loop Sound ID",
"Weather Ashstorm:Storm Threshold",
"Weather Blight:Cloud Texture",
"Weather Blight:Clouds Maximum Percent",
"Weather Blight:Transition Delta",
"Weather Blight:Sky Sunrise Color",
"Weather Blight:Sky Day Color",
"Weather Blight:Sky Sunset Color",
"Weather Blight:Sky Night Color",
"Weather Blight:Fog Sunrise Color",
"Weather Blight:Fog Day Color",
"Weather Blight:Fog Sunset Color",
"Weather Blight:Fog Night Color",
"Weather Blight:Ambient Sunrise Color",
"Weather Blight:Ambient Day Color",
"Weather Blight:Ambient Sunset Color",
"Weather Blight:Ambient Night Color",
"Weather Blight:Sun Sunrise Color",
"Weather Blight:Sun Day Color",
"Weather Blight:Sun Sunset Color",
"Weather Blight:Sun Night Color",
"Weather Blight:Sun Disc Sunset Color",
"Weather Blight:Land Fog Day Depth",
"Weather Blight:Land Fog Night Depth",
"Weather Blight:Wind Speed",
"Weather Blight:Cloud Speed",
"Weather Blight:Glare View",
"Weather Blight:Ambient Loop Sound ID",
"Weather Blight:Storm Threshold",
"Weather Blight:Disease Chance",
// for Bloodmoon
"Weather Snow:Cloud Texture",
"Weather Snow:Clouds Maximum Percent",
"Weather Snow:Transition Delta",
"Weather Snow:Sky Sunrise Color",
"Weather Snow:Sky Day Color",
"Weather Snow:Sky Sunset Color",
"Weather Snow:Sky Night Color",
"Weather Snow:Fog Sunrise Color",
"Weather Snow:Fog Day Color",
"Weather Snow:Fog Sunset Color",
"Weather Snow:Fog Night Color",
"Weather Snow:Ambient Sunrise Color",
"Weather Snow:Ambient Day Color",
"Weather Snow:Ambient Sunset Color",
"Weather Snow:Ambient Night Color",
"Weather Snow:Sun Sunrise Color",
"Weather Snow:Sun Day Color",
"Weather Snow:Sun Sunset Color",
"Weather Snow:Sun Night Color",
"Weather Snow:Sun Disc Sunset Color",
"Weather Snow:Land Fog Day Depth",
"Weather Snow:Land Fog Night Depth",
"Weather Snow:Wind Speed",
"Weather Snow:Cloud Speed",
"Weather Snow:Glare View",
"Weather Snow:Snow Diameter",
"Weather Snow:Snow Height Min",
"Weather Snow:Snow Height Max",
"Weather Snow:Snow Entrance Speed",
"Weather Snow:Max Snowflakes",
"Weather Snow:Ambient Loop Sound ID",
"Weather Snow:Snow Threshold",
// for Bloodmoon
"Weather Blizzard:Cloud Texture",
"Weather Blizzard:Clouds Maximum Percent",
"Weather Blizzard:Transition Delta",
"Weather Blizzard:Sky Sunrise Color",
"Weather Blizzard:Sky Day Color",
"Weather Blizzard:Sky Sunset Color",
"Weather Blizzard:Sky Night Color",
"Weather Blizzard:Fog Sunrise Color",
"Weather Blizzard:Fog Day Color",
"Weather Blizzard:Fog Sunset Color",
"Weather Blizzard:Fog Night Color",
"Weather Blizzard:Ambient Sunrise Color",
"Weather Blizzard:Ambient Day Color",
"Weather Blizzard:Ambient Sunset Color",
"Weather Blizzard:Ambient Night Color",
"Weather Blizzard:Sun Sunrise Color",
"Weather Blizzard:Sun Day Color",
"Weather Blizzard:Sun Sunset Color",
"Weather Blizzard:Sun Night Color",
"Weather Blizzard:Sun Disc Sunset Color",
"Weather Blizzard:Land Fog Day Depth",
"Weather Blizzard:Land Fog Night Depth",
"Weather Blizzard:Wind Speed",
"Weather Blizzard:Cloud Speed",
"Weather Blizzard:Glare View",
"Weather Blizzard:Ambient Loop Sound ID",
"Weather Blizzard:Storm Threshold",
// moons
"Moons:Secunda Size",
"Moons:Secunda Axis Offset",
"Moons:Secunda Speed",
"Moons:Secunda Daily Increment",
"Moons:Secunda Moon Shadow Early Fade Angle",
"Moons:Secunda Fade Start Angle",
"Moons:Secunda Fade End Angle",
"Moons:Secunda Fade In Start",
"Moons:Secunda Fade In Finish",
"Moons:Secunda Fade Out Start",
"Moons:Secunda Fade Out Finish",
"Moons:Masser Size",
"Moons:Masser Axis Offset",
"Moons:Masser Speed",
"Moons:Masser Daily Increment",
"Moons:Masser Moon Shadow Early Fade Angle",
"Moons:Masser Fade Start Angle",
"Moons:Masser Fade End Angle",
"Moons:Masser Fade In Start",
"Moons:Masser Fade In Finish",
"Moons:Masser Fade Out Start",
"Moons:Masser Fade Out Finish",
"Moons:Script Color",
0 0
}; };
@ -48,14 +649,26 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) {
std::string section(""); std::string section("");
MwIniImporter::multistrmap map; MwIniImporter::multistrmap map;
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str()); boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
ToUTF8::Utf8Encoder encoder(mEncoding);
std::string line; std::string line;
while (std::getline(file, line)) { while (std::getline(file, line)) {
line = encoder.getUtf8(line);
// unify Unix-style and Windows file ending
if (!(line.empty()) && (line[line.length()-1]) == '\r') {
line = line.substr(0, line.length()-1);
}
if(line[0] == '[') { if(line[0] == '[') {
if(line.length() > 2) { int pos = line.find(']');
section = line.substr(1, line.length()-3); if(pos < 2) {
std::cout << "Warning: ini file wrongly formatted (" << line << "). Line ignored." << std::endl;
continue;
} }
section = line.substr(1, line.find(']')-1);
continue; continue;
} }
@ -147,7 +760,7 @@ void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) {
std::string value(*it); std::string value(*it);
std::replace( value.begin(), value.end(), ' ', '_' ); std::replace( value.begin(), value.end(), ' ', '_' );
std::replace( value.begin(), value.end(), ':', '_' ); std::replace( value.begin(), value.end(), ':', '_' );
value.append(",").append(vc->substr(0,vc->length()-1)); value.append(",").append(vc->substr(0,vc->length()));
insertMultistrmap(cfg, "fallback", value); insertMultistrmap(cfg, "fallback", value);
} }
} }
@ -216,3 +829,8 @@ void MwIniImporter::writeToFile(boost::iostreams::stream<boost::iostreams::file_
} }
} }
} }
void MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding)
{
mEncoding = encoding;
}

@ -8,12 +8,15 @@
#include <vector> #include <vector>
#include <exception> #include <exception>
#include <components/to_utf8/to_utf8.hpp>
class MwIniImporter { class MwIniImporter {
public: public:
typedef std::map<std::string, std::string> strmap; typedef std::map<std::string, std::string> strmap;
typedef std::map<std::string, std::vector<std::string> > multistrmap; typedef std::map<std::string, std::vector<std::string> > multistrmap;
MwIniImporter(); MwIniImporter();
void setInputEncoding(const ToUTF8::FromType& encoding);
void setVerbose(bool verbose); void setVerbose(bool verbose);
multistrmap loadIniFile(std::string filename); multistrmap loadIniFile(std::string filename);
multistrmap loadCfgFile(std::string filename); multistrmap loadCfgFile(std::string filename);
@ -25,9 +28,11 @@ class MwIniImporter {
private: private:
void insertMultistrmap(multistrmap &cfg, std::string key, std::string value); void insertMultistrmap(multistrmap &cfg, std::string key, std::string value);
std::string numberToString(int n); std::string numberToString(int n);
std::string toUTF8(const std::string &str);
bool mVerbose; bool mVerbose;
strmap mMergeMap; strmap mMergeMap;
std::vector<std::string> mMergeFallback; std::vector<std::string> mMergeFallback;
ToUTF8::FromType mEncoding;
}; };

@ -18,6 +18,11 @@ int main(int argc, char *argv[]) {
("cfg,c", bpo::value<std::string>(), "openmw.cfg file") ("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file") ("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
("game-files,g", "import esm and esp files") ("game-files,g", "import esm and esp files")
("encoding,e", bpo::value<std::string>()-> default_value("win1252"),
"Character encoding used in OpenMW game messages:\n"
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
"\n\twin1252 - Western European (Latin) alphabet, used by default")
; ;
p_desc.add("ini", 1).add("cfg", 1); p_desc.add("ini", 1).add("cfg", 1);
@ -57,6 +62,10 @@ int main(int argc, char *argv[]) {
MwIniImporter importer; MwIniImporter importer;
importer.setVerbose(vm.count("verbose")); importer.setVerbose(vm.count("verbose"));
// Font encoding settings
std::string encoding(vm["encoding"].as<std::string>());
importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile); MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile); MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);

@ -0,0 +1,76 @@
set (OPENCS_SRC
main.cpp editor.cpp
model/doc/documentmanager.cpp model/doc/document.cpp
model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp
model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp
model/world/columnbase.cpp
model/tools/tools.cpp model/tools/operation.cpp model/tools/stage.cpp model/tools/verifier.cpp
model/tools/mandatoryid.cpp model/tools/reportmodel.cpp
view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp view/doc/subviewfactory.cpp
view/doc/subview.cpp
view/world/table.cpp view/world/tablesubview.cpp view/world/subviews.cpp view/world/util.cpp
view/world/dialoguesubview.cpp
view/tools/reportsubview.cpp view/tools/subviews.cpp
)
set (OPENCS_HDR
editor.hpp
model/doc/documentmanager.hpp model/doc/document.hpp model/doc/state.hpp
model/world/universalid.hpp model/world/record.hpp model/world/idcollection.hpp model/world/data.hpp
model/world/idtable.hpp model/world/columns.hpp model/world/idtableproxymodel.hpp
model/world/commands.hpp model/world/columnbase.hpp
model/tools/tools.hpp model/tools/operation.hpp model/tools/stage.hpp model/tools/verifier.hpp
model/tools/mandatoryid.hpp model/tools/reportmodel.hpp
view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp view/doc/subviewfactory.hpp
view/doc/subview.hpp view/doc/subviewfactoryimp.hpp
view/world/table.hpp view/world/tablesubview.hpp view/world/subviews.hpp view/world/util.hpp
view/world/dialoguesubview.hpp
view/tools/reportsubview.hpp view/tools/subviews.hpp
)
set (OPENCS_US
)
set (OPENCS_RES
)
source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR})
if(WIN32)
set(QT_USE_QTMAIN TRUE)
endif(WIN32)
find_package(Qt4 COMPONENTS QtCore QtGui QtXml QtXmlPatterns REQUIRED)
include(${QT_USE_FILE})
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR})
qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(opencs
${OPENCS_SRC}
${OPENCS_UI_HDR}
${OPENCS_MOC_SRC}
${OPENCS_RES_SRC}
)
target_link_libraries(opencs
${Boost_LIBRARIES}
${QT_LIBRARIES}
components
)

@ -0,0 +1,49 @@
#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)
{
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ()));
}
void CS::Editor::createDocument()
{
std::ostringstream stream;
stream << "NewDocument" << (++mNewDocumentIndex);
CSMDoc::Document *document = mDocumentManager.addDocument (stream.str());
static const char *sGlobals[] =
{
"Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0
};
for (int i=0; sGlobals[i]; ++i)
{
ESM::Global record;
record.mId = sGlobals[i];
record.mValue = i==0 ? 1 : 0;
record.mType = ESM::VT_Float;
document->getData().getGlobals().add (record);
}
document->getData().merge(); /// \todo remove once proper ESX loading is implemented
mViewManager.addView (document);
}
int CS::Editor::run()
{
/// \todo Instead of creating an empty document, open a small welcome dialogue window with buttons for new/load/recent projects
createDocument();
return QApplication::exec();
}

@ -0,0 +1,37 @@
#ifndef CS_EDITOR_H
#define CS_EDITOR_H
#include <QObject>
#include "model/doc/documentmanager.hpp"
#include "view/doc/viewmanager.hpp"
namespace CS
{
class Editor : public QObject
{
Q_OBJECT
int mNewDocumentIndex; ///< \todo remove when the proper new document dialogue is implemented.
CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager;
// not implemented
Editor (const Editor&);
Editor& operator= (const Editor&);
public:
Editor();
int run();
///< \return error status
public slots:
void createDocument();
};
}
#endif

@ -0,0 +1,39 @@
#include "editor.hpp"
#include <exception>
#include <iostream>
#include <QtGui/QApplication>
class Application : public QApplication
{
private:
bool notify (QObject *receiver, QEvent *event)
{
try
{
return QApplication::notify (receiver, event);
}
catch (const std::exception& exception)
{
std::cerr << "An exception has been caught: " << exception.what() << std::endl;
}
return false;
}
public:
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
};
int main(int argc, char *argv[])
{
Application mApplication (argc, argv);
CS::Editor editor;
return editor.run();
}

@ -0,0 +1,114 @@
#include "document.hpp"
CSMDoc::Document::Document (const std::string& name)
: mTools (mData)
{
mName = name; ///< \todo replace with ESX list
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int)));
// dummy implementation -> remove when proper save is implemented.
mSaveCount = 0;
connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving()));
}
QUndoStack& CSMDoc::Document::getUndoStack()
{
return mUndoStack;
}
int CSMDoc::Document::getState() const
{
int state = 0;
if (!mUndoStack.isClean())
state |= State_Modified;
if (mSaveCount)
state |= State_Locked | State_Saving | State_Operation;
if (int operations = mTools.getRunningOperations())
state |= State_Locked | State_Operation | operations;
return state;
}
const std::string& CSMDoc::Document::getName() const
{
return mName;
}
void CSMDoc::Document::save()
{
mSaveCount = 1;
mSaveTimer.start (500);
emit stateChanged (getState(), this);
emit progress (1, 16, State_Saving, 1, this);
}
CSMWorld::UniversalId CSMDoc::Document::verify()
{
CSMWorld::UniversalId id = mTools.runVerifier();
emit stateChanged (getState(), this);
return id;
}
void CSMDoc::Document::abortOperation (int type)
{
mTools.abortOperation (type);
if (type==State_Saving)
{
mSaveTimer.stop();
emit stateChanged (getState(), this);
}
}
void CSMDoc::Document::modificationStateChanged (bool clean)
{
emit stateChanged (getState(), this);
}
void CSMDoc::Document::operationDone (int type)
{
emit stateChanged (getState(), this);
}
void CSMDoc::Document::saving()
{
++mSaveCount;
emit progress (mSaveCount, 16, State_Saving, 1, this);
if (mSaveCount>15)
{
mSaveCount = 0;
mSaveTimer.stop();
mUndoStack.setClean();
emit stateChanged (getState(), this);
}
}
const CSMWorld::Data& CSMDoc::Document::getData() const
{
return mData;
}
CSMWorld::Data& CSMDoc::Document::getData()
{
return mData;
}
CSMTools::ReportModel *CSMDoc::Document::getReport (const CSMWorld::UniversalId& id)
{
return mTools.getReport (id);
}
void CSMDoc::Document::progress (int current, int max, int type)
{
emit progress (current, max, type, 1, this);
}

@ -0,0 +1,87 @@
#ifndef CSM_DOC_DOCUMENT_H
#define CSM_DOC_DOCUMENT_H
#include <string>
#include <QUndoStack>
#include <QObject>
#include <QTimer>
#include "../world/data.hpp"
#include "../tools/tools.hpp"
#include "state.hpp"
class QAbstractItemModel;
namespace CSMDoc
{
class Document : public QObject
{
Q_OBJECT
private:
std::string mName; ///< \todo replace name with ESX list
CSMWorld::Data mData;
CSMTools::Tools mTools;
// It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is
// using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
QUndoStack mUndoStack;
int mSaveCount; ///< dummy implementation -> remove when proper save is implemented.
QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented.
// not implemented
Document (const Document&);
Document& operator= (const Document&);
public:
Document (const std::string& name);
///< \todo replace name with ESX list
QUndoStack& getUndoStack();
int getState() const;
const std::string& getName() const;
///< \todo replace with ESX list
void save();
CSMWorld::UniversalId verify();
void abortOperation (int type);
const CSMWorld::Data& getData() const;
CSMWorld::Data& getData();
CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id);
///< The ownership of the returned report is not transferred.
signals:
void stateChanged (int state, CSMDoc::Document *document);
void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
private slots:
void modificationStateChanged (bool clean);
void operationDone (int type);
void saving();
///< dummy implementation -> remove when proper save is implemented.
public slots:
void progress (int current, int max, int type);
};
}
#endif

@ -0,0 +1,37 @@
#include "documentmanager.hpp"
#include <algorithm>
#include <stdexcept>
#include "document.hpp"
CSMDoc::DocumentManager::DocumentManager() {}
CSMDoc::DocumentManager::~DocumentManager()
{
for (std::vector<Document *>::iterator iter (mDocuments.begin()); iter!=mDocuments.end(); ++iter)
delete *iter;
}
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::string& name)
{
Document *document = new Document (name);
mDocuments.push_back (document);
return document;
}
bool CSMDoc::DocumentManager::removeDocument (Document *document)
{
std::vector<Document *>::iterator iter = std::find (mDocuments.begin(), mDocuments.end(), document);
if (iter==mDocuments.end())
throw std::runtime_error ("removing invalid document");
mDocuments.erase (iter);
delete document;
return mDocuments.empty();
}

@ -0,0 +1,32 @@
#ifndef CSM_DOC_DOCUMENTMGR_H
#define CSM_DOC_DOCUMENTMGR_H
#include <vector>
#include <string>
namespace CSMDoc
{
class Document;
class DocumentManager
{
std::vector<Document *> mDocuments;
DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&);
public:
DocumentManager();
~DocumentManager();
Document *addDocument (const std::string& name);
///< The ownership of the returned document is not transferred to the caller.
bool removeDocument (Document *document);
///< \return last document removed?
};
}
#endif

@ -0,0 +1,19 @@
#ifndef CSM_DOC_STATE_H
#define CSM_DOC_STATE_H
namespace CSMDoc
{
enum State
{
State_Modified = 1,
State_Locked = 2,
State_Operation = 4,
State_Saving = 8,
State_Verifying = 16,
State_Compiling = 32, // not implemented yet
State_Searching = 64 // not implemented yet
};
}
#endif

@ -0,0 +1,21 @@
#include "mandatoryid.hpp"
#include "../world/idcollection.hpp"
CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection,
const CSMWorld::UniversalId& collectionId, const std::vector<std::string>& ids)
: mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids)
{}
int CSMTools::MandatoryIdStage::setup()
{
return mIds.size();
}
void CSMTools::MandatoryIdStage::perform (int stage, std::vector<std::string>& messages)
{
if (mIdCollection.searchId (mIds.at (stage))==-1 ||
mIdCollection.getRecord (mIds.at (stage)).isDeleted())
messages.push_back (mCollectionId.toString() + "|Missing mandatory record: " + mIds.at (stage));
}

@ -0,0 +1,38 @@
#ifndef CSM_TOOLS_MANDATORYID_H
#define CSM_TOOLS_MANDATORYID_H
#include <string>
#include <vector>
#include "../world/universalid.hpp"
#include "stage.hpp"
namespace CSMWorld
{
class IdCollectionBase;
}
namespace CSMTools
{
/// \brief Verify stage: make sure that records with specific IDs exist.
class MandatoryIdStage : public Stage
{
const CSMWorld::IdCollectionBase& mIdCollection;
CSMWorld::UniversalId mCollectionId;
std::vector<std::string> mIds;
public:
MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection, const CSMWorld::UniversalId& collectionId,
const std::vector<std::string>& ids);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

@ -0,0 +1,84 @@
#include "operation.hpp"
#include <string>
#include <vector>
#include <QTimer>
#include "../doc/state.hpp"
#include "stage.hpp"
void CSMTools::Operation::prepareStages()
{
mCurrentStage = mStages.begin();
mCurrentStep = 0;
mCurrentStepTotal = 0;
mTotalSteps = 0;
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
{
iter->second = iter->first->setup();
mTotalSteps += iter->second;
}
}
CSMTools::Operation::Operation (int type) : mType (type) {}
CSMTools::Operation::~Operation()
{
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
delete iter->first;
}
void CSMTools::Operation::run()
{
prepareStages();
QTimer timer;
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify()));
timer.start (0);
exec();
}
void CSMTools::Operation::appendStage (Stage *stage)
{
mStages.push_back (std::make_pair (stage, 0));
}
void CSMTools::Operation::abort()
{
exit();
}
void CSMTools::Operation::verify()
{
std::vector<std::string> messages;
while (mCurrentStage!=mStages.end())
{
if (mCurrentStep>=mCurrentStage->second)
{
mCurrentStep = 0;
++mCurrentStage;
}
else
{
mCurrentStage->first->perform (mCurrentStep++, messages);
++mCurrentStepTotal;
break;
}
}
emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType);
for (std::vector<std::string>::const_iterator iter (messages.begin()); iter!=messages.end(); ++iter)
emit reportMessage (iter->c_str(), mType);
if (mCurrentStage==mStages.end())
exit();
}

@ -0,0 +1,54 @@
#ifndef CSM_TOOLS_OPERATION_H
#define CSM_TOOLS_OPERATION_H
#include <vector>
#include <QThread>
namespace CSMTools
{
class Stage;
class Operation : public QThread
{
Q_OBJECT
int mType;
std::vector<std::pair<Stage *, int> > mStages; // stage, number of steps
std::vector<std::pair<Stage *, int> >::iterator mCurrentStage;
int mCurrentStep;
int mCurrentStepTotal;
int mTotalSteps;
void prepareStages();
public:
Operation (int type);
virtual ~Operation();
virtual void run();
void appendStage (Stage *stage);
///< The ownership of \a stage is transferred to *this.
///
/// \attention Do no call this function while this Operation is running.
signals:
void progress (int current, int max, int type);
void reportMessage (const QString& message, int type);
public slots:
void abort();
private slots:
void verify();
};
}
#endif

@ -0,0 +1,71 @@
#include "reportmodel.hpp"
#include <stdexcept>
int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return mRows.size();
}
int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return 2;
}
QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
{
if (role!=Qt::DisplayRole)
return QVariant();
if (index.column()==0)
return static_cast<int> (mRows.at (index.row()).first.getType());
else
return mRows.at (index.row()).second.c_str();
}
QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orientation, int role) const
{
if (role!=Qt::DisplayRole)
return QVariant();
if (orientation==Qt::Vertical)
return QVariant();
return tr (section==0 ? "Type" : "Description");
}
bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& parent)
{
if (parent.isValid())
return false;
mRows.erase (mRows.begin()+row, mRows.begin()+row+count);
return true;
}
void CSMTools::ReportModel::add (const std::string& row)
{
std::string::size_type index = row.find ('|');
if (index==std::string::npos)
throw std::logic_error ("invalid report message");
beginInsertRows (QModelIndex(), mRows.size(), mRows.size());
mRows.push_back (std::make_pair (row.substr (0, index), row.substr (index+1)));
endInsertRows();
}
const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const
{
return mRows.at (row).first;
}

@ -0,0 +1,37 @@
#ifndef CSM_TOOLS_REPORTMODEL_H
#define CSM_TOOLS_REPORTMODEL_H
#include <vector>
#include <string>
#include <QAbstractTableModel>
#include "../world/universalid.hpp"
namespace CSMTools
{
class ReportModel : public QAbstractTableModel
{
Q_OBJECT
std::vector<std::pair<CSMWorld::UniversalId, std::string> > mRows;
public:
virtual int rowCount (const QModelIndex & parent = QModelIndex()) const;
virtual int columnCount (const QModelIndex & parent = QModelIndex()) const;
virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const;
virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex());
void add (const std::string& row);
const CSMWorld::UniversalId& getUniversalId (int row) const;
};
}
#endif

@ -0,0 +1,4 @@
#include "stage.hpp"
CSMTools::Stage::~Stage() {}

@ -0,0 +1,24 @@
#ifndef CSM_TOOLS_STAGE_H
#define CSM_TOOLS_STAGE_H
#include <vector>
#include <string>
namespace CSMTools
{
class Stage
{
public:
virtual ~Stage();
virtual int setup() = 0;
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages) = 0;
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

@ -0,0 +1,123 @@
#include "tools.hpp"
#include <QThreadPool>
#include "verifier.hpp"
#include "../doc/state.hpp"
#include "../world/data.hpp"
#include "../world/universalid.hpp"
#include "reportmodel.hpp"
#include "mandatoryid.hpp"
CSMTools::Operation *CSMTools::Tools::get (int type)
{
switch (type)
{
case CSMDoc::State_Verifying: return mVerifier;
}
return 0;
}
const CSMTools::Operation *CSMTools::Tools::get (int type) const
{
return const_cast<Tools *> (this)->get (type);
}
CSMTools::Verifier *CSMTools::Tools::getVerifier()
{
if (!mVerifier)
{
mVerifier = new Verifier;
connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone()));
connect (mVerifier, SIGNAL (reportMessage (const QString&, int)),
this, SLOT (verifierMessage (const QString&, int)));
std::vector<std::string> mandatoryIds; // I want C++11, damn it!
mandatoryIds.push_back ("Day");
mandatoryIds.push_back ("DaysPassed");
mandatoryIds.push_back ("GameHour");
mandatoryIds.push_back ("Month");
mandatoryIds.push_back ("PCRace");
mandatoryIds.push_back ("PCVampire");
mandatoryIds.push_back ("PCWerewolf");
mandatoryIds.push_back ("PCYear");
mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(),
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds));
}
return mVerifier;
}
CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0), mNextReportNumber (0)
{
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
delete iter->second;
}
CSMTools::Tools::~Tools()
{
delete mVerifier;
}
CSMWorld::UniversalId CSMTools::Tools::runVerifier()
{
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
mActiveReports[CSMDoc::State_Verifying] = mNextReportNumber-1;
getVerifier()->start();
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber-1);
}
void CSMTools::Tools::abortOperation (int type)
{
if (Operation *operation = get (type))
operation->abort();
}
int CSMTools::Tools::getRunningOperations() const
{
static const int sOperations[] =
{
CSMDoc::State_Verifying,
-1
};
int result = 0;
for (int i=0; sOperations[i]!=-1; ++i)
if (const Operation *operation = get (sOperations[i]))
if (operation->isRunning())
result |= sOperations[i];
return result;
}
CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id)
{
if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults)
throw std::logic_error ("invalid request for report model: " + id.toString());
return mReports.at (id.getIndex());
}
void CSMTools::Tools::verifierDone()
{
emit done (CSMDoc::State_Verifying);
}
void CSMTools::Tools::verifierMessage (const QString& message, int type)
{
std::map<int, int>::iterator iter = mActiveReports.find (type);
if (iter!=mActiveReports.end())
mReports[iter->second]->add (message.toStdString());
}

@ -0,0 +1,73 @@
#ifndef CSM_TOOLS_TOOLS_H
#define CSM_TOOLS_TOOLS_H
#include <QObject>
#include <map>
namespace CSMWorld
{
class Data;
class UniversalId;
}
namespace CSMTools
{
class Verifier;
class Operation;
class ReportModel;
class Tools : public QObject
{
Q_OBJECT
CSMWorld::Data& mData;
Verifier *mVerifier;
std::map<int, ReportModel *> mReports;
int mNextReportNumber;
std::map<int, int> mActiveReports; // type, report number
// not implemented
Tools (const Tools&);
Tools& operator= (const Tools&);
Verifier *getVerifier();
Operation *get (int type);
///< Returns a 0-pointer, if operation hasn't been used yet.
const Operation *get (int type) const;
///< Returns a 0-pointer, if operation hasn't been used yet.
public:
Tools (CSMWorld::Data& data);
virtual ~Tools();
CSMWorld::UniversalId runVerifier();
///< \return ID of the report for this verification run
void abortOperation (int type);
///< \attention The operation is not aborted immediately.
int getRunningOperations() const;
ReportModel *getReport (const CSMWorld::UniversalId& id);
///< The ownership of the returned report is not transferred.
private slots:
void verifierDone();
void verifierMessage (const QString& message, int type);
signals:
void progress (int current, int max, int type);
void done (int type);
};
}
#endif

@ -0,0 +1,7 @@
#include "verifier.hpp"
#include "../doc/state.hpp"
CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying)
{}

@ -0,0 +1,17 @@
#ifndef CSM_TOOLS_VERIFIER_H
#define CSM_TOOLS_VERIFIER_H
#include "operation.hpp"
namespace CSMTools
{
class Verifier : public Operation
{
public:
Verifier();
};
}
#endif

@ -0,0 +1,13 @@
#include "columnbase.hpp"
CSMWorld::ColumnBase::ColumnBase (const std::string& title, int flags)
: mTitle (title), mFlags (flags)
{}
CSMWorld::ColumnBase::~ColumnBase() {}
bool CSMWorld::ColumnBase::isUserEditable() const
{
return isEditable();
}

@ -0,0 +1,57 @@
#ifndef CSM_WOLRD_COLUMNBASE_H
#define CSM_WOLRD_COLUMNBASE_H
#include <string>
#include <Qt>
#include <QVariant>
#include "record.hpp"
namespace CSMWorld
{
struct ColumnBase
{
enum Roles
{
Role_Flags = Qt::UserRole
};
enum Flags
{
Flag_Table = 1, // column should be displayed in table view
Flag_Dialogue = 2 // column should be displayed in dialogue view
};
std::string mTitle;
int mFlags;
ColumnBase (const std::string& title, int flag);
virtual ~ColumnBase();
virtual bool isEditable() const = 0;
virtual bool isUserEditable() const;
///< Can this column be edited directly by the user?
};
template<typename ESXRecordT>
struct Column : public ColumnBase
{
std::string mTitle;
int mFlags;
Column (const std::string& title, int flags = Flag_Table | Flag_Dialogue)
: ColumnBase (title, flags) {}
virtual QVariant get (const Record<ESXRecordT>& record) const = 0;
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
throw std::logic_error ("Column " + mTitle + " is not editable");
}
};
}
#endif

@ -0,0 +1,95 @@
#ifndef CSM_WOLRD_COLUMNS_H
#define CSM_WOLRD_COLUMNS_H
#include "columnbase.hpp"
namespace CSMWorld
{
template<typename ESXRecordT>
struct FloatValueColumn : public Column<ESXRecordT>
{
FloatValueColumn() : Column<ESXRecordT> ("Value") {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mValue;
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT base = record.getBase();
base.mValue = data.toFloat();
record.setModified (base);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT>
struct StringIdColumn : public Column<ESXRecordT>
{
StringIdColumn() : Column<ESXRecordT> ("ID") {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mId.c_str();
}
virtual bool isEditable() const
{
return false;
}
};
template<typename ESXRecordT>
struct RecordStateColumn : public Column<ESXRecordT>
{
RecordStateColumn() : Column<ESXRecordT> ("*") {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
if (record.mState==Record<ESXRecordT>::State_Erased)
return static_cast<int> (Record<ESXRecordT>::State_Deleted);
return static_cast<int> (record.mState);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
record.mState = static_cast<RecordBase::State> (data.toInt());
}
virtual bool isEditable() const
{
return true;
}
virtual bool isUserEditable() const
{
return false;
}
};
template<typename ESXRecordT>
struct FixedRecordTypeColumn : public Column<ESXRecordT>
{
int mType;
FixedRecordTypeColumn (int type) : Column<ESXRecordT> ("Type", 0), mType (type) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return mType;
}
virtual bool isEditable() const
{
return false;
}
};
}
#endif

@ -0,0 +1,108 @@
#include "commands.hpp"
#include <QAbstractTableModel>
#include "idtableproxymodel.hpp"
#include "idtable.hpp"
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_)
{
mOld = mModel.data (mIndex, Qt::EditRole);
setText ("Modify " + mModel.headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString());
}
void CSMWorld::ModifyCommand::redo()
{
mModel.setData (mIndex, mNew);
}
void CSMWorld::ModifyCommand::undo()
{
mModel.setData (mIndex, mOld);
}
CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id)
{
setText (("Create record " + id).c_str());
}
void CSMWorld::CreateCommand::redo()
{
mModel.addRecord (mId);
}
void CSMWorld::CreateCommand::undo()
{
mModel.removeRow (mModel.getModelIndex (mId, 0).row());
}
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
{
setText (("Revert record " + id).c_str());
mOld = model.getRecord (id).clone();
}
CSMWorld::RevertCommand::~RevertCommand()
{
delete mOld;
}
void CSMWorld::RevertCommand::redo()
{
QModelIndex index = mModel.getModelIndex (mId, 1);
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
if (state==RecordBase::State_ModifiedOnly)
{
mModel.removeRows (index.row(), 1);
}
else
{
mModel.setData (index, static_cast<int> (RecordBase::State_BaseOnly));
}
}
void CSMWorld::RevertCommand::undo()
{
mModel.setRecord (*mOld);
}
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
{
setText (("Delete record " + id).c_str());
mOld = model.getRecord (id).clone();
}
CSMWorld::DeleteCommand::~DeleteCommand()
{
delete mOld;
}
void CSMWorld::DeleteCommand::redo()
{
QModelIndex index = mModel.getModelIndex (mId, 1);
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
if (state==RecordBase::State_ModifiedOnly)
{
mModel.removeRows (index.row(), 1);
}
else
{
mModel.setData (index, static_cast<int> (RecordBase::State_Deleted));
}
}
void CSMWorld::DeleteCommand::undo()
{
mModel.setRecord (*mOld);
}

@ -0,0 +1,95 @@
#ifndef CSM_WOLRD_COMMANDS_H
#define CSM_WOLRD_COMMANDS_H
#include "record.hpp"
#include <string>
#include <QVariant>
#include <QUndoCommand>
#include <QModelIndex>
class QModelIndex;
class QAbstractItemModel;
namespace CSMWorld
{
class IdTableProxyModel;
class IdTable;
class RecordBase;
class ModifyCommand : public QUndoCommand
{
QAbstractItemModel& mModel;
QModelIndex mIndex;
QVariant mNew;
QVariant mOld;
public:
ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_,
QUndoCommand *parent = 0);
virtual void redo();
virtual void undo();
};
class CreateCommand : public QUndoCommand
{
IdTableProxyModel& mModel;
std::string mId;
public:
CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent = 0);
virtual void redo();
virtual void undo();
};
class RevertCommand : public QUndoCommand
{
IdTable& mModel;
std::string mId;
RecordBase *mOld;
// not implemented
RevertCommand (const RevertCommand&);
RevertCommand& operator= (const RevertCommand&);
public:
RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
virtual ~RevertCommand();
virtual void redo();
virtual void undo();
};
class DeleteCommand : public QUndoCommand
{
IdTable& mModel;
std::string mId;
RecordBase *mOld;
// not implemented
DeleteCommand (const DeleteCommand&);
DeleteCommand& operator= (const DeleteCommand&);
public:
DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
virtual ~DeleteCommand();
virtual void redo();
virtual void undo();
};
}
#endif

@ -0,0 +1,62 @@
#include "data.hpp"
#include <stdexcept>
#include <QAbstractTableModel>
#include <components/esm/loadglob.hpp>
#include "idtable.hpp"
#include "columns.hpp"
void CSMWorld::Data::addModel (QAbstractTableModel *model, UniversalId::Type type1,
UniversalId::Type type2)
{
mModels.push_back (model);
mModelIndex.insert (std::make_pair (type1, model));
if (type2!=UniversalId::Type_None)
mModelIndex.insert (std::make_pair (type2, model));
}
CSMWorld::Data::Data()
{
mGlobals.addColumn (new StringIdColumn<ESM::Global>);
mGlobals.addColumn (new RecordStateColumn<ESM::Global>);
mGlobals.addColumn (new FixedRecordTypeColumn<ESM::Global> (UniversalId::Type_Global));
mGlobals.addColumn (new FloatValueColumn<ESM::Global>);
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
}
CSMWorld::Data::~Data()
{
for (std::vector<QAbstractTableModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
delete *iter;
}
const CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const
{
return mGlobals;
}
CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals()
{
return mGlobals;
}
QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id)
{
std::map<UniversalId::Type, QAbstractTableModel *>::iterator iter = mModelIndex.find (id.getType());
if (iter==mModelIndex.end())
throw std::logic_error ("No table model available for " + id.toString());
return iter->second;
}
void CSMWorld::Data::merge()
{
mGlobals.merge();
}

@ -0,0 +1,50 @@
#ifndef CSM_WOLRD_DATA_H
#define CSM_WOLRD_DATA_H
#include <map>
#include <vector>
#include <components/esm/loadglob.hpp>
#include "idcollection.hpp"
#include "universalid.hpp"
class QAbstractTableModel;
namespace CSMWorld
{
class Data
{
IdCollection<ESM::Global> mGlobals;
std::vector<QAbstractTableModel *> mModels;
std::map<UniversalId::Type, QAbstractTableModel *> mModelIndex;
// not implemented
Data (const Data&);
Data& operator= (const Data&);
void addModel (QAbstractTableModel *model, UniversalId::Type type1,
UniversalId::Type type2 = UniversalId::Type_None);
public:
Data();
~Data();
const IdCollection<ESM::Global>& getGlobals() const;
IdCollection<ESM::Global>& getGlobals();
QAbstractTableModel *getTableModel (const UniversalId& id);
///< If no table model is available for \a id, an exception is thrown.
///
/// \note The returned table may either be the model for the ID itself or the model that
/// contains the record specified by the ID.
void merge();
///< Merge modified into base.
};
}
#endif

@ -0,0 +1,6 @@
#include "idcollection.hpp"
CSMWorld::IdCollectionBase::IdCollectionBase() {}
CSMWorld::IdCollectionBase::~IdCollectionBase() {}

@ -0,0 +1,325 @@
#ifndef CSM_WOLRD_IDCOLLECTION_H
#define CSM_WOLRD_IDCOLLECTION_H
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <cctype>
#include <stdexcept>
#include <functional>
#include <QVariant>
#include "columnbase.hpp"
namespace CSMWorld
{
class IdCollectionBase
{
// not implemented
IdCollectionBase (const IdCollectionBase&);
IdCollectionBase& operator= (const IdCollectionBase&);
public:
IdCollectionBase();
virtual ~IdCollectionBase();
virtual int getSize() const = 0;
virtual std::string getId (int index) const = 0;
virtual int getIndex (const std::string& id) const = 0;
virtual int getColumns() const = 0;
virtual const ColumnBase& getColumn (int column) const = 0;
virtual QVariant getData (int index, int column) const = 0;
virtual void setData (int index, int column, const QVariant& data) = 0;
virtual void merge() = 0;
///< Merge modified into base.
virtual void purge() = 0;
///< Remove records that are flagged as erased.
virtual void removeRows (int index, int count) = 0;
virtual void appendBlankRecord (const std::string& id) = 0;
virtual int searchId (const std::string& id) const = 0;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record) = 0;
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
virtual void appendRecord (const RecordBase& record) = 0;
///< If the record type does not match, an exception is thrown.
virtual std::string getId (const RecordBase& record) const = 0;
///< Return ID for \a record.
///
/// \attention Throw san exception, if the type of \a record does not match.
virtual const RecordBase& getRecord (const std::string& id) const = 0;
};
///< \brief Collection of ID-based records
template<typename ESXRecordT>
class IdCollection : public IdCollectionBase
{
std::vector<Record<ESXRecordT> > mRecords;
std::map<std::string, int> mIndex;
std::vector<Column<ESXRecordT> *> mColumns;
// not implemented
IdCollection (const IdCollection&);
IdCollection& operator= (const IdCollection&);
public:
IdCollection();
virtual ~IdCollection();
void add (const ESXRecordT& record);
///< Add a new record (modified)
virtual int getSize() const;
virtual std::string getId (int index) const;
virtual int getIndex (const std::string& id) const;
virtual int getColumns() const;
virtual QVariant getData (int index, int column) const;
virtual void setData (int index, int column, const QVariant& data);
virtual const ColumnBase& getColumn (int column) const;
virtual void merge();
///< Merge modified into base.
virtual void purge();
///< Remove records that are flagged as erased.
virtual void removeRows (int index, int count) ;
virtual void appendBlankRecord (const std::string& id);
virtual int searchId (const std::string& id) const;
////< Search record with \a id.
/// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record);
///< If the record type does not match, an exception is thrown.
///
/// \attention \a record must not change the ID.
virtual void appendRecord (const RecordBase& record);
///< If the record type does not match, an exception is thrown.
virtual std::string getId (const RecordBase& record) const;
///< Return ID for \a record.
///
/// \attention Throw san exception, if the type of \a record does not match.
virtual const RecordBase& getRecord (const std::string& id) const;
void addColumn (Column<ESXRecordT> *column);
};
template<typename ESXRecordT>
IdCollection<ESXRecordT>::IdCollection()
{}
template<typename ESXRecordT>
IdCollection<ESXRecordT>::~IdCollection()
{
for (typename std::vector<Column<ESXRecordT> *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)
delete *iter;
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::add (const ESXRecordT& record)
{
std::string id;
std::transform (record.mId.begin(), record.mId.end(), std::back_inserter (id),
(int(*)(int)) std::tolower);
std::map<std::string, int>::iterator iter = mIndex.find (id);
if (iter==mIndex.end())
{
Record<ESXRecordT> record2;
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record;
mRecords.push_back (record2);
mIndex.insert (std::make_pair (id, mRecords.size()-1));
}
else
{
mRecords[iter->second].setModified (record);
}
}
template<typename ESXRecordT>
int IdCollection<ESXRecordT>::getSize() const
{
return mRecords.size();
}
template<typename ESXRecordT>
std::string IdCollection<ESXRecordT>::getId (int index) const
{
return mRecords.at (index).get().mId;
}
template<typename ESXRecordT>
int IdCollection<ESXRecordT>::getIndex (const std::string& id) const
{
int index = searchId (id);
if (index==-1)
throw std::runtime_error ("invalid ID: " + id);
return index;
}
template<typename ESXRecordT>
int IdCollection<ESXRecordT>::getColumns() const
{
return mColumns.size();
}
template<typename ESXRecordT>
QVariant IdCollection<ESXRecordT>::getData (int index, int column) const
{
return mColumns.at (column)->get (mRecords.at (index));
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::setData (int index, int column, const QVariant& data)
{
return mColumns.at (column)->set (mRecords.at (index), data);
}
template<typename ESXRecordT>
const ColumnBase& IdCollection<ESXRecordT>::getColumn (int column) const
{
return *mColumns.at (column);
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::addColumn (Column<ESXRecordT> *column)
{
mColumns.push_back (column);
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::merge()
{
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
iter->merge();
purge();
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::purge()
{
mRecords.erase (std::remove_if (mRecords.begin(), mRecords.end(),
std::mem_fun_ref (&Record<ESXRecordT>::isErased) // I want lambda :(
), mRecords.end());
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::removeRows (int index, int count)
{
mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count);
typename std::map<std::string, int>::iterator iter = mIndex.begin();
while (iter!=mIndex.end())
{
if (iter->second>=index)
{
if (iter->second>=index+count)
{
iter->second -= count;
}
else
{
mIndex.erase (iter++);
}
}
++iter;
}
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::appendBlankRecord (const std::string& id)
{
ESXRecordT record;
record.mId = id;
record.blank();
add (record);
}
template<typename ESXRecordT>
int IdCollection<ESXRecordT>::searchId (const std::string& id) const
{
std::string id2;
std::transform (id.begin(), id.end(), std::back_inserter (id2),
(int(*)(int)) std::tolower);
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
if (iter==mIndex.end())
return -1;
return iter->second;
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::replace (int index, const RecordBase& record)
{
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
}
template<typename ESXRecordT>
void IdCollection<ESXRecordT>::appendRecord (const RecordBase& record)
{
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
mIndex.insert (std::make_pair (getId (record), mRecords.size()-1));
}
template<typename ESXRecordT>
std::string IdCollection<ESXRecordT>::getId (const RecordBase& record) const
{
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
return (record2.isModified() ? record2.mModified : record2.mBase).mId;
}
template<typename ESXRecordT>
const RecordBase& IdCollection<ESXRecordT>::getRecord (const std::string& id) const
{
int index = getIndex (id);
return mRecords.at (index);
}
}
#endif

@ -0,0 +1,137 @@
#include "idtable.hpp"
#include "idcollection.hpp"
CSMWorld::IdTable::IdTable (IdCollectionBase *idCollection) : mIdCollection (idCollection)
{
}
CSMWorld::IdTable::~IdTable()
{
}
int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return mIdCollection->getSize();
}
int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const
{
if (parent.isValid())
return 0;
return mIdCollection->getColumns();
}
QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const
{
if (role!=Qt::DisplayRole && role!=Qt::EditRole)
return QVariant();
if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable())
return QVariant();
return mIdCollection->getData (index.row(), index.column());
}
QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation, int role) const
{
if (orientation==Qt::Vertical)
return QVariant();
if (role==Qt::DisplayRole)
return tr (mIdCollection->getColumn (section).mTitle.c_str());
if (role==ColumnBase::Role_Flags)
return mIdCollection->getColumn (section).mFlags;
return QVariant();
}
bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &value, int role)
{
if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)
{
mIdCollection->setData (index.row(), index.column(), value);
emit dataChanged (CSMWorld::IdTable::index (index.row(), 0),
CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1));
return true;
}
return false;
}
Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const
{
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
if (mIdCollection->getColumn (index.column()).isUserEditable())
flags |= Qt::ItemIsEditable;
return flags;
}
bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& parent)
{
if (parent.isValid())
return false;
beginRemoveRows (parent, row, row+count-1);
mIdCollection->removeRows (row, count);
endRemoveRows();
return true;
}
void CSMWorld::IdTable::addRecord (const std::string& id)
{
int index = mIdCollection->getSize();
beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendBlankRecord (id);
endInsertRows();
}
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
{
return index (mIdCollection->getIndex (id), column);
}
void CSMWorld::IdTable::setRecord (const RecordBase& record)
{
int index = mIdCollection->searchId (mIdCollection->getId (record));
if (index==-1)
{
int index = mIdCollection->getSize();
beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendRecord (record);
endInsertRows();
}
else
{
mIdCollection->replace (index, record);
emit dataChanged (CSMWorld::IdTable::index (index, 0),
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
}
}
const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const
{
return mIdCollection->getRecord (id);
}

@ -0,0 +1,53 @@
#ifndef CSM_WOLRD_IDTABLE_H
#define CSM_WOLRD_IDTABLE_H
#include <QAbstractTableModel>
namespace CSMWorld
{
class IdCollectionBase;
class RecordBase;
class IdTable : public QAbstractTableModel
{
Q_OBJECT
IdCollectionBase *mIdCollection;
// not implemented
IdTable (const IdTable&);
IdTable& operator= (const IdTable&);
public:
IdTable (IdCollectionBase *idCollection);
///< The ownership of \a idCollection is not transferred.
virtual ~IdTable();
virtual int rowCount (const QModelIndex & parent = QModelIndex()) const;
virtual int columnCount (const QModelIndex & parent = QModelIndex()) const;
virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const;
virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
virtual Qt::ItemFlags flags (const QModelIndex & index) const;
virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex());
void addRecord (const std::string& id);
QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const RecordBase& record);
///< Add record or overwrite existing recrod.
const RecordBase& getRecord (const std::string& id) const;
};
}
#endif

@ -0,0 +1,18 @@
#include "idtableproxymodel.hpp"
#include "idtable.hpp"
CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
: QSortFilterProxyModel (parent)
{}
void CSMWorld::IdTableProxyModel::addRecord (const std::string& id)
{
dynamic_cast<IdTable&> (*sourceModel()).addRecord (id);
}
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
{
return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column));
}

@ -0,0 +1,24 @@
#ifndef CSM_WOLRD_IDTABLEPROXYMODEL_H
#define CSM_WOLRD_IDTABLEPROXYMODEL_H
#include <QSortFilterProxyModel>
#include <string>
namespace CSMWorld
{
class IdTableProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
IdTableProxyModel (QObject *parent = 0);
virtual void addRecord (const std::string& id);
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
};
}
#endif

@ -0,0 +1,21 @@
#include "record.hpp"
CSMWorld::RecordBase::~RecordBase() {}
bool CSMWorld::RecordBase::RecordBase::isDeleted() const
{
return mState==State_Deleted || mState==State_Erased;
}
bool CSMWorld::RecordBase::RecordBase::isErased() const
{
return mState==State_Erased;
}
bool CSMWorld::RecordBase::RecordBase::isModified() const
{
return mState==State_Modified || mState==State_ModifiedOnly;
}

@ -0,0 +1,104 @@
#ifndef CSM_WOLRD_RECORD_H
#define CSM_WOLRD_RECORD_H
#include <stdexcept>
namespace CSMWorld
{
struct RecordBase
{
enum State
{
State_BaseOnly = 0, // defined in base only
State_Modified = 1, // exists in base, but has been modified
State_ModifiedOnly = 2, // newly created in modified
State_Deleted = 3, // exists in base, but has been deleted
State_Erased = 4 // does not exist at all (we mostly treat that the same way as deleted)
};
State mState;
virtual ~RecordBase();
virtual RecordBase *clone() const = 0;
bool isDeleted() const;
bool isErased() const;
bool isModified() const;
};
template <typename ESXRecordT>
struct Record : public RecordBase
{
ESXRecordT mBase;
ESXRecordT mModified;
virtual RecordBase *clone() const;
const ESXRecordT& get() const;
///< Throws an exception, if the record is deleted.
const ESXRecordT& getBase() const;
///< Throws an exception, if the record is deleted. Returns modified, if there is no base.
void setModified (const ESXRecordT& modified);
///< Throws an exception, if the record is deleted.
void merge();
///< Merge modified into base.
};
template <typename ESXRecordT>
RecordBase *Record<ESXRecordT>::clone() const
{
return new Record<ESXRecordT> (*this);
}
template <typename ESXRecordT>
const ESXRecordT& Record<ESXRecordT>::get() const
{
if (mState==State_Erased)
throw std::logic_error ("attempt to access a deleted record");
return mState==State_BaseOnly ? mBase : mModified;
}
template <typename ESXRecordT>
const ESXRecordT& Record<ESXRecordT>::getBase() const
{
if (mState==State_Erased)
throw std::logic_error ("attempt to access a deleted record");
return mState==State_ModifiedOnly ? mModified : mBase;
}
template <typename ESXRecordT>
void Record<ESXRecordT>::setModified (const ESXRecordT& modified)
{
if (mState==State_Erased)
throw std::logic_error ("attempt to modify a deleted record");
mModified = modified;
if (mState!=State_ModifiedOnly)
mState = mBase==mModified ? State_BaseOnly : State_Modified;
}
template <typename ESXRecordT>
void Record<ESXRecordT>::merge()
{
if (isModified())
{
mBase = mModified;
mState = State_BaseOnly;
}
else if (mState==State_Deleted)
{
mState = State_Erased;
}
}
}
#endif

@ -0,0 +1,237 @@
#include "universalid.hpp"
#include <ostream>
#include <stdexcept>
#include <sstream>
namespace
{
struct TypeData
{
CSMWorld::UniversalId::Class mClass;
CSMWorld::UniversalId::Type mType;
const char *mName;
};
static const TypeData sNoArg[] =
{
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
};
static const TypeData sIdArg[] =
{
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
};
static const TypeData sIndexArg[] =
{
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results" },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
};
}
CSMWorld::UniversalId::UniversalId (const std::string& universalId)
{
std::string::size_type index = universalId.find (':');
if (index==std::string::npos)
{
std::string type = universalId.substr (0, index);
if (index==std::string::npos)
{
for (int i=0; sNoArg[i].mName; ++i)
if (type==sNoArg[i].mName)
{
mArgumentType = ArgumentType_None;
mType = sNoArg[i].mType;
mClass = sNoArg[i].mClass;
return;
}
}
else
{
for (int i=0; sIdArg[i].mName; ++i)
if (type==sIdArg[i].mName)
{
mArgumentType = ArgumentType_Id;
mType = sIdArg[i].mType;
mClass = sIdArg[i].mClass;
mId = universalId.substr (0, index);
return;
}
for (int i=0; sIndexArg[i].mName; ++i)
if (type==sIndexArg[i].mName)
{
mArgumentType = ArgumentType_Index;
mType = sIndexArg[i].mType;
mClass = sIndexArg[i].mClass;
std::istringstream stream (universalId.substr (0, index));
if (stream >> mIndex)
return;
break;
}
}
}
throw std::runtime_error ("invalid UniversalId: " + universalId);
}
CSMWorld::UniversalId::UniversalId (Type type) : mArgumentType (ArgumentType_None), mType (type), mIndex (0)
{
for (int i=0; sNoArg[i].mName; ++i)
if (type==sNoArg[i].mType)
{
mClass = sNoArg[i].mClass;
return;
}
throw std::logic_error ("invalid argument-less UniversalId type");
}
CSMWorld::UniversalId::UniversalId (Type type, const std::string& id)
: mArgumentType (ArgumentType_Id), mType (type), mId (id), mIndex (0)
{
for (int i=0; sIdArg[i].mName; ++i)
if (type==sIdArg[i].mType)
{
mClass = sIdArg[i].mClass;
return;
}
throw std::logic_error ("invalid ID argument UniversalId type");
}
CSMWorld::UniversalId::UniversalId (Type type, int index)
: mArgumentType (ArgumentType_Index), mType (type), mIndex (index)
{
for (int i=0; sIndexArg[i].mName; ++i)
if (type==sIndexArg[i].mType)
{
mClass = sIndexArg[i].mClass;
return;
}
throw std::logic_error ("invalid index argument UniversalId type");
}
CSMWorld::UniversalId::Class CSMWorld::UniversalId::getClass() const
{
return mClass;
}
CSMWorld::UniversalId::ArgumentType CSMWorld::UniversalId::getArgumentType() const
{
return mArgumentType;
}
CSMWorld::UniversalId::Type CSMWorld::UniversalId::getType() const
{
return mType;
}
const std::string& CSMWorld::UniversalId::getId() const
{
if (mArgumentType!=ArgumentType_Id)
throw std::logic_error ("invalid access to ID of non-ID UniversalId");
return mId;
}
int CSMWorld::UniversalId::getIndex() const
{
if (mArgumentType!=ArgumentType_Index)
throw std::logic_error ("invalid access to index of non-index UniversalId");
return mIndex;
}
bool CSMWorld::UniversalId::isEqual (const UniversalId& universalId) const
{
if (mClass!=universalId.mClass || mArgumentType!=universalId.mArgumentType || mType!=universalId.mType)
return false;
switch (mArgumentType)
{
case ArgumentType_Id: return mId==universalId.mId;
case ArgumentType_Index: return mIndex==universalId.mIndex;
default: return true;
}
}
bool CSMWorld::UniversalId::isLess (const UniversalId& universalId) const
{
if (mType<universalId.mType)
return true;
if (mType>universalId.mType)
return false;
switch (mArgumentType)
{
case ArgumentType_Id: return mId<universalId.mId;
case ArgumentType_Index: return mIndex<universalId.mIndex;
default: return false;
}
}
std::string CSMWorld::UniversalId::getTypeName() const
{
const TypeData *typeData = mArgumentType==ArgumentType_None ? sNoArg :
(mArgumentType==ArgumentType_Id ? sIdArg : sIndexArg);
for (int i=0; typeData[i].mName; ++i)
if (typeData[i].mType==mType)
return typeData[i].mName;
throw std::logic_error ("failed to retrieve UniversalId type name");
}
std::string CSMWorld::UniversalId::toString() const
{
std::ostringstream stream;
stream << getTypeName();
switch (mArgumentType)
{
case ArgumentType_None: break;
case ArgumentType_Id: stream << ": " << mId; break;
case ArgumentType_Index: stream << ": " << mIndex; break;
}
return stream.str();
}
bool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)
{
return left.isEqual (right);
}
bool CSMWorld::operator!= (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)
{
return !left.isEqual (right);
}
bool CSMWorld::operator< (const UniversalId& left, const UniversalId& right)
{
return left.isLess (right);
}
std::ostream& CSMWorld::operator< (std::ostream& stream, const CSMWorld::UniversalId& universalId)
{
return stream << universalId.toString();
}

@ -0,0 +1,96 @@
#ifndef CSM_WOLRD_UNIVERSALID_H
#define CSM_WOLRD_UNIVERSALID_H
#include <string>
#include <iosfwd>
#include <QMetaType>
namespace CSMWorld
{
class UniversalId
{
public:
enum Class
{
Class_None = 0,
Class_Record,
Class_SubRecord,
Class_RecordList,
Class_Collection, // multiple types of records combined
Class_Transient, // not part of the world data or the project data
Class_NonRecord // record like data that is not part of the world
};
enum ArgumentType
{
ArgumentType_None,
ArgumentType_Id,
ArgumentType_Index
};
enum Type
{
Type_None,
Type_Globals,
Type_Global,
Type_VerificationResults
};
private:
Class mClass;
ArgumentType mArgumentType;
Type mType;
std::string mId;
int mIndex;
public:
UniversalId (const std::string& universalId);
UniversalId (Type type = Type_None);
///< Using a type for a non-argument-less UniversalId will throw an exception.
UniversalId (Type type, const std::string& id);
///< Using a type for a non-ID-argument UniversalId will throw an exception.
UniversalId (Type type, int index);
///< Using a type for a non-index-argument UniversalId will throw an exception.
Class getClass() const;
ArgumentType getArgumentType() const;
Type getType() const;
const std::string& getId() const;
///< Calling this function for a non-ID type will throw an exception.
int getIndex() const;
///< Calling this function for a non-index type will throw an exception.
bool isEqual (const UniversalId& universalId) const;
bool isLess (const UniversalId& universalId) const;
std::string getTypeName() const;
std::string toString() const;
};
bool operator== (const UniversalId& left, const UniversalId& right);
bool operator!= (const UniversalId& left, const UniversalId& right);
bool operator< (const UniversalId& left, const UniversalId& right);
std::ostream& operator< (std::ostream& stream, const UniversalId& universalId);
}
Q_DECLARE_METATYPE (CSMWorld::UniversalId)
#endif

@ -0,0 +1,54 @@
#include "operation.hpp"
#include <sstream>
#include "../../model/doc/document.hpp"
void CSVDoc::Operation::updateLabel (int threads)
{
if (threads==-1 || ((threads==0)!=mStalling))
{
std::string name ("unknown operation");
switch (mType)
{
case CSMDoc::State_Saving: name = "saving"; break;
case CSMDoc::State_Verifying: name = "verifying"; break;
}
std::ostringstream stream;
if ((mStalling = (threads<=0)))
{
stream << name << " (waiting for a free worker thread)";
}
else
{
stream << name << " (%p%)";
}
setFormat (stream.str().c_str());
}
}
CSVDoc::Operation::Operation (int type) : mType (type), mStalling (false)
{
/// \todo Add a cancel button or a pop up menu with a cancel item
updateLabel();
/// \todo assign different progress bar colours to allow the user to distinguish easily between operation types
}
void CSVDoc::Operation::setProgress (int current, int max, int threads)
{
updateLabel (threads);
setRange (0, max);
setValue (current);
}
int CSVDoc::Operation::getType() const
{
return mType;
}

@ -0,0 +1,31 @@
#ifndef CSV_DOC_OPERATION_H
#define CSV_DOC_OPERATION_H
#include <QProgressBar>
namespace CSVDoc
{
class Operation : public QProgressBar
{
Q_OBJECT
int mType;
bool mStalling;
// not implemented
Operation (const Operation&);
Operation& operator= (const Operation&);
void updateLabel (int threads = -1);
public:
Operation (int type);
void setProgress (int current, int max, int threads);
int getType() const;
};
}
#endif

@ -0,0 +1,47 @@
#include "operations.hpp"
#include <QVBoxLayout>
#include "operation.hpp"
CSVDoc::Operations::Operations()
{
/// \todo make widget height fixed (exactly the height required to display all operations)
setFeatures (QDockWidget::NoDockWidgetFeatures);
QWidget *widget = new QWidget;
setWidget (widget);
mLayout = new QVBoxLayout;
widget->setLayout (mLayout);
}
void CSVDoc::Operations::setProgress (int current, int max, int type, int threads)
{
for (std::vector<Operation *>::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter)
if ((*iter)->getType()==type)
{
(*iter)->setProgress (current, max, threads);
return;
}
Operation *operation = new Operation (type);
mLayout->addWidget (operation);
mOperations.push_back (operation);
operation->setProgress (current, max, threads);
}
void CSVDoc::Operations::quitOperation (int type)
{
for (std::vector<Operation *>::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter)
if ((*iter)->getType()==type)
{
delete *iter;
mOperations.erase (iter);
break;
}
}

@ -0,0 +1,37 @@
#ifndef CSV_DOC_OPERATIONS_H
#define CSV_DOC_OPERATIONS_H
#include <vector>
#include <QDockWidget>
class QVBoxLayout;
namespace CSVDoc
{
class Operation;
class Operations : public QDockWidget
{
Q_OBJECT
QVBoxLayout *mLayout;
std::vector<Operation *> mOperations;
// not implemented
Operations (const Operations&);
Operations& operator= (const Operations&);
public:
Operations();
void setProgress (int current, int max, int type, int threads);
///< Implicitly starts the operation, if it is not running already.
void quitOperation (int type);
///< Calling this function for an operation that is not running is a no-op.
};
}
#endif

@ -0,0 +1,18 @@
#include "subview.hpp"
CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id)
{
/// \todo add a button to the title bar that clones this sub view
setWindowTitle (mUniversalId.toString().c_str());
/// \todo remove (for testing only)
setMinimumWidth (100);
setMinimumHeight (60);
}
CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const
{
return mUniversalId;
}

@ -0,0 +1,45 @@
#ifndef CSV_DOC_SUBVIEW_H
#define CSV_DOC_SUBVIEW_H
#include "../../model/doc/document.hpp"
#include "../../model/world/universalid.hpp"
#include "subviewfactory.hpp"
#include <QDockWidget>
class QUndoStack;
namespace CSMWorld
{
class Data;
}
namespace CSVDoc
{
class SubView : public QDockWidget
{
Q_OBJECT
CSMWorld::UniversalId mUniversalId;
// not implemented
SubView (const SubView&);
SubView& operator= (SubView&);
public:
SubView (const CSMWorld::UniversalId& id);
CSMWorld::UniversalId getUniversalId() const;
virtual void setEditLock (bool locked) = 0;
signals:
void focusId (const CSMWorld::UniversalId& universalId);
};
}
#endif

@ -0,0 +1,38 @@
#include "subviewfactory.hpp"
#include <cassert>
#include <stdexcept>
CSVDoc::SubViewFactoryBase::SubViewFactoryBase() {}
CSVDoc::SubViewFactoryBase::~SubViewFactoryBase() {}
CSVDoc::SubViewFactoryManager::SubViewFactoryManager() {}
CSVDoc::SubViewFactoryManager::~SubViewFactoryManager()
{
for (std::map<CSMWorld::UniversalId::Type, SubViewFactoryBase *>::iterator iter (mSubViewFactories.begin());
iter!=mSubViewFactories.end(); ++iter)
delete iter->second;
}
void CSVDoc::SubViewFactoryManager::add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory)
{
assert (mSubViewFactories.find (id)==mSubViewFactories.end());
mSubViewFactories.insert (std::make_pair (id, factory));
}
CSVDoc::SubView *CSVDoc::SubViewFactoryManager::makeSubView (const CSMWorld::UniversalId& id,
CSMDoc::Document& document)
{
std::map<CSMWorld::UniversalId::Type, SubViewFactoryBase *>::iterator iter = mSubViewFactories.find (id.getType());
if (iter==mSubViewFactories.end())
throw std::runtime_error ("Failed to create a sub view for: " + id.toString());
return iter->second->makeSubView (id, document);
}

@ -0,0 +1,55 @@
#ifndef CSV_DOC_SUBVIEWFACTORY_H
#define CSV_DOC_SUBVIEWFACTORY_H
#include <map>
#include "../../model/world/universalid.hpp"
namespace CSMDoc
{
class Document;
}
namespace CSVDoc
{
class SubView;
class SubViewFactoryBase
{
// not implemented
SubViewFactoryBase (const SubViewFactoryBase&);
SubViewFactoryBase& operator= (const SubViewFactoryBase&);
public:
SubViewFactoryBase();
virtual ~SubViewFactoryBase();
virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) = 0;
///< The ownership of the returned sub view is not transferred.
};
class SubViewFactoryManager
{
std::map<CSMWorld::UniversalId::Type, SubViewFactoryBase *> mSubViewFactories;
// not implemented
SubViewFactoryManager (const SubViewFactoryManager&);
SubViewFactoryManager& operator= (const SubViewFactoryManager&);
public:
SubViewFactoryManager();
~SubViewFactoryManager();
void add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory);
///< The ownership of \a factory is transferred to this.
SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
///< The ownership of the returned sub view is not transferred.
};
}
#endif

@ -0,0 +1,50 @@
#ifndef CSV_DOC_SUBVIEWFACTORYIMP_H
#define CSV_DOC_SUBVIEWFACTORYIMP_H
#include "../../model/doc/document.hpp"
#include "subviewfactory.hpp"
namespace CSVDoc
{
template<class SubViewT>
class SubViewFactory : public SubViewFactoryBase
{
public:
virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
};
template<class SubViewT>
CSVDoc::SubView *SubViewFactory<SubViewT>::makeSubView (const CSMWorld::UniversalId& id,
CSMDoc::Document& document)
{
return new SubViewT (id, document);
}
template<class SubViewT>
class SubViewFactoryWithCreateFlag : public SubViewFactoryBase
{
bool mCreateAndDelete;
public:
SubViewFactoryWithCreateFlag (bool createAndDelete);
virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
};
template<class SubViewT>
SubViewFactoryWithCreateFlag<SubViewT>::SubViewFactoryWithCreateFlag (bool createAndDelete)
: mCreateAndDelete (createAndDelete)
{}
template<class SubViewT>
CSVDoc::SubView *SubViewFactoryWithCreateFlag<SubViewT>::makeSubView (const CSMWorld::UniversalId& id,
CSMDoc::Document& document)
{
return new SubViewT (id, document, mCreateAndDelete);
}
}
#endif

@ -0,0 +1,216 @@
#include "view.hpp"
#include <sstream>
#include <stdexcept>
#include <QCloseEvent>
#include <QMenuBar>
#include <QMdiArea>
#include "../../model/doc/document.hpp"
#include "../world/subviews.hpp"
#include "../tools/subviews.hpp"
#include "viewmanager.hpp"
#include "operations.hpp"
#include "subview.hpp"
void CSVDoc::View::closeEvent (QCloseEvent *event)
{
if (!mViewManager.closeRequest (this))
event->ignore();
}
void CSVDoc::View::setupFileMenu()
{
QMenu *file = menuBar()->addMenu (tr ("&File"));
QAction *new_ = new QAction (tr ("New"), this);
connect (new_, SIGNAL (triggered()), this, SIGNAL (newDocumentRequest()));
file->addAction (new_);
mSave = new QAction (tr ("&Save"), this);
connect (mSave, SIGNAL (triggered()), this, SLOT (save()));
file->addAction (mSave);
}
void CSVDoc::View::setupEditMenu()
{
QMenu *edit = menuBar()->addMenu (tr ("&Edit"));
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("&Undo"));
mUndo->setShortcuts (QKeySequence::Undo);
edit->addAction (mUndo);
mRedo= mDocument->getUndoStack().createRedoAction (this, tr("&Redo"));
mRedo->setShortcuts (QKeySequence::Redo);
edit->addAction (mRedo);
}
void CSVDoc::View::setupViewMenu()
{
QMenu *view = menuBar()->addMenu (tr ("&View"));
QAction *newWindow = new QAction (tr ("&New View"), this);
connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));
view->addAction (newWindow);
}
void CSVDoc::View::setupWorldMenu()
{
QMenu *world = menuBar()->addMenu (tr ("&World"));
QAction *globals = new QAction (tr ("Globals"), this);
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
world->addAction (globals);
mVerify = new QAction (tr ("&Verify"), this);
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
world->addAction (mVerify);
}
void CSVDoc::View::setupUi()
{
setupFileMenu();
setupEditMenu();
setupViewMenu();
setupWorldMenu();
}
void CSVDoc::View::updateTitle()
{
std::ostringstream stream;
stream << mDocument->getName();
if (mDocument->getState() & CSMDoc::State_Modified)
stream << " *";
if (mViewTotal>1)
stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]";
setWindowTitle (stream.str().c_str());
}
void CSVDoc::View::updateActions()
{
bool editing = !(mDocument->getState() & CSMDoc::State_Locked);
for (std::vector<QAction *>::iterator iter (mEditingActions.begin()); iter!=mEditingActions.end(); ++iter)
(*iter)->setEnabled (editing);
mUndo->setEnabled (editing & mDocument->getUndoStack().canUndo());
mRedo->setEnabled (editing & mDocument->getUndoStack().canRedo());
mSave->setEnabled (!(mDocument->getState() & CSMDoc::State_Saving));
mVerify->setEnabled (!(mDocument->getState() & CSMDoc::State_Verifying));
}
CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews)
: 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
mOperations = new Operations;
addDockWidget (Qt::BottomDockWidgetArea, mOperations);
updateTitle();
setupUi();
CSVWorld::addSubViewFactories (mSubViewFactory);
CSVTools::addSubViewFactories (mSubViewFactory);
}
CSVDoc::View::~View()
{
}
const CSMDoc::Document *CSVDoc::View::getDocument() const
{
return mDocument;
}
CSMDoc::Document *CSVDoc::View::getDocument()
{
return mDocument;
}
void CSVDoc::View::setIndex (int viewIndex, int totalViews)
{
mViewIndex = viewIndex;
mViewTotal = totalViews;
updateTitle();
}
void CSVDoc::View::updateDocumentState()
{
updateTitle();
updateActions();
static const int operations[] =
{
CSMDoc::State_Saving, CSMDoc::State_Verifying,
-1 // end marker
};
int state = mDocument->getState() ;
for (int i=0; operations[i]!=-1; ++i)
if (!(state & operations[i]))
mOperations->quitOperation (operations[i]);
QList<CSVDoc::SubView *> subViews = findChildren<CSVDoc::SubView *>();
for (QList<CSVDoc::SubView *>::iterator iter (subViews.begin()); iter!=subViews.end(); ++iter)
(*iter)->setEditLock (state & CSMDoc::State_Locked);
}
void CSVDoc::View::updateProgress (int current, int max, int type, int threads)
{
mOperations->setProgress (current, max, type, threads);
}
void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id)
{
/// \todo add an user setting for limiting the number of sub views per top level view. Automatically open a new top level view if this
/// number is exceeded
/// \todo if the sub view limit setting is one, the sub view title bar should be hidden and the text in the main title bar adjusted
/// accordingly
/// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis)
SubView *view = mSubViewFactory.makeSubView (id, *mDocument);
addDockWidget (Qt::TopDockWidgetArea, view);
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this,
SLOT (addSubView (const CSMWorld::UniversalId&)));
view->show();
}
void CSVDoc::View::newView()
{
mViewManager.addView (mDocument);
}
void CSVDoc::View::save()
{
mDocument->save();
}
void CSVDoc::View::verify()
{
addSubView (mDocument->verify());
}
void CSVDoc::View::addGlobalsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Globals);
}

@ -0,0 +1,103 @@
#ifndef CSV_DOC_VIEW_H
#define CSV_DOC_VIEW_H
#include <vector>
#include <map>
#include <QMainWindow>
#include "subviewfactory.hpp"
class QAction;
namespace CSMDoc
{
class Document;
}
namespace CSMWorld
{
class UniversalId;
}
namespace CSVDoc
{
class ViewManager;
class Operations;
class View : public QMainWindow
{
Q_OBJECT
ViewManager& mViewManager;
CSMDoc::Document *mDocument;
int mViewIndex;
int mViewTotal;
QAction *mUndo;
QAction *mRedo;
QAction *mSave;
QAction *mVerify;
std::vector<QAction *> mEditingActions;
Operations *mOperations;
SubViewFactoryManager mSubViewFactory;
// not implemented
View (const View&);
View& operator= (const View&);
private:
void closeEvent (QCloseEvent *event);
void setupFileMenu();
void setupEditMenu();
void setupViewMenu();
void setupWorldMenu();
void setupUi();
void updateTitle();
void updateActions();
public:
View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews);
///< The ownership of \a document is not transferred to *this.
virtual ~View();
const CSMDoc::Document *getDocument() const;
CSMDoc::Document *getDocument();
void setIndex (int viewIndex, int totalViews);
void updateDocumentState();
void updateProgress (int current, int max, int type, int threads);
signals:
void newDocumentRequest();
public slots:
void addSubView (const CSMWorld::UniversalId& id);
private slots:
void newView();
void save();
void verify();
void addGlobalsSubView();
};
}
#endif

@ -0,0 +1,112 @@
#include "viewmanager.hpp"
#include <map>
#include "../../model/doc/documentmanager.hpp"
#include "../../model/doc/document.hpp"
#include "view.hpp"
void CSVDoc::ViewManager::updateIndices()
{
std::map<CSMDoc::Document *, std::pair<int, int> > documents;
for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
{
std::map<CSMDoc::Document *, std::pair<int, int> >::iterator document = documents.find ((*iter)->getDocument());
if (document==documents.end())
document =
documents.insert (
std::make_pair ((*iter)->getDocument(), std::make_pair (0, countViews ((*iter)->getDocument())))).
first;
(*iter)->setIndex (document->second.first++, document->second.second);
}
}
CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
: mDocumentManager (documentManager)
{
}
CSVDoc::ViewManager::~ViewManager()
{
for (std::vector<View *>::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
delete *iter;
}
CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document)
{
if (countViews (document)==0)
{
// new document
connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)),
this, SLOT (documentStateChanged (int, CSMDoc::Document *)));
connect (document, SIGNAL (progress (int, int, int, int, CSMDoc::Document *)),
this, SLOT (progress (int, int, int, int, CSMDoc::Document *)));
}
View *view = new View (*this, document, countViews (document)+1);
mViews.push_back (view);
view->show();
connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest()));
updateIndices();
return view;
}
int CSVDoc::ViewManager::countViews (const CSMDoc::Document *document) const
{
int count = 0;
for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
if ((*iter)->getDocument()==document)
++count;
return count;
}
bool CSVDoc::ViewManager::closeRequest (View *view)
{
std::vector<View *>::iterator iter = std::find (mViews.begin(), mViews.end(), view);
if (iter!=mViews.end())
{
bool last = countViews (view->getDocument())<=1;
/// \todo check if save is in progress -> warn user about possible data loss
/// \todo check if document has not been saved -> return false and start close dialogue
mViews.erase (iter);
view->deleteLater();
if (last)
mDocumentManager.removeDocument (view->getDocument());
else
updateIndices();
}
return true;
}
void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document)
{
for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
if ((*iter)->getDocument()==document)
(*iter)->updateDocumentState();
}
void CSVDoc::ViewManager::progress (int current, int max, int type, int threads, CSMDoc::Document *document)
{
for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
if ((*iter)->getDocument()==document)
(*iter)->updateProgress (current, max, type, threads);
}

@ -0,0 +1,58 @@
#ifndef CSV_DOC_VIEWMANAGER_H
#define CSV_DOC_VIEWMANAGER_H
#include <vector>
#include <QObject>
namespace CSMDoc
{
class Document;
class DocumentManager;
}
namespace CSVDoc
{
class View;
class ViewManager : public QObject
{
Q_OBJECT
CSMDoc::DocumentManager& mDocumentManager;
std::vector<View *> mViews;
// not implemented
ViewManager (const ViewManager&);
ViewManager& operator= (const ViewManager&);
void updateIndices();
public:
ViewManager (CSMDoc::DocumentManager& documentManager);
virtual ~ViewManager();
View *addView (CSMDoc::Document *document);
///< The ownership of the returned view is not transferred.
int countViews (const CSMDoc::Document *document) const;
///< Return number of views for \a document.
bool closeRequest (View *view);
signals:
void newDocumentRequest();
private slots:
void documentStateChanged (int state, CSMDoc::Document *document);
void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
};
}
#endif

@ -0,0 +1,32 @@
#include "reportsubview.hpp"
#include <QTableView>
#include <QHeaderView>
#include "../../model/tools/reportmodel.hpp"
CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: CSVDoc::SubView (id), mModel (document.getReport (id))
{
setWidget (mTable = new QTableView (this));
mTable->setModel (mModel);
mTable->horizontalHeader()->setResizeMode (QHeaderView::Interactive);
mTable->verticalHeader()->hide();
mTable->setSortingEnabled (true);
mTable->setSelectionBehavior (QAbstractItemView::SelectRows);
mTable->setSelectionMode (QAbstractItemView::ExtendedSelection);
connect (mTable, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (show (const QModelIndex&)));
}
void CSVTools::ReportSubView::setEditLock (bool locked)
{
// ignored. We don't change document state anyway.
}
void CSVTools::ReportSubView::show (const QModelIndex& index)
{
focusId (mModel->getUniversalId (index.row()));
}

@ -0,0 +1,42 @@
#ifndef CSV_TOOLS_REPORTSUBVIEW_H
#define CSV_TOOLS_REPORTSUBVIEW_H
#include "../doc/subview.hpp"
class QTableView;
class QModelIndex;
namespace CSMDoc
{
class Document;
}
namespace CSMTools
{
class ReportModel;
}
namespace CSVTools
{
class Table;
class ReportSubView : public CSVDoc::SubView
{
Q_OBJECT
CSMTools::ReportModel *mModel;
QTableView *mTable;
public:
ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
virtual void setEditLock (bool locked);
private slots:
void show (const QModelIndex& index);
};
}
#endif

@ -0,0 +1,12 @@
#include "subviews.hpp"
#include "../doc/subviewfactoryimp.hpp"
#include "reportsubview.hpp"
void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{
manager.add (CSMWorld::UniversalId::Type_VerificationResults,
new CSVDoc::SubViewFactory<ReportSubView>);
}

@ -0,0 +1,14 @@
#ifndef CSV_TOOLS_SUBVIEWS_H
#define CSV_TOOLS_SUBVIEWS_H
namespace CSVDoc
{
class SubViewFactoryManager;
}
namespace CSVTools
{
void addSubViewFactories (CSVDoc::SubViewFactoryManager& manager);
}
#endif

@ -0,0 +1,40 @@
#include "dialoguesubview.hpp"
#include <QGridLayout>
#include <QLabel>
#include <QAbstractTableModel>
#include "../../model/world/columnbase.hpp"
CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
bool createAndDelete)
: SubView (id)
{
QWidget *widget = new QWidget (this);
setWidget (widget);
QGridLayout *layout = new QGridLayout;
widget->setLayout (layout);
QAbstractTableModel *model = document.getData().getTableModel (id);
int columns = model->columnCount();
for (int i=0; i<columns; ++i)
{
int flags = model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();
if (flags & CSMWorld::ColumnBase::Flag_Dialogue)
{
layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i, 0);
}
}
}
void CSVWorld::DialogueSubView::setEditLock (bool locked)
{
}

@ -0,0 +1,24 @@
#ifndef CSV_WORLD_DIALOGUESUBVIEW_H
#define CSV_WORLD_DIALOGUESUBVIEW_H
#include "../doc/subview.hpp"
namespace CSMDoc
{
class Document;
}
namespace CSVWorld
{
class DialogueSubView : public CSVDoc::SubView
{
public:
DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete);
virtual void setEditLock (bool locked);
};
}
#endif

@ -0,0 +1,16 @@
#include "subviews.hpp"
#include "../doc/subviewfactoryimp.hpp"
#include "tablesubview.hpp"
#include "dialoguesubview.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{
manager.add (CSMWorld::UniversalId::Type_Globals,
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (true));
manager.add (CSMWorld::UniversalId::Type_Global,
new CSVDoc::SubViewFactoryWithCreateFlag<DialogueSubView> (true));
}

@ -0,0 +1,14 @@
#ifndef CSV_WORLD_SUBVIEWS_H
#define CSV_WORLD_SUBVIEWS_H
namespace CSVDoc
{
class SubViewFactoryManager;
}
namespace CSVWorld
{
void addSubViewFactories (CSVDoc::SubViewFactoryManager& manager);
}
#endif

@ -0,0 +1,199 @@
#include "table.hpp"
#include <QHeaderView>
#include <QAction>
#include <QMenu>
#include <QContextMenuEvent>
#include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/idtableproxymodel.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/record.hpp"
#include "util.hpp"
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
QMenu menu (this);
/// \todo add menu items for select all and clear selection
if (!mEditLock)
{
if (mCreateAction)
menu.addAction (mCreateAction);
if (listRevertableSelectedIds().size()>0)
menu.addAction (mRevertAction);
if (listDeletableSelectedIds().size()>0)
menu.addAction (mDeleteAction);
}
menu.exec (event->globalPos());
}
std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector<std::string> revertableIds;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter)
{
std::string id = mProxyModel->data (*iter).toString().toStdString();
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt());
if (state!=CSMWorld::RecordBase::State_BaseOnly)
revertableIds.push_back (id);
}
return revertableIds;
}
std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector<std::string> deletableIds;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter)
{
std::string id = mProxyModel->data (*iter).toString().toStdString();
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt());
if (state!=CSMWorld::RecordBase::State_Deleted)
deletableIds.push_back (id);
}
return deletableIds;
}
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack,
bool createAndDelete)
: mUndoStack (undoStack), mCreateAction (0), mEditLock (false)
{
mModel = &dynamic_cast<CSMWorld::IdTable&> (*data.getTableModel (id));
mProxyModel = new CSMWorld::IdTableProxyModel (this);
mProxyModel->setSourceModel (mModel);
setModel (mProxyModel);
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
verticalHeader()->hide();
setSortingEnabled (true);
setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection);
int columns = mModel->columnCount();
for (int i=0; i<columns; ++i)
{
int flags = mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();
if (flags & CSMWorld::ColumnBase::Flag_Table)
{
CommandDelegate *delegate = new CommandDelegate (undoStack, this);
mDelegates.push_back (delegate);
setItemDelegateForColumn (i, delegate);
}
else
hideColumn (i);
}
/// \todo make initial layout fill the whole width of the table
if (createAndDelete)
{
mCreateAction = new QAction (tr ("Add Record"), this);
connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord()));
addAction (mCreateAction);
}
mRevertAction = new QAction (tr ("Revert Record"), this);
connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord()));
addAction (mRevertAction);
mDeleteAction = new QAction (tr ("Delete Record"), this);
connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord()));
addAction (mDeleteAction);
}
void CSVWorld::Table::setEditLock (bool locked)
{
for (std::vector<CommandDelegate *>::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter)
(*iter)->setEditLock (locked);
mEditLock = locked;
}
CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
{
return CSMWorld::UniversalId (
static_cast<CSMWorld::UniversalId::Type> (mProxyModel->data (mProxyModel->index (row, 2)).toInt()),
mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString());
}
#include <sstream> /// \todo remove
void CSVWorld::Table::createRecord()
{
if (!mEditLock)
{
/// \todo ask the user for an ID instead.
static int index = 0;
std::ostringstream stream;
stream << "id" << index++;
mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str()));
}
}
void CSVWorld::Table::revertRecord()
{
if (!mEditLock)
{
std::vector<std::string> revertableIds = listRevertableSelectedIds();
if (revertableIds.size()>0)
{
if (revertableIds.size()>1)
mUndoStack.beginMacro (tr ("Revert multiple records"));
for (std::vector<std::string>::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter)
mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter));
if (revertableIds.size()>1)
mUndoStack.endMacro();
}
}
}
void CSVWorld::Table::deleteRecord()
{
if (!mEditLock)
{
std::vector<std::string> deletableIds = listDeletableSelectedIds();
if (deletableIds.size()>0)
{
if (deletableIds.size()>1)
mUndoStack.beginMacro (tr ("Delete multiple records"));
for (std::vector<std::string>::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter)
mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter));
if (deletableIds.size()>1)
mUndoStack.endMacro();
}
}
}

@ -0,0 +1,65 @@
#ifndef CSV_WORLD_TABLE_H
#define CSV_WORLD_TABLE_H
#include <vector>
#include <string>
#include <QTableView>
class QUndoStack;
class QAction;
namespace CSMWorld
{
class Data;
class UniversalId;
class IdTableProxyModel;
class IdTable;
}
namespace CSVWorld
{
class CommandDelegate;
///< Table widget
class Table : public QTableView
{
Q_OBJECT
std::vector<CommandDelegate *> mDelegates;
QUndoStack& mUndoStack;
QAction *mCreateAction;
QAction *mRevertAction;
QAction *mDeleteAction;
CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTable *mModel;
bool mEditLock;
private:
void contextMenuEvent (QContextMenuEvent *event);
std::vector<std::string> listRevertableSelectedIds() const;
std::vector<std::string> listDeletableSelectedIds() const;
public:
Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete);
///< \param createAndDelete Allow creation and deletion of records.
void setEditLock (bool locked);
CSMWorld::UniversalId getUniversalId (int row) const;
private slots:
void createRecord();
void revertRecord();
void deleteRecord();
};
}
#endif

@ -0,0 +1,25 @@
#include "tablesubview.hpp"
#include "../../model/doc/document.hpp"
#include "table.hpp"
CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
bool createAndDelete)
: SubView (id)
{
setWidget (mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete));
connect (mTable, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (rowActivated (const QModelIndex&)));
}
void CSVWorld::TableSubView::setEditLock (bool locked)
{
mTable->setEditLock (locked);
}
void CSVWorld::TableSubView::rowActivated (const QModelIndex& index)
{
focusId (mTable->getUniversalId (index.row()));
}

@ -0,0 +1,35 @@
#ifndef CSV_WORLD_TABLESUBVIEW_H
#define CSV_WORLD_TABLESUBVIEW_H
#include "../doc/subview.hpp"
class QModelIndex;
namespace CSMDoc
{
class Document;
}
namespace CSVWorld
{
class Table;
class TableSubView : public CSVDoc::SubView
{
Q_OBJECT
Table *mTable;
public:
TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete);
virtual void setEditLock (bool locked);
private slots:
void rowActivated (const QModelIndex& index);
};
}
#endif

@ -0,0 +1,57 @@
#include "util.hpp"
#include <QUndoStack>
#include "../../model/world/commands.hpp"
CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model)
: mModel (model)
{}
int CSVWorld::NastyTableModelHack::rowCount (const QModelIndex & parent) const
{
return mModel.rowCount (parent);
}
int CSVWorld::NastyTableModelHack::columnCount (const QModelIndex & parent) const
{
return mModel.columnCount (parent);
}
QVariant CSVWorld::NastyTableModelHack::data (const QModelIndex & index, int role) const
{
return mModel.data (index, role);
}
bool CSVWorld::NastyTableModelHack::setData ( const QModelIndex &index, const QVariant &value, int role)
{
mData = value;
return true;
}
QVariant CSVWorld::NastyTableModelHack::getData() const
{
return mData;
}
CSVWorld::CommandDelegate::CommandDelegate (QUndoStack& undoStack, QObject *parent)
: QStyledItemDelegate (parent), mUndoStack (undoStack), mEditLock (false)
{}
void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model,
const QModelIndex& index) const
{
if (!mEditLock)
{
NastyTableModelHack hack (*model);
QStyledItemDelegate::setModelData (editor, &hack, index);
mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData()));
}
///< \todo provide some kind of feedback to the user, indicating that editing is currently not possible.
}
void CSVWorld::CommandDelegate::setEditLock (bool locked)
{
mEditLock = locked;
}

@ -0,0 +1,50 @@
#ifndef CSV_WORLD_UTIL_H
#define CSV_WORLD_UTIL_H
#include <QAbstractTableModel>
#include <QStyledItemDelegate>
class QUndoStack;
namespace CSVWorld
{
///< \brief Getting the data out of an editor widget
///
/// Really, Qt? Really?
class NastyTableModelHack : public QAbstractTableModel
{
QAbstractItemModel& mModel;
QVariant mData;
public:
NastyTableModelHack (QAbstractItemModel& model);
int rowCount (const QModelIndex & parent = QModelIndex()) const;
int columnCount (const QModelIndex & parent = QModelIndex()) const;
QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const;
bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
QVariant getData() const;
};
///< \brief Use commands instead of manipulating the model directly
class CommandDelegate : public QStyledItemDelegate
{
QUndoStack& mUndoStack;
bool mEditLock;
public:
CommandDelegate (QUndoStack& undoStack, QObject *parent);
void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const;
void setEditLock (bool locked);
};
}
#endif

@ -30,7 +30,7 @@ add_openmw_dir (mwgui
formatting inventorywindow container hud countdialog tradewindow settingswindow formatting inventorywindow container hud countdialog tradewindow settingswindow
confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu
itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog
enchantingdialog trainingwindow travelwindow enchantingdialog trainingwindow travelwindow imagebutton
) )
add_openmw_dir (mwdialogue add_openmw_dir (mwdialogue

@ -8,6 +8,7 @@
#include <components/bsa/bsa_archive.hpp> #include <components/bsa/bsa_archive.hpp>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <components/translation/translation.hpp>
#include <components/nifoverrides/nifoverrides.hpp> #include <components/nifoverrides/nifoverrides.hpp>
#include <components/nifbullet/bullet_nif_loader.hpp> #include <components/nifbullet/bullet_nif_loader.hpp>
@ -330,16 +331,23 @@ void OMW::Engine::go()
// cursor replacer (converts the cursor from the bsa so they can be used by mygui) // cursor replacer (converts the cursor from the bsa so they can be used by mygui)
MWGui::CursorReplace replacer; MWGui::CursorReplace replacer;
// Create encoder
ToUTF8::Utf8Encoder encoder (mEncoding);
// Create the world // Create the world
mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster,
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); mResDir, mCfgMgr.getCachePath(), mNewGame, &encoder, mFallbackMap));
//Load translation data
mTranslationDataStorage.setEncoder(&encoder);
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster);
// Create window manager - this manages all the MW-specific GUI windows // Create window manager - this manages all the MW-specific GUI windows
MWScript::registerExtensions (mExtensions); MWScript::registerExtensions (mExtensions);
mEnvironment.setWindowManager (new MWGui::WindowManager( mEnvironment.setWindowManager (new MWGui::WindowManager(
mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"),
mCfgMgr.getCachePath ().string(), mScriptConsoleMode)); mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage));
// Create sound system // Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound));
@ -356,7 +364,7 @@ void OMW::Engine::go()
// Create dialog system // Create dialog system
mEnvironment.setJournal (new MWDialogue::Journal); mEnvironment.setJournal (new MWDialogue::Journal);
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts)); mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage));
// Sets up the input system // Sets up the input system
mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, mEnvironment.setInputManager (new MWInput::InputManager (*mOgre,
@ -487,7 +495,7 @@ void OMW::Engine::showFPS(int level)
mFpsLevel = level; mFpsLevel = level;
} }
void OMW::Engine::setEncoding(const std::string& encoding) void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding)
{ {
mEncoding = encoding; mEncoding = encoding;
} }

@ -5,6 +5,7 @@
#include <components/compiler/extensions.hpp> #include <components/compiler/extensions.hpp>
#include <components/files/collections.hpp> #include <components/files/collections.hpp>
#include <components/translation/translation.hpp>
#include "mwbase/environment.hpp" #include "mwbase/environment.hpp"
@ -59,7 +60,7 @@ namespace OMW
class Engine : private Ogre::FrameListener class Engine : private Ogre::FrameListener
{ {
MWBase::Environment mEnvironment; MWBase::Environment mEnvironment;
std::string mEncoding; ToUTF8::FromType mEncoding;
Files::PathContainer mDataDirs; Files::PathContainer mDataDirs;
boost::filesystem::path mResDir; boost::filesystem::path mResDir;
OEngine::Render::OgreRenderer *mOgre; OEngine::Render::OgreRenderer *mOgre;
@ -79,9 +80,9 @@ namespace OMW
Compiler::Extensions mExtensions; Compiler::Extensions mExtensions;
Compiler::Context *mScriptContext; Compiler::Context *mScriptContext;
Files::Collections mFileCollections; Files::Collections mFileCollections;
bool mFSStrict; bool mFSStrict;
Translation::Storage mTranslationDataStorage;
// not implemented // not implemented
Engine (const Engine&); Engine (const Engine&);
@ -154,7 +155,7 @@ namespace OMW
void setCompileAll (bool all); void setCompileAll (bool all);
/// Font encoding /// Font encoding
void setEncoding(const std::string& encoding); void setEncoding(const ToUTF8::FromType& encoding);
void setAnimationVerbose(bool animverbose); void setAnimationVerbose(bool animverbose);

@ -181,21 +181,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
// Font encoding settings // Font encoding settings
std::string encoding(variables["encoding"].as<std::string>()); std::string encoding(variables["encoding"].as<std::string>());
if (encoding == "win1250") std::cout << ToUTF8::encodingUsingMessage(encoding) << std::endl;
{ engine.setEncoding(ToUTF8::calculateEncoding(encoding));
std::cout << "Using Central and Eastern European font encoding." << std::endl;
engine.setEncoding(encoding);
}
else if (encoding == "win1251")
{
std::cout << "Using Cyrillic font encoding." << std::endl;
engine.setEncoding(encoding);
}
else
{
std::cout << "Using default (English) font encoding." << std::endl;
engine.setEncoding("win1252");
}
// directory settings // directory settings
engine.enableFSStrict(variables["fs-strict"].as<bool>()); engine.enableFSStrict(variables["fs-strict"].as<bool>());

@ -7,6 +7,8 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/translation/translation.hpp>
#include "../mwmechanics/stat.hpp" #include "../mwmechanics/stat.hpp"
#include "../mwgui/mode.hpp" #include "../mwgui/mode.hpp"
@ -233,6 +235,8 @@ namespace MWBase
virtual void startSpellMaking(MWWorld::Ptr actor) = 0; virtual void startSpellMaking(MWWorld::Ptr actor) = 0;
virtual void startEnchanting(MWWorld::Ptr actor) = 0; virtual void startEnchanting(MWWorld::Ptr actor) = 0;
virtual void startTraining(MWWorld::Ptr actor) = 0; virtual void startTraining(MWWorld::Ptr actor) = 0;
virtual const Translation::Storage& getTranslationDataStorage() const = 0;
}; };
} }

@ -133,6 +133,10 @@ namespace MWBase
virtual char getGlobalVariableType (const std::string& name) const = 0; virtual char getGlobalVariableType (const std::string& name) const = 0;
///< Return ' ', if there is no global variable with this name. ///< Return ' ', if there is no global variable with this name.
virtual std::vector<std::string> getGlobals () const = 0;
virtual std::string getCurrentCellName() const = 0;
virtual MWWorld::Ptr getPtr (const std::string& name, bool activeOnly) = 0; virtual MWWorld::Ptr getPtr (const std::string& name, bool activeOnly) = 0;
///< Return a pointer to a liveCellRef with the given name. ///< Return a pointer to a liveCellRef with the given name.

@ -204,33 +204,10 @@ namespace MWClass
std::string text; std::string text;
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
if (ref->mRef.mTeleport) if (ref->mRef.mTeleport)
{ {
std::string dest;
if (ref->mRef.mDestCell != "")
{
// door leads to an interior, use interior name as tooltip
dest = ref->mRef.mDestCell;
}
else
{
// door leads to exterior, use cell name (if any), otherwise translated region name
int x,y;
MWBase::Environment::get().getWorld()->positionToIndex (ref->mRef.mDoorDest.pos[0], ref->mRef.mDoorDest.pos[1], x, y);
const ESM::Cell* cell = store.get<ESM::Cell>().find(x,y);
if (cell->mName != "")
dest = cell->mName;
else
{
const ESM::Region* region =
store.get<ESM::Region>().find(cell->mRegion);
dest = region->mName;
}
}
text += "\n#{sTo}"; text += "\n#{sTo}";
text += "\n"+dest; text += "\n" + getDestination(*ref);
} }
if (ref->mRef.mLockLevel > 0) if (ref->mRef.mLockLevel > 0)
@ -246,6 +223,37 @@ namespace MWClass
return info; return info;
} }
std::string Door::getDestination (const MWWorld::LiveCellRef<ESM::Door>& door)
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
std::string dest;
if (door.mRef.mDestCell != "")
{
// door leads to an interior, use interior name as tooltip
dest = door.mRef.mDestCell;
}
else
{
// door leads to exterior, use cell name (if any), otherwise translated region name
int x,y;
MWBase::Environment::get().getWorld()->positionToIndex (door.mRef.mDoorDest.pos[0], door.mRef.mDoorDest.pos[1], x, y);
const ESM::Cell* cell = store.get<ESM::Cell>().find(x,y);
if (cell->mName != "")
dest = cell->mName;
else
{
const ESM::Region* region =
store.get<ESM::Region>().find(cell->mRegion);
//name as is, not a token
return region->mName;
}
}
return "#{sCell=" + dest + "}";
}
MWWorld::Ptr MWWorld::Ptr
Door::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const Door::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
{ {

@ -31,6 +31,9 @@ namespace MWClass
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
static std::string getDestination (const MWWorld::LiveCellRef<ESM::Door>& door);
///< @return destination cell name or token
virtual void lock (const MWWorld::Ptr& ptr, int lockLevel) const; virtual void lock (const MWWorld::Ptr& ptr, int lockLevel) const;
///< Lock object ///< Lock object

@ -16,6 +16,7 @@
#include <components/compiler/scriptparser.hpp> #include <components/compiler/scriptparser.hpp>
#include <components/interpreter/interpreter.hpp> #include <components/interpreter/interpreter.hpp>
#include <components/interpreter/defines.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -39,47 +40,14 @@
#include "filter.hpp" #include "filter.hpp"
namespace
{
std::string toLower (const std::string& name)
{
std::string lowerCase;
std::transform (name.begin(), name.end(), std::back_inserter (lowerCase),
(int(*)(int)) std::tolower);
return lowerCase;
}
bool stringCompareNoCase (std::string first, std::string second)
{
unsigned int i=0;
while ( (i<first.length()) && (i<second.length()) )
{
if (tolower(first[i])<tolower(second[i])) return true;
else if (tolower(first[i])>tolower(second[i])) return false;
++i;
}
if (first.length()<second.length())
return true;
else
return false;
}
//helper function
std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos)
{
return toLower(str).find(toLower(substr),pos);
}
}
namespace MWDialogue namespace MWDialogue
{ {
DialogueManager::DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose) : DialogueManager::DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage) :
mCompilerContext (MWScript::CompilerContext::Type_Dialgoue), mCompilerContext (MWScript::CompilerContext::Type_Dialgoue),
mErrorStream(std::cout.rdbuf()),mErrorHandler(mErrorStream) mErrorStream(std::cout.rdbuf()),mErrorHandler(mErrorStream)
, mTemporaryDispositionChange(0.f) , mTemporaryDispositionChange(0.f)
, mPermanentDispositionChange(0.f), mScriptVerbose (scriptVerbose) , mPermanentDispositionChange(0.f), mScriptVerbose (scriptVerbose)
, mTranslationDataStorage(translationDataStorage)
{ {
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
@ -93,26 +61,55 @@ namespace MWDialogue
MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin(); MWWorld::Store<ESM::Dialogue>::iterator it = dialogs.begin();
for (; it != dialogs.end(); ++it) for (; it != dialogs.end(); ++it)
{ {
mDialogueMap[toLower(it->mId)] = *it; mDialogueMap[Misc::StringUtils::lowerCase(it->mId)] = *it;
} }
} }
void DialogueManager::addTopic (const std::string& topic) void DialogueManager::addTopic (const std::string& topic)
{ {
mKnownTopics[toLower(topic)] = true; mKnownTopics[Misc::StringUtils::lowerCase(topic)] = true;
} }
void DialogueManager::parseText (const std::string& text) void DialogueManager::parseText (const std::string& text)
{ {
std::list<std::string>::iterator it; std::vector<HyperTextToken> hypertext = ParseHyperText(text);
for(it = mActorKnownTopics.begin();it != mActorKnownTopics.end();++it)
//calculation of standard form fir all hyperlinks
for (size_t i = 0; i < hypertext.size(); ++i)
{
if (hypertext[i].mLink)
{
size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText);
for(; asterisk_count > 0; --asterisk_count)
hypertext[i].mText.append("*");
hypertext[i].mText = mTranslationDataStorage.topicStandardForm(hypertext[i].mText);
}
}
for (size_t i = 0; i < hypertext.size(); ++i)
{ {
size_t pos = find_str_ci(text,*it,0); std::list<std::string>::iterator it;
if(pos !=std::string::npos) for(it = mActorKnownTopics.begin(); it != mActorKnownTopics.end(); ++it)
{ {
mKnownTopics[*it] = true; if (hypertext[i].mLink)
{
if( hypertext[i].mText == *it )
{
mKnownTopics[hypertext[i].mText] = true;
}
}
else if( !mTranslationDataStorage.hasTranslation() )
{
size_t pos = Misc::StringUtils::lowerCase(hypertext[i].mText).find(*it, 0);
if(pos !=std::string::npos)
{
mKnownTopics[*it] = true;
}
}
} }
} }
updateTopics(); updateTopics();
} }
@ -155,7 +152,9 @@ namespace MWDialogue
} }
parseText (info->mResponse); parseText (info->mResponse);
win->addText (info->mResponse);
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript); executeScript (info->mResultScript);
mLastTopic = it->mId; mLastTopic = it->mId;
mLastDialogue = *info; mLastDialogue = *info;
@ -267,7 +266,8 @@ namespace MWDialogue
else else
win->addTitle (topic); win->addTitle (topic);
win->addText (info->mResponse); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript); executeScript (info->mResultScript);
@ -294,10 +294,11 @@ namespace MWDialogue
{ {
if (filter.search (*iter)) if (filter.search (*iter))
{ {
mActorKnownTopics.push_back (toLower (iter->mId)); std::string lower = Misc::StringUtils::lowerCase(iter->mId);
mActorKnownTopics.push_back (lower);
//does the player know the topic? //does the player know the topic?
if (mKnownTopics.find (toLower (iter->mId)) != mKnownTopics.end()) if (mKnownTopics.find (lower) != mKnownTopics.end())
{ {
keywordList.push_back (iter->mId); keywordList.push_back (iter->mId);
} }
@ -355,7 +356,7 @@ namespace MWDialogue
win->setServices (windowServices); win->setServices (windowServices);
// sort again, because the previous sort was case-sensitive // sort again, because the previous sort was case-sensitive
keywordList.sort(stringCompareNoCase); keywordList.sort(Misc::StringUtils::ciEqual);
win->setKeywords(keywordList); win->setKeywords(keywordList);
mChoice = choice; mChoice = choice;
@ -411,7 +412,9 @@ namespace MWDialogue
mIsInChoice = false; mIsInChoice = false;
std::string text = info->mResponse; std::string text = info->mResponse;
parseText (text); parseText (text);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (text);
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext));
executeScript (info->mResultScript); executeScript (info->mResultScript);
mLastTopic = mLastTopic; mLastTopic = mLastTopic;
mLastDialogue = *info; mLastDialogue = *info;
@ -433,7 +436,7 @@ namespace MWDialogue
{ {
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->askQuestion(question); win->askQuestion(question);
mChoiceMap[toLower(question)] = choice; mChoiceMap[Misc::StringUtils::lowerCase(question)] = choice;
mIsInChoice = true; mIsInChoice = true;
} }
@ -493,4 +496,57 @@ namespace MWDialogue
{ {
mTemporaryDispositionChange += delta; mTemporaryDispositionChange += delta;
} }
std::vector<HyperTextToken> ParseHyperText(const std::string& text)
{
std::vector<HyperTextToken> result;
MyGUI::UString utext(text);
size_t pos_begin, pos_end, iteration_pos = 0;
for(;;)
{
pos_begin = utext.find('@', iteration_pos);
if (pos_begin != std::string::npos)
pos_end = utext.find('#', pos_begin);
if (pos_begin != std::string::npos && pos_end != std::string::npos)
{
result.push_back( HyperTextToken(utext.substr(iteration_pos, pos_begin - iteration_pos), false) );
std::string link = utext.substr(pos_begin + 1, pos_end - pos_begin - 1);
result.push_back( HyperTextToken(link, true) );
iteration_pos = pos_end + 1;
}
else
{
result.push_back( HyperTextToken(utext.substr(iteration_pos), false) );
break;
}
}
return result;
}
size_t RemovePseudoAsterisks(std::string& phrase)
{
size_t pseudoAsterisksCount = 0;
const char specialPseudoAsteriskCharacter = 127;
if( !phrase.empty() )
{
std::string::reverse_iterator rit = phrase.rbegin();
while( rit != phrase.rend() && *rit == specialPseudoAsteriskCharacter )
{
pseudoAsterisksCount++;
++rit;
}
}
phrase = phrase.substr(0, phrase.length() - pseudoAsterisksCount);
return pseudoAsterisksCount;
}
} }

@ -7,6 +7,7 @@
#include <list> #include <list>
#include <components/compiler/streamerrorhandler.hpp> #include <components/compiler/streamerrorhandler.hpp>
#include <components/translation/translation.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -20,6 +21,7 @@ namespace MWDialogue
std::map<std::string, bool> mKnownTopics;// Those are the topics the player knows. std::map<std::string, bool> mKnownTopics;// Those are the topics the player knows.
std::list<std::string> mActorKnownTopics; std::list<std::string> mActorKnownTopics;
Translation::Storage& mTranslationDataStorage;
MWScript::CompilerContext mCompilerContext; MWScript::CompilerContext mCompilerContext;
std::ostream mErrorStream; std::ostream mErrorStream;
Compiler::StreamErrorHandler mErrorHandler; Compiler::StreamErrorHandler mErrorHandler;
@ -50,7 +52,7 @@ namespace MWDialogue
public: public:
DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose); DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage);
virtual void startDialogue (const MWWorld::Ptr& actor); virtual void startDialogue (const MWWorld::Ptr& actor);
@ -72,6 +74,21 @@ namespace MWDialogue
virtual int getTemporaryDispositionChange () const; virtual int getTemporaryDispositionChange () const;
virtual void applyTemporaryDispositionChange (int delta); virtual void applyTemporaryDispositionChange (int delta);
}; };
struct HyperTextToken
{
HyperTextToken(const std::string& text, bool link) : mText(text), mLink(link) {}
std::string mText;
bool mLink;
};
// In translations (at least Russian) the links are marked with @#, so
// it should be a function to parse it
std::vector<HyperTextToken> ParseHyperText(const std::string& text);
size_t RemovePseudoAsterisks(std::string& phrase);
} }
#endif #endif

@ -88,7 +88,7 @@ void BookWindow::setTakeButtonShow(bool show)
mTakeButton->setVisible(show); mTakeButton->setVisible(show);
} }
void BookWindow::onCloseButtonClicked (MyGUI::Widget* _sender) void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender)
{ {
// no 3d sounds because the object could be in a container. // no 3d sounds because the object could be in a container.
MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0);
@ -96,7 +96,7 @@ void BookWindow::onCloseButtonClicked (MyGUI::Widget* _sender)
mWindowManager.removeGuiMode(GM_Book); mWindowManager.removeGuiMode(GM_Book);
} }
void BookWindow::onTakeButtonClicked (MyGUI::Widget* _sender) void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender)
{ {
MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack);
@ -106,7 +106,7 @@ void BookWindow::onTakeButtonClicked (MyGUI::Widget* _sender)
mWindowManager.removeGuiMode(GM_Book); mWindowManager.removeGuiMode(GM_Book);
} }
void BookWindow::onNextPageButtonClicked (MyGUI::Widget* _sender) void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender)
{ {
if ((mCurrentPage+1)*2 < mPages.size()) if ((mCurrentPage+1)*2 < mPages.size())
{ {
@ -118,7 +118,7 @@ void BookWindow::onNextPageButtonClicked (MyGUI::Widget* _sender)
} }
} }
void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* _sender) void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender)
{ {
if (mCurrentPage > 0) if (mCurrentPage > 0)
{ {

@ -5,6 +5,8 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "imagebutton.hpp"
namespace MWGui namespace MWGui
{ {
class BookWindow : public WindowBase class BookWindow : public WindowBase
@ -16,19 +18,19 @@ namespace MWGui
void setTakeButtonShow(bool show); void setTakeButtonShow(bool show);
protected: protected:
void onNextPageButtonClicked (MyGUI::Widget* _sender); void onNextPageButtonClicked (MyGUI::Widget* sender);
void onPrevPageButtonClicked (MyGUI::Widget* _sender); void onPrevPageButtonClicked (MyGUI::Widget* sender);
void onCloseButtonClicked (MyGUI::Widget* _sender); void onCloseButtonClicked (MyGUI::Widget* sender);
void onTakeButtonClicked (MyGUI::Widget* _sender); void onTakeButtonClicked (MyGUI::Widget* sender);
void updatePages(); void updatePages();
void clearPages(); void clearPages();
private: private:
MyGUI::Button* mCloseButton; MWGui::ImageButton* mCloseButton;
MyGUI::Button* mTakeButton; MWGui::ImageButton* mTakeButton;
MyGUI::Button* mNextPageButton; MWGui::ImageButton* mNextPageButton;
MyGUI::Button* mPrevPageButton; MWGui::ImageButton* mPrevPageButton;
MyGUI::TextBox* mLeftPageNumber; MyGUI::TextBox* mLeftPageNumber;
MyGUI::TextBox* mRightPageNumber; MyGUI::TextBox* mRightPageNumber;
MyGUI::Widget* mLeftPage; MyGUI::Widget* mLeftPage;

@ -195,13 +195,13 @@ void ContainerBase::sellAlreadyBoughtItem(MyGUI::Widget* _sender, int count)
if (isInventory()) if (isInventory())
{ {
MWBase::Environment::get().getWindowManager()->getTradeWindow()->addItem(object, count); MWBase::Environment::get().getWindowManager()->getTradeWindow()->addItem(object, count);
MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count); MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, true);
MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems();
} }
else else
{ {
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addItem(object, count); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addItem(object, count);
MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count); MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, true);
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems();
} }
@ -218,13 +218,13 @@ void ContainerBase::sellItem(MyGUI::Widget* _sender, int count)
if (isInventory()) if (isInventory())
{ {
MWBase::Environment::get().getWindowManager()->getTradeWindow()->addBarteredItem(object, count); MWBase::Environment::get().getWindowManager()->getTradeWindow()->addBarteredItem(object, count);
MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count); MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, false);
MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems();
} }
else else
{ {
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addBarteredItem(object, count); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addBarteredItem(object, count);
MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count); MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, false);
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems();
} }

@ -2,7 +2,6 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <openengine/ogre/imagerotate.hpp> #include <openengine/ogre/imagerotate.hpp>
#include <openengine/ogre/atlas.hpp>
#include <OgreResourceGroupManager.h> #include <OgreResourceGroupManager.h>
#include <OgreRoot.h> #include <OgreRoot.h>
@ -14,7 +13,4 @@ CursorReplace::CursorReplace()
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90); OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90);
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45); OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45);
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45); OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45);
OEngine::Render::Atlas::createFromFile("atlas1.cfg", "mwgui1", "textures\\");
OEngine::Render::Atlas::createFromFile("mainmenu.cfg", "mwgui2", "textures\\");
} }

@ -16,6 +16,8 @@
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwdialogue/dialoguemanagerimp.hpp"
#include "dialogue_history.hpp" #include "dialogue_history.hpp"
#include "widgets.hpp" #include "widgets.hpp"
#include "list.hpp" #include "list.hpp"
@ -52,7 +54,6 @@ bool sortByLength (const std::string& left, const std::string& right)
{ {
return left.size() > right.size(); return left.size() > right.size();
} }
} }
@ -180,9 +181,30 @@ void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
if(color != "#B29154") if(color != "#B29154")
{ {
MyGUI::UString key = mHistory->getColorTextAt(cursorPosition); MyGUI::UString key = mHistory->getColorTextAt(cursorPosition);
if(color == "#686EBA") MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key));
if(color == "#572D21") MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); if(color == "#686EBA")
{
std::map<size_t, HyperLink>::iterator i = mHyperLinks.upper_bound(cursorPosition);
if( !mHyperLinks.empty() )
{
--i;
if( i->first + i->second.mLength > cursorPosition)
{
MWBase::Environment::get().getDialogueManager()->keywordSelected(i->second.mTrueValue);
}
}
else
{
// the link was colored, but it is not in mHyperLinks.
// It means that those liunks are not marked with @# and found
// by topic name search
MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key));
}
}
if(color == "#572D21")
MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key));
} }
} }
@ -258,6 +280,7 @@ void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName)
setTitle(npcName); setTitle(npcName);
mTopicsList->clear(); mTopicsList->clear();
mHyperLinks.clear();
mHistory->setCaption(""); mHistory->setCaption("");
updateOptions(); updateOptions();
} }
@ -340,7 +363,7 @@ void addColorInString(std::string& str, const std::string& keyword,std::string c
} }
} }
std::string DialogueWindow::parseText(std::string text) std::string DialogueWindow::parseText(const std::string& text)
{ {
bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored) bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored)
@ -358,11 +381,56 @@ std::string DialogueWindow::parseText(std::string text)
// sort by length to make sure longer topics are replaced first // sort by length to make sure longer topics are replaced first
std::sort(topics.begin(), topics.end(), sortByLength); std::sort(topics.begin(), topics.end(), sortByLength);
for(std::vector<std::string>::const_iterator it = topics.begin(); it != topics.end(); ++it) std::vector<MWDialogue::HyperTextToken> hypertext = MWDialogue::ParseHyperText(text);
size_t historySize = 0;
if(mHistory->getClient()->getSubWidgetText() != nullptr)
{ {
addColorInString(text,*it,"#686EBA","#B29154"); historySize = mHistory->getOnlyText().size();
} }
return text;
std::string result;
size_t hypertextPos = 0;
for (size_t i = 0; i < hypertext.size(); ++i)
{
if (hypertext[i].mLink)
{
size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText);
std::string standardForm = hypertext[i].mText;
for(; asterisk_count > 0; --asterisk_count)
standardForm.append("*");
standardForm =
MWBase::Environment::get().getWindowManager()->
getTranslationDataStorage().topicStandardForm(standardForm);
if( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() )
{
result.append("#686EBA").append(hypertext[i].mText).append("#B29154");
mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length();
mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm);
}
else
result += hypertext[i].mText;
}
else
{
if( !mWindowManager.getTranslationDataStorage().hasTranslation() )
{
for(std::vector<std::string>::const_iterator it = topics.begin(); it != topics.end(); ++it)
{
addColorInString(hypertext[i].mText, *it, "#686EBA", "#B29154");
}
}
result += hypertext[i].mText;
}
hypertextPos += MyGUI::UString(hypertext[i].mText).length();
}
return result;
} }
void DialogueWindow::addText(std::string text) void DialogueWindow::addText(std::string text)
@ -370,6 +438,11 @@ void DialogueWindow::addText(std::string text)
mHistory->addDialogText("#B29154"+parseText(text)+"#B29154"); mHistory->addDialogText("#B29154"+parseText(text)+"#B29154");
} }
void DialogueWindow::addMessageBox(const std::string& text)
{
mHistory->addDialogText("\n#FFFFFF"+text+"#B29154");
}
void DialogueWindow::addTitle(std::string text) void DialogueWindow::addTitle(std::string text)
{ {
// This is called from the dialogue manager, so text is // This is called from the dialogue manager, so text is
@ -394,6 +467,7 @@ void DialogueWindow::updateOptions()
{ {
//Clear the list of topics //Clear the list of topics
mTopicsList->clear(); mTopicsList->clear();
mHyperLinks.clear();
mHistory->eraseText(0, mHistory->getTextLength()); mHistory->eraseText(0, mHistory->getTextLength());
if (mPtr.getTypeName() == typeid(ESM::NPC).name()) if (mPtr.getTypeName() == typeid(ESM::NPC).name())

@ -65,6 +65,7 @@ namespace MWGui
void setKeywords(std::list<std::string> keyWord); void setKeywords(std::list<std::string> keyWord);
void removeKeyword(std::string keyWord); void removeKeyword(std::string keyWord);
void addText(std::string text); void addText(std::string text);
void addMessageBox(const std::string& text);
void addTitle(std::string text); void addTitle(std::string text);
void askQuestion(std::string question); void askQuestion(std::string question);
void goodbye(); void goodbye();
@ -92,12 +93,18 @@ namespace MWGui
virtual void onReferenceUnavailable(); virtual void onReferenceUnavailable();
struct HyperLink
{
size_t mLength;
std::string mTrueValue;
};
private: private:
void updateOptions(); void updateOptions();
/** /**
*Helper function that add topic keyword in blue in a text. *Helper function that add topic keyword in blue in a text.
*/ */
std::string parseText(std::string text); std::string parseText(const std::string& text);
int mServices; int mServices;
@ -109,6 +116,8 @@ namespace MWGui
MyGUI::EditPtr mDispositionText; MyGUI::EditPtr mDispositionText;
PersuasionDialog mPersuasionDialog; PersuasionDialog mPersuasionDialog;
std::map<size_t, HyperLink> mHyperLinks;
}; };
} }
#endif #endif

@ -1,5 +1,10 @@
#include "formatting.hpp" #include "formatting.hpp"
#include <components/interpreter/defines.hpp>
#include "../mwscript/interpretercontext.hpp"
#include "../mwworld/ptr.hpp"
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@ -68,6 +73,9 @@ std::vector<std::string> BookTextParser::split(std::string text, const int width
{ {
std::vector<std::string> result; std::vector<std::string> result;
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
text = Interpreter::fixDefinesBook(text, interpreterContext);
boost::algorithm::replace_all(text, "<BR>", "\n"); boost::algorithm::replace_all(text, "<BR>", "\n");
boost::algorithm::replace_all(text, "<P>", "\n\n"); boost::algorithm::replace_all(text, "<P>", "\n\n");
@ -167,6 +175,10 @@ std::vector<std::string> BookTextParser::split(std::string text, const int width
MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width) MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width)
{ {
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
text = Interpreter::fixDefinesBook(text, interpreterContext);
mParent = parent; mParent = parent;
mWidth = width; mWidth = width;
mHeight = 0; mHeight = 0;

@ -319,7 +319,7 @@ void HUD::setCellName(const std::string& cellName)
mCellNameTimer = 5.0f; mCellNameTimer = 5.0f;
mCellName = cellName; mCellName = cellName;
mCellNameBox->setCaption(mCellName); mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}");
mCellNameBox->setVisible(mMapVisible); mCellNameBox->setVisible(mMapVisible);
} }
} }

@ -0,0 +1,63 @@
#include "imagebutton.hpp"
#include <OgreTextureManager.h>
namespace MWGui
{
void ImageButton::setPropertyOverride(const std::string &_key, const std::string &_value)
{
if (_key == "ImageHighlighted")
mImageHighlighted = _value;
else if (_key == "ImagePushed")
mImagePushed = _value;
else if (_key == "ImageNormal")
{
if (mImageNormal == "")
{
setImageTexture(_value);
}
mImageNormal = _value;
}
else
ImageBox::setPropertyOverride(_key, _value);
}
void ImageButton::onMouseSetFocus(Widget* _old)
{
setImageTexture(mImageHighlighted);
ImageBox::onMouseSetFocus(_old);
}
void ImageButton::onMouseLostFocus(Widget* _new)
{
setImageTexture(mImageNormal);
ImageBox::onMouseLostFocus(_new);
}
void ImageButton::onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id)
{
if (_id == MyGUI::MouseButton::Left)
setImageTexture(mImagePushed);
ImageBox::onMouseButtonPressed(_left, _top, _id);
}
MyGUI::IntSize ImageButton::getRequestedSize()
{
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName(mImageNormal);
if (texture.isNull())
{
std::cerr << "ImageButton: can't find " << mImageNormal << std::endl;
return MyGUI::IntSize(0,0);
}
return MyGUI::IntSize (texture->getWidth(), texture->getHeight());
}
void ImageButton::onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id)
{
if (_id == MyGUI::MouseButton::Left)
setImageTexture(mImageHighlighted);
ImageBox::onMouseButtonReleased(_left, _top, _id);
}
}

@ -0,0 +1,33 @@
#ifndef MWGUI_IMAGEBUTTON_H
#define MWGUI_IMAGEBUTTON_H
#include "MyGUI_ImageBox.h"
namespace MWGui
{
/**
* @brief allows using different image textures depending on the button state
*/
class ImageButton : public MyGUI::ImageBox
{
MYGUI_RTTI_DERIVED(ImageButton)
public:
MyGUI::IntSize getRequestedSize();
protected:
virtual void setPropertyOverride(const std::string& _key, const std::string& _value);
virtual void onMouseLostFocus(MyGUI::Widget* _new);
virtual void onMouseSetFocus(MyGUI::Widget* _old);
virtual void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id);
virtual void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id);
std::string mImageHighlighted;
std::string mImageNormal;
std::string mImagePushed;
};
}
#endif

@ -24,19 +24,6 @@
#include "scrollwindow.hpp" #include "scrollwindow.hpp"
#include "spellwindow.hpp" #include "spellwindow.hpp"
namespace
{
std::string toLower (const std::string& name)
{
std::string lowerCase;
std::transform (name.begin(), name.end(), std::back_inserter (lowerCase),
(int(*)(int)) std::tolower);
return lowerCase;
}
}
namespace MWGui namespace MWGui
{ {
@ -284,7 +271,7 @@ namespace MWGui
for (MWWorld::ContainerStoreIterator it = invStore.begin(); for (MWWorld::ContainerStoreIterator it = invStore.begin();
it != invStore.end(); ++it) it != invStore.end(); ++it)
{ {
if (toLower(it->getCellRef().mRefID) == "gold_001") if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001"))
return it->getRefData().getCount(); return it->getRefData().getCount();
} }
return 0; return 0;

@ -83,10 +83,9 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize)
MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_journal.layout", parWindowManager) : WindowBase("openmw_journal.layout", parWindowManager)
, mLastPos(0)
, mVisible(false)
, mPageNumber(0) , mPageNumber(0)
{ {
mMainWidget->setVisible(false);
//setCoord(0,0,498, 342); //setCoord(0,0,498, 342);
center(); center();
@ -115,20 +114,15 @@ MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager)
//displayLeftText(list.front()); //displayLeftText(list.front());
} }
void MWGui::JournalWindow::setVisible(bool visible) void MWGui::JournalWindow::close()
{ {
if (mVisible && !visible) MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0);
MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0);
mVisible = visible;
mMainWidget->setVisible(visible);
} }
void MWGui::JournalWindow::open() void MWGui::JournalWindow::open()
{ {
mPageNumber = 0; mPageNumber = 0;
std::string journalOpenSound = "book open"; MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
MWBase::Environment::get().getSoundManager()->playSound (journalOpenSound, 1.0, 1.0);
if(MWBase::Environment::get().getJournal()->begin()!=MWBase::Environment::get().getJournal()->end()) if(MWBase::Environment::get().getJournal()->begin()!=MWBase::Environment::get().getJournal()->end())
{ {
book journal; book journal;
@ -182,7 +176,7 @@ void MWGui::JournalWindow::displayRightText(std::string text)
} }
void MWGui::JournalWindow::notifyNextPage(MyGUI::WidgetPtr _sender) void MWGui::JournalWindow::notifyNextPage(MyGUI::Widget* _sender)
{ {
if(mPageNumber < int(mLeftPages.size())-1) if(mPageNumber < int(mLeftPages.size())-1)
{ {
@ -194,7 +188,7 @@ void MWGui::JournalWindow::notifyNextPage(MyGUI::WidgetPtr _sender)
} }
} }
void MWGui::JournalWindow::notifyPrevPage(MyGUI::WidgetPtr _sender) void MWGui::JournalWindow::notifyPrevPage(MyGUI::Widget* _sender)
{ {
if(mPageNumber > 0) if(mPageNumber > 0)
{ {

@ -7,6 +7,7 @@
#include <utility> #include <utility>
#include "window_base.hpp" #include "window_base.hpp"
#include "imagebutton.hpp"
namespace MWGui namespace MWGui
{ {
@ -15,8 +16,7 @@ namespace MWGui
public: public:
JournalWindow(MWBase::WindowManager& parWindowManager); JournalWindow(MWBase::WindowManager& parWindowManager);
virtual void open(); virtual void open();
virtual void close();
virtual void setVisible(bool visible); // only used to play close sound
private: private:
void displayLeftText(std::string text); void displayLeftText(std::string text);
@ -26,22 +26,16 @@ namespace MWGui
/** /**
*Called when next/prev button is used. *Called when next/prev button is used.
*/ */
void notifyNextPage(MyGUI::WidgetPtr _sender); void notifyNextPage(MyGUI::Widget* _sender);
void notifyPrevPage(MyGUI::WidgetPtr _sender); void notifyPrevPage(MyGUI::Widget* _sender);
static const int sLineHeight;
MyGUI::WidgetPtr mSkillAreaWidget, mSkillClientWidget;
MyGUI::ScrollBar* mSkillScrollerWidget;
int mLastPos, mClientHeight;
MyGUI::EditPtr mLeftTextWidget; MyGUI::EditPtr mLeftTextWidget;
MyGUI::EditPtr mRightTextWidget; MyGUI::EditPtr mRightTextWidget;
MyGUI::ButtonPtr mPrevBtn; MWGui::ImageButton* mPrevBtn;
MyGUI::ButtonPtr mNextBtn; MWGui::ImageButton* mNextBtn;
std::vector<std::string> mLeftPages; std::vector<std::string> mLeftPages;
std::vector<std::string> mRightPages; std::vector<std::string> mRightPages;
int mPageNumber; //store the number of the current left page int mPageNumber; //store the number of the current left page
bool mVisible;
}; };
} }

@ -213,22 +213,26 @@ namespace MWGui
void LoadingScreen::changeWallpaper () void LoadingScreen::changeWallpaper ()
{ {
std::vector<std::string> splash; if (mResources.isNull ())
Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false);
for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it)
{ {
if (it->size() < 6) mResources = Ogre::StringVectorPtr (new Ogre::StringVector);
continue;
std::string start = it->substr(0, 6); Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false);
boost::to_lower(start); for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it)
{
if (it->size() < 6)
continue;
std::string start = it->substr(0, 6);
boost::to_lower(start);
if (start == "splash") if (start == "splash")
splash.push_back (*it); mResources->push_back (*it);
}
} }
if (splash.size())
if (mResources->size())
{ {
std::string randomSplash = splash[rand() % splash.size()]; std::string randomSplash = mResources->at (rand() % mResources->size());
Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General");
mBackgroundImage->setImageTexture (randomSplash); mBackgroundImage->setImageTexture (randomSplash);

@ -42,6 +42,7 @@ namespace MWGui
Ogre::Rectangle2D* mRectangle; Ogre::Rectangle2D* mRectangle;
Ogre::MaterialPtr mBackgroundMaterial; Ogre::MaterialPtr mBackgroundMaterial;
Ogre::StringVectorPtr mResources;
bool mLoadingOn; bool mLoadingOn;

@ -20,65 +20,57 @@ namespace MWGui
{ {
setCoord(0,0,w,h); setCoord(0,0,w,h);
int height = 64 * 3;
if (mButtonBox) if (mButtonBox)
MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); MyGUI::Gui::getInstance ().destroyWidget(mButtonBox);
mButtonBox = mMainWidget->createWidget<MyGUI::Widget>("", MyGUI::IntCoord(w/2 - 64, h/2 - height/2, 128, height), MyGUI::Align::Default); mButtonBox = mMainWidget->createWidget<MyGUI::Widget>("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);
int curH = 0; int curH = 0;
mReturn = mButtonBox->createWidget<MyGUI::Button> ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); std::vector<std::string> buttons;
mReturn->setImageResource ("Menu_Return"); buttons.push_back("return");
mReturn->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::returnToGame); //buttons.push_back("newgame");
curH += 64; //buttons.push_back("loadgame");
//buttons.push_back("savegame");
buttons.push_back("options");
/* //buttons.push_back("credits");
mNewGame = mButtonBox->createWidget<MyGUI::Button> ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); buttons.push_back("exitgame");
mNewGame->setImageResource ("Menu_NewGame");
curH += 64; int maxwidth = 0;
mLoadGame = mButtonBox->createWidget<MyGUI::Button> ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); mButtons.clear();
mLoadGame->setImageResource ("Menu_LoadGame"); for (std::vector<std::string>::iterator it = buttons.begin(); it != buttons.end(); ++it)
curH += 64; {
MWGui::ImageButton* button = mButtonBox->createWidget<MWGui::ImageButton>
("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default);
mSaveGame = mButtonBox->createWidget<MyGUI::Button> ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds");
mSaveGame->setImageResource ("Menu_SaveGame"); button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds");
curH += 64; button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds");
*/ MyGUI::IntSize requested = button->getRequestedSize();
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked);
mOptions = mButtonBox->createWidget<MyGUI::Button> ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); mButtons[*it] = button;
mOptions->setImageResource ("Menu_Options"); curH += requested.height;
mOptions->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::showOptions);
curH += 64; if (requested.width > maxwidth)
maxwidth = requested.width;
/* }
mCredits = mButtonBox->createWidget<MyGUI::Button> ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); for (std::map<std::string, MWGui::ImageButton*>::iterator it = mButtons.begin(); it != mButtons.end(); ++it)
mCredits->setImageResource ("Menu_Credits"); {
curH += 64; MyGUI::IntSize requested = it->second->getRequestedSize();
*/ it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height);
}
mExitGame = mButtonBox->createWidget<MyGUI::Button> ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default);
mExitGame->setImageResource ("Menu_ExitGame"); mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH);
mExitGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::exitGame);
curH += 64;
}
void MainMenu::returnToGame(MyGUI::Widget* sender)
{
MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu);
}
void MainMenu::showOptions(MyGUI::Widget* sender)
{
MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings);
} }
void MainMenu::exitGame(MyGUI::Widget* sender) void MainMenu::onButtonClicked(MyGUI::Widget *sender)
{ {
Ogre::Root::getSingleton ().queueEndRendering (); if (sender == mButtons["return"])
MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu);
else if (sender == mButtons["options"])
MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings);
else if (sender == mButtons["exitgame"])
Ogre::Root::getSingleton ().queueEndRendering ();
} }
} }

@ -1,5 +1,7 @@
#include <openengine/gui/layout.hpp> #include <openengine/gui/layout.hpp>
#include "imagebutton.hpp"
namespace MWGui namespace MWGui
{ {
@ -11,19 +13,11 @@ namespace MWGui
void onResChange(int w, int h); void onResChange(int w, int h);
private: private:
MyGUI::Button* mReturn;
MyGUI::Button* mNewGame;
MyGUI::Button* mLoadGame;
MyGUI::Button* mSaveGame;
MyGUI::Button* mOptions;
MyGUI::Button* mCredits;
MyGUI::Button* mExitGame;
MyGUI::Widget* mButtonBox; MyGUI::Widget* mButtonBox;
void returnToGame(MyGUI::Widget* sender); std::map<std::string, MWGui::ImageButton*> mButtons;
void showOptions(MyGUI::Widget* sender);
void exitGame(MyGUI::Widget* sender); void onButtonClicked (MyGUI::Widget* sender);
}; };
} }

@ -299,7 +299,7 @@ MapWindow::~MapWindow()
void MapWindow::setCellName(const std::string& cellName) void MapWindow::setCellName(const std::string& cellName)
{ {
setTitle(cellName); setTitle("#{sCell=" + cellName + "}");
} }
void MapWindow::addVisitedLocation(const std::string& name, int x, int y) void MapWindow::addVisitedLocation(const std::string& name, int x, int y)

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

Loading…
Cancel
Save