forked from mirror/openmw-tes3mp
commit
7ecf020fcb
@ -0,0 +1,39 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- next
|
||||
before_install:
|
||||
- pwd
|
||||
- git submodule update --init --recursive
|
||||
- echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
|
||||
- echo "yes" | sudo apt-add-repository ppa:openmw/deps
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock libzzip-dev
|
||||
- sudo apt-get install -qq libqt4-dev libxaw7-dev libxrandr-dev libfreeimage-dev libpng-dev
|
||||
- sudo apt-get install -qq libopenal-dev libmpg123-dev libsndfile1-dev
|
||||
- sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev
|
||||
- sudo apt-get install -qq libbullet-dev libogre-static-dev libmygui-static-dev libsdl2-static-dev libunshield-dev
|
||||
- sudo mkdir /usr/src/gtest/build
|
||||
- cd /usr/src/gtest/build
|
||||
- sudo cmake .. -DBUILD_SHARED_LIBS=1
|
||||
- sudo make -j4
|
||||
- sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
|
||||
- sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
|
||||
before_script:
|
||||
- cd -
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DOGRE_STATIC=1 -DMYGUI_STATIC=1 -DBOOST_STATIC=1 -DSDL2_STATIC=1 -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1
|
||||
script:
|
||||
- make -j4
|
||||
after_script:
|
||||
- ./openmw_test_suite
|
||||
notifications:
|
||||
recipients:
|
||||
- lgromanowski+travis.ci@gmail.com
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
@ -0,0 +1,6 @@
|
||||
#include "textslotmsgbox.hpp"
|
||||
|
||||
void TextSlotMsgBox::setTextSlot(const QString& string)
|
||||
{
|
||||
setText(string);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#ifndef TEXT_SLOT_MSG_BOX
|
||||
#define TEXT_SLOT_MSG_BOX
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
class TextSlotMsgBox : public QMessageBox
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void setTextSlot(const QString& string);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,493 @@
|
||||
#include "unshieldthread.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
static bool make_sure_directory_exists(bfs::path directory)
|
||||
{
|
||||
|
||||
if(!bfs::exists(directory))
|
||||
{
|
||||
bfs::create_directories(directory);
|
||||
}
|
||||
|
||||
return bfs::exists(directory);
|
||||
}
|
||||
|
||||
void fill_path(bfs::path& path, const std::string& name)
|
||||
{
|
||||
size_t start = 0;
|
||||
|
||||
size_t i;
|
||||
for(i = 0; i < name.length(); i++)
|
||||
{
|
||||
switch(name[i])
|
||||
{
|
||||
case '\\':
|
||||
path /= name.substr(start, i-start);
|
||||
start = i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
path /= name.substr(start, i-start);
|
||||
}
|
||||
|
||||
std::string get_setting(const std::string& category, const std::string& setting, const std::string& inx)
|
||||
{
|
||||
size_t start = inx.find(category);
|
||||
start = inx.find(setting, start) + setting.length() + 3;
|
||||
|
||||
size_t end = inx.find("!", start);
|
||||
|
||||
return inx.substr(start, end-start);
|
||||
}
|
||||
|
||||
std::string read_to_string(const bfs::path& path)
|
||||
{
|
||||
std::ifstream strstream(path.c_str(), std::ios::in | std::ios::binary);
|
||||
std::string str;
|
||||
|
||||
strstream.seekg(0, std::ios::end);
|
||||
str.resize(strstream.tellg());
|
||||
strstream.seekg(0, std::ios::beg);
|
||||
strstream.read(&str[0], str.size());
|
||||
strstream.close();
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void add_setting(const std::string& category, const std::string& setting, const std::string& val, std::string& ini)
|
||||
{
|
||||
size_t loc;
|
||||
loc = ini.find("[" + category + "]");
|
||||
|
||||
// If category is not found, create it
|
||||
if(loc == std::string::npos)
|
||||
{
|
||||
loc = ini.size() + 2;
|
||||
ini += ("\r\n[" + category + "]\r\n");
|
||||
}
|
||||
|
||||
loc += category.length() +2 +2;
|
||||
ini.insert(loc, setting + "=" + val + "\r\n");
|
||||
}
|
||||
|
||||
void bloodmoon_fix_ini(std::string& ini, const bfs::path inxPath)
|
||||
{
|
||||
std::string inx = read_to_string(inxPath);
|
||||
|
||||
// Remove this one setting (the only one actually changed by bloodmoon, as opposed to just adding new ones)
|
||||
size_t start = ini.find("[Weather Blight]");
|
||||
start = ini.find("Ambient Loop Sound ID", start);
|
||||
size_t end = ini.find("\r\n", start) +2;
|
||||
ini.erase(start, end-start);
|
||||
|
||||
std::string category;
|
||||
std::string setting;
|
||||
|
||||
category = "General";
|
||||
{
|
||||
setting = "Werewolf FOV"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Moons";
|
||||
{
|
||||
setting = "Script Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Weather";
|
||||
{
|
||||
setting = "Snow Ripples"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Ripple Radius"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Ripples Per Flake"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Ripple Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Ripple Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Gravity Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow High Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Low Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Weather Blight";
|
||||
{
|
||||
setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Weather Snow";
|
||||
{
|
||||
setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Diameter"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Height Min"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Height Max"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Snow Entrance Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Max Snowflakes"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
category = "Weather Blizzard";
|
||||
{
|
||||
setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
setting = "Storm Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fix_ini(const bfs::path& output_dir, bfs::path cdPath, bool tribunal, bool bloodmoon)
|
||||
{
|
||||
bfs::path ini_path = output_dir;
|
||||
ini_path /= "Morrowind.ini";
|
||||
|
||||
std::string ini = read_to_string(ini_path.string());
|
||||
|
||||
if(tribunal)
|
||||
{
|
||||
add_setting("Game Files", "GameFile1", "Tribunal.esm", ini);
|
||||
add_setting("Archives", "Archive 0", "Tribunal.bsa", ini);
|
||||
}
|
||||
if(bloodmoon)
|
||||
{
|
||||
bloodmoon_fix_ini(ini, cdPath / "setup.inx");
|
||||
add_setting("Game Files", "GameFile2", "Bloodmoon.esm", ini);
|
||||
add_setting("Archives", "Archive 1", "Bloodmoon.bsa", ini);
|
||||
}
|
||||
|
||||
std::ofstream inistream(ini_path.c_str());
|
||||
inistream << ini;
|
||||
inistream.close();
|
||||
}
|
||||
|
||||
void installToPath(const bfs::path& from, const bfs::path& to, bool copy = false)
|
||||
{
|
||||
make_sure_directory_exists(to);
|
||||
|
||||
for ( bfs::directory_iterator end, dir(from); dir != end; ++dir )
|
||||
{
|
||||
if(bfs::is_directory(dir->path()))
|
||||
installToPath(dir->path(), to / dir->path().filename(), copy);
|
||||
else
|
||||
{
|
||||
if(copy)
|
||||
{
|
||||
bfs::path dest = to / dir->path().filename();
|
||||
if(bfs::exists(dest))
|
||||
bfs::remove_all(dest);
|
||||
bfs::copy_file(dir->path(), dest);
|
||||
}
|
||||
else
|
||||
bfs::rename(dir->path(), to / dir->path().filename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bfs::path findFile(const bfs::path& in, std::string filename, bool recursive = true)
|
||||
{
|
||||
if(recursive)
|
||||
{
|
||||
for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir )
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
|
||||
return dir->path();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( bfs::directory_iterator end, dir(in); dir != end; ++dir )
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
|
||||
return dir->path();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool contains(const bfs::path& in, std::string filename)
|
||||
{
|
||||
for(bfs::directory_iterator end, dir(in); dir != end; ++dir)
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t getTime(const char* time)
|
||||
{
|
||||
struct tm tms;
|
||||
memset(&tms, 0, sizeof(struct tm));
|
||||
strptime(time, "%d %B %Y", &tms);
|
||||
return mktime(&tms);
|
||||
}
|
||||
}
|
||||
|
||||
bool UnshieldThread::SetMorrowindPath(const std::string& path)
|
||||
{
|
||||
mMorrowindPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnshieldThread::SetTribunalPath(const std::string& path)
|
||||
{
|
||||
mTribunalPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnshieldThread::SetBloodmoonPath(const std::string& path)
|
||||
{
|
||||
mBloodmoonPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnshieldThread::SetOutputPath(const std::string& path)
|
||||
{
|
||||
mOutputPath = path;
|
||||
}
|
||||
|
||||
bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index)
|
||||
{
|
||||
bool success;
|
||||
bfs::path dirname;
|
||||
bfs::path filename;
|
||||
int directory = unshield_file_directory(unshield, index);
|
||||
|
||||
dirname = output_dir;
|
||||
|
||||
if (prefix && prefix[0])
|
||||
dirname /= prefix;
|
||||
|
||||
if (directory >= 0)
|
||||
{
|
||||
const char* tmp = unshield_directory_name(unshield, directory);
|
||||
if (tmp && tmp[0])
|
||||
fill_path(dirname, tmp);
|
||||
}
|
||||
|
||||
make_sure_directory_exists(dirname);
|
||||
|
||||
filename = dirname;
|
||||
filename /= unshield_file_name(unshield, index);
|
||||
|
||||
emit signalGUI(QString("Extracting: ") + QString(filename.c_str()));
|
||||
|
||||
success = unshield_file_save(unshield, index, filename.c_str());
|
||||
|
||||
if (!success)
|
||||
bfs::remove(filename);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini)
|
||||
{
|
||||
Unshield * unshield;
|
||||
unshield = unshield_open(cab.c_str());
|
||||
|
||||
int i;
|
||||
for (i = 0; i < unshield_file_group_count(unshield); i++)
|
||||
{
|
||||
UnshieldFileGroup* file_group = unshield_file_group_get(unshield, i);
|
||||
|
||||
for (size_t j = file_group->first_file; j <= file_group->last_file; j++)
|
||||
{
|
||||
if (unshield_file_is_valid(unshield, j))
|
||||
extract_file(unshield, output_dir, file_group->name, j);
|
||||
}
|
||||
}
|
||||
unshield_close(unshield);
|
||||
}
|
||||
|
||||
|
||||
bool UnshieldThread::extract()
|
||||
{
|
||||
bfs::path outputDataFilesDir = mOutputPath;
|
||||
outputDataFilesDir /= "Data Files";
|
||||
bfs::path extractPath = mOutputPath;
|
||||
extractPath /= "extract-temp";
|
||||
|
||||
if(!mMorrowindDone && mMorrowindPath.string().length() > 0)
|
||||
{
|
||||
mMorrowindDone = true;
|
||||
|
||||
bfs::path mwExtractPath = extractPath / "morrowind";
|
||||
extract_cab(mMorrowindPath, mwExtractPath, true);
|
||||
|
||||
bfs::path dFilesDir = findFile(mwExtractPath, "morrowind.esm").parent_path();
|
||||
|
||||
installToPath(dFilesDir, outputDataFilesDir);
|
||||
|
||||
// Videos are often kept uncompressed on the cd
|
||||
bfs::path videosPath = findFile(mMorrowindPath.parent_path(), "video", false);
|
||||
if(videosPath.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Videos..."));
|
||||
installToPath(videosPath, outputDataFilesDir / "Video", true);
|
||||
}
|
||||
|
||||
bfs::path cdDFiles = findFile(mMorrowindPath.parent_path(), "data files", false);
|
||||
if(cdDFiles.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Uncompressed Data files from CD..."));
|
||||
installToPath(cdDFiles, outputDataFilesDir, true);
|
||||
}
|
||||
|
||||
|
||||
bfs::rename(findFile(mwExtractPath, "morrowind.ini"), outputDataFilesDir / "Morrowind.ini");
|
||||
|
||||
mTribunalDone = contains(outputDataFilesDir, "tribunal.esm");
|
||||
mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm");
|
||||
|
||||
}
|
||||
|
||||
else if(!mTribunalDone && mTribunalPath.string().length() > 0)
|
||||
{
|
||||
mTribunalDone = true;
|
||||
|
||||
bfs::path tbExtractPath = extractPath / "tribunal";
|
||||
extract_cab(mTribunalPath, tbExtractPath, true);
|
||||
|
||||
bfs::path dFilesDir = findFile(tbExtractPath, "tribunal.esm").parent_path();
|
||||
|
||||
installToPath(dFilesDir, outputDataFilesDir);
|
||||
|
||||
// Mt GOTY CD has Sounds in a seperate folder from the rest of the data files
|
||||
bfs::path soundsPath = findFile(tbExtractPath, "sounds", false);
|
||||
if(soundsPath.string() != "")
|
||||
installToPath(soundsPath, outputDataFilesDir / "Sounds");
|
||||
|
||||
bfs::path cdDFiles = findFile(mTribunalPath.parent_path(), "data files", false);
|
||||
if(cdDFiles.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Uncompressed Data files from CD..."));
|
||||
installToPath(cdDFiles, outputDataFilesDir, true);
|
||||
}
|
||||
|
||||
mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm");
|
||||
|
||||
fix_ini(outputDataFilesDir, bfs::path(mTribunalPath).parent_path(), mTribunalDone, mBloodmoonDone);
|
||||
}
|
||||
|
||||
else if(!mBloodmoonDone && mBloodmoonPath.string().length() > 0)
|
||||
{
|
||||
mBloodmoonDone = true;
|
||||
|
||||
bfs::path bmExtractPath = extractPath / "bloodmoon";
|
||||
extract_cab(mBloodmoonPath, bmExtractPath, true);
|
||||
|
||||
bfs::path dFilesDir = findFile(bmExtractPath, "bloodmoon.esm").parent_path();
|
||||
|
||||
installToPath(dFilesDir, outputDataFilesDir);
|
||||
|
||||
// My GOTY CD contains a folder within cab files called Tribunal patch,
|
||||
// which contains Tribunal.esm
|
||||
bfs::path tbPatchPath = findFile(bmExtractPath, "tribunal.esm");
|
||||
if(tbPatchPath.string() != "")
|
||||
bfs::rename(tbPatchPath, outputDataFilesDir / "Tribunal.esm");
|
||||
|
||||
bfs::path cdDFiles = findFile(mBloodmoonPath.parent_path(), "data files", false);
|
||||
if(cdDFiles.string() != "")
|
||||
{
|
||||
emit signalGUI(QString("Installing Uncompressed Data files from CD..."));
|
||||
installToPath(cdDFiles, outputDataFilesDir, true);
|
||||
}
|
||||
|
||||
fix_ini(outputDataFilesDir, bfs::path(mBloodmoonPath).parent_path(), false, mBloodmoonDone);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnshieldThread::Done()
|
||||
{
|
||||
// Get rid of unnecessary files
|
||||
bfs::remove_all(mOutputPath / "extract-temp");
|
||||
|
||||
// Set modified time to release dates, to preserve load order
|
||||
if(mMorrowindDone)
|
||||
bfs::last_write_time(findFile(mOutputPath, "morrowind.esm"), getTime("1 May 2002"));
|
||||
|
||||
if(mTribunalDone)
|
||||
bfs::last_write_time(findFile(mOutputPath, "tribunal.esm"), getTime("6 November 2002"));
|
||||
|
||||
if(mBloodmoonDone)
|
||||
bfs::last_write_time(findFile(mOutputPath, "bloodmoon.esm"), getTime("3 June 2003"));
|
||||
}
|
||||
|
||||
std::string UnshieldThread::GetMWEsmPath()
|
||||
{
|
||||
return findFile(mOutputPath / "Data Files", "morrowind.esm").string();
|
||||
}
|
||||
|
||||
bool UnshieldThread::TribunalDone()
|
||||
{
|
||||
return mTribunalDone;
|
||||
}
|
||||
|
||||
bool UnshieldThread::BloodmoonDone()
|
||||
{
|
||||
return mBloodmoonDone;
|
||||
}
|
||||
|
||||
void UnshieldThread::run()
|
||||
{
|
||||
extract();
|
||||
emit close();
|
||||
}
|
||||
|
||||
UnshieldThread::UnshieldThread()
|
||||
{
|
||||
unshield_set_log_level(0);
|
||||
mMorrowindDone = false;
|
||||
mTribunalDone = false;
|
||||
mBloodmoonDone = false;
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
#ifndef UNSHIELD_THREAD_H
|
||||
#define UNSHIELD_THREAD_H
|
||||
|
||||
#include <QThread>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <libunshield.h>
|
||||
|
||||
class UnshieldThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
bool SetMorrowindPath(const std::string& path);
|
||||
bool SetTribunalPath(const std::string& path);
|
||||
bool SetBloodmoonPath(const std::string& path);
|
||||
|
||||
void SetOutputPath(const std::string& path);
|
||||
|
||||
bool extract();
|
||||
|
||||
bool TribunalDone();
|
||||
bool BloodmoonDone();
|
||||
|
||||
void Done();
|
||||
|
||||
std::string GetMWEsmPath();
|
||||
|
||||
UnshieldThread();
|
||||
|
||||
private:
|
||||
|
||||
void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false);
|
||||
bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index);
|
||||
|
||||
boost::filesystem::path mMorrowindPath;
|
||||
boost::filesystem::path mTribunalPath;
|
||||
boost::filesystem::path mBloodmoonPath;
|
||||
|
||||
bool mMorrowindDone;
|
||||
bool mTribunalDone;
|
||||
bool mBloodmoonDone;
|
||||
|
||||
boost::filesystem::path mOutputPath;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void signalGUI(QString);
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,20 @@
|
||||
|
||||
#include "andnode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
CSMFilter::AndNode::AndNode (const std::vector<boost::shared_ptr<Node> >& nodes)
|
||||
: NAryNode (nodes, "and")
|
||||
{}
|
||||
|
||||
bool CSMFilter::AndNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
int size = getSize();
|
||||
|
||||
for (int i=0; i<size; ++i)
|
||||
if (!(*this)[i].test (table, row, columns))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
#ifndef CSM_FILTER_ANDNODE_H
|
||||
#define CSM_FILTER_ANDNODE_H
|
||||
|
||||
#include "narynode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class AndNode : public NAryNode
|
||||
{
|
||||
bool mAnd;
|
||||
|
||||
public:
|
||||
|
||||
AndNode (const std::vector<boost::shared_ptr<Node> >& nodes);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,15 @@
|
||||
|
||||
#include "booleannode.hpp"
|
||||
|
||||
CSMFilter::BooleanNode::BooleanNode (bool true_) : mTrue (true_) {}
|
||||
|
||||
bool CSMFilter::BooleanNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
return mTrue;
|
||||
}
|
||||
|
||||
std::string CSMFilter::BooleanNode::toString (bool numericColumns) const
|
||||
{
|
||||
return mTrue ? "true" : "false";
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef CSM_FILTER_BOOLEANNODE_H
|
||||
#define CSM_FILTER_BOOLEANNODE_H
|
||||
|
||||
#include "leafnode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class BooleanNode : public LeafNode
|
||||
{
|
||||
bool mTrue;
|
||||
|
||||
public:
|
||||
|
||||
BooleanNode (bool true_);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,25 @@
|
||||
#ifndef CSM_FILTER_FILTER_H
|
||||
#define CSM_FILTER_FILTER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/filter.hpp>
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
/// \brief Wrapper for Filter record
|
||||
struct Filter : public ESM::Filter
|
||||
{
|
||||
enum Scope
|
||||
{
|
||||
Scope_Project = 0, // per project
|
||||
Scope_Session = 1, // exists only for one editing session; not saved
|
||||
Scope_Content = 2 // embedded in the edited content file
|
||||
};
|
||||
|
||||
Scope mScope;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
|
||||
#include "leafnode.hpp"
|
||||
|
||||
std::vector<int> CSMFilter::LeafNode::getReferencedColumns() const
|
||||
{
|
||||
return std::vector<int>();
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
#ifndef CSM_FILTER_LEAFNODE_H
|
||||
#define CSM_FILTER_LEAFNODE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class LeafNode : public Node
|
||||
{
|
||||
public:
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,60 @@
|
||||
|
||||
#include "narynode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
CSMFilter::NAryNode::NAryNode (const std::vector<boost::shared_ptr<Node> >& nodes,
|
||||
const std::string& name)
|
||||
: mNodes (nodes), mName (name)
|
||||
{}
|
||||
|
||||
int CSMFilter::NAryNode::getSize() const
|
||||
{
|
||||
return mNodes.size();
|
||||
}
|
||||
|
||||
const CSMFilter::Node& CSMFilter::NAryNode::operator[] (int index) const
|
||||
{
|
||||
return *mNodes.at (index);
|
||||
}
|
||||
|
||||
std::vector<int> CSMFilter::NAryNode::getReferencedColumns() const
|
||||
{
|
||||
std::vector<int> columns;
|
||||
|
||||
for (std::vector<boost::shared_ptr<Node> >::const_iterator iter (mNodes.begin());
|
||||
iter!=mNodes.end(); ++iter)
|
||||
{
|
||||
std::vector<int> columns2 = (*iter)->getReferencedColumns();
|
||||
|
||||
columns.insert (columns.end(), columns2.begin(), columns2.end());
|
||||
}
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
std::string CSMFilter::NAryNode::toString (bool numericColumns) const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << mName << " (";
|
||||
|
||||
bool first = true;
|
||||
int size = getSize();
|
||||
|
||||
for (int i=0; i<size; ++i)
|
||||
{
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
stream << ", ";
|
||||
|
||||
stream << (*this)[i].toString (numericColumns);
|
||||
}
|
||||
|
||||
stream << ")";
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
#ifndef CSM_FILTER_NARYNODE_H
|
||||
#define CSM_FILTER_NARYNODE_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class NAryNode : public Node
|
||||
{
|
||||
std::vector<boost::shared_ptr<Node> > mNodes;
|
||||
std::string mName;
|
||||
|
||||
public:
|
||||
|
||||
NAryNode (const std::vector<boost::shared_ptr<Node> >& nodes, const std::string& name);
|
||||
|
||||
int getSize() const;
|
||||
|
||||
const Node& operator[] (int index) const;
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,6 @@
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
CSMFilter::Node::Node() {}
|
||||
|
||||
CSMFilter::Node::~Node() {}
|
@ -0,0 +1,53 @@
|
||||
#ifndef CSM_FILTER_NODE_H
|
||||
#define CSM_FILTER_NODE_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class IdTable;
|
||||
}
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
/// \brief Root class for the filter node hierarchy
|
||||
///
|
||||
/// \note When the function documentation for this class mentions "this node", this should be
|
||||
/// interpreted as "the node and all its children".
|
||||
class Node
|
||||
{
|
||||
// not implemented
|
||||
Node (const Node&);
|
||||
Node& operator= (const Node&);
|
||||
|
||||
public:
|
||||
|
||||
Node();
|
||||
|
||||
virtual ~Node();
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const = 0;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const = 0;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const = 0;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE (boost::shared_ptr<CSMFilter::Node>)
|
||||
|
||||
#endif
|
@ -0,0 +1,10 @@
|
||||
|
||||
#include "notnode.hpp"
|
||||
|
||||
CSMFilter::NotNode::NotNode (boost::shared_ptr<Node> child) : UnaryNode (child, "not") {}
|
||||
|
||||
bool CSMFilter::NotNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
return !getChild().test (table, row, columns);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
#ifndef CSM_FILTER_NOTNODE_H
|
||||
#define CSM_FILTER_NOTNODE_H
|
||||
|
||||
#include "unarynode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class NotNode : public UnaryNode
|
||||
{
|
||||
public:
|
||||
|
||||
NotNode (boost::shared_ptr<Node> child);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,20 @@
|
||||
|
||||
#include "ornode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
CSMFilter::OrNode::OrNode (const std::vector<boost::shared_ptr<Node> >& nodes)
|
||||
: NAryNode (nodes, "or")
|
||||
{}
|
||||
|
||||
bool CSMFilter::OrNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
int size = getSize();
|
||||
|
||||
for (int i=0; i<size; ++i)
|
||||
if ((*this)[i].test (table, row, columns))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
#ifndef CSM_FILTER_ORNODE_H
|
||||
#define CSM_FILTER_ORNODE_H
|
||||
|
||||
#include "narynode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class OrNode : public NAryNode
|
||||
{
|
||||
bool mAnd;
|
||||
|
||||
public:
|
||||
|
||||
OrNode (const std::vector<boost::shared_ptr<Node> >& nodes);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,617 @@
|
||||
|
||||
#include "parser.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "../world/columns.hpp"
|
||||
#include "../world/data.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "booleannode.hpp"
|
||||
#include "ornode.hpp"
|
||||
#include "andnode.hpp"
|
||||
#include "notnode.hpp"
|
||||
#include "textnode.hpp"
|
||||
#include "valuenode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
struct Token
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Type_EOS,
|
||||
Type_None,
|
||||
Type_String,
|
||||
Type_Number,
|
||||
Type_Open,
|
||||
Type_Close,
|
||||
Type_OpenSquare,
|
||||
Type_CloseSquare,
|
||||
Type_Comma,
|
||||
Type_OneShot,
|
||||
Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously.
|
||||
Type_Keyword_False,
|
||||
Type_Keyword_And,
|
||||
Type_Keyword_Or,
|
||||
Type_Keyword_Not,
|
||||
Type_Keyword_Text,
|
||||
Type_Keyword_Value
|
||||
};
|
||||
|
||||
Type mType;
|
||||
std::string mString;
|
||||
double mNumber;
|
||||
|
||||
Token (Type type = Type_None);
|
||||
|
||||
Token (Type type, const std::string& string);
|
||||
///< Non-string type that can also be interpreted as a string.
|
||||
|
||||
Token (const std::string& string);
|
||||
|
||||
Token (double number);
|
||||
|
||||
operator bool() const;
|
||||
|
||||
bool isString() const;
|
||||
};
|
||||
|
||||
Token::Token (Type type) : mType (type) {}
|
||||
|
||||
Token::Token (Type type, const std::string& string) : mType (type), mString (string) {}
|
||||
|
||||
Token::Token (const std::string& string) : mType (Type_String), mString (string) {}
|
||||
|
||||
Token::Token (double number) : mType (Type_Number), mNumber (number) {}
|
||||
|
||||
bool Token::isString() const
|
||||
{
|
||||
return mType==Type_String || mType>=Type_Keyword_True;
|
||||
}
|
||||
|
||||
Token::operator bool() const
|
||||
{
|
||||
return mType!=Type_None;
|
||||
}
|
||||
|
||||
bool operator== (const Token& left, const Token& right)
|
||||
{
|
||||
if (left.mType!=right.mType)
|
||||
return false;
|
||||
|
||||
switch (left.mType)
|
||||
{
|
||||
case Token::Type_String: return left.mString==right.mString;
|
||||
case Token::Type_Number: return left.mNumber==right.mNumber;
|
||||
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CSMFilter::Token CSMFilter::Parser::getStringToken()
|
||||
{
|
||||
std::string string;
|
||||
|
||||
int size = static_cast<int> (mInput.size());
|
||||
|
||||
for (; mIndex<size; ++mIndex)
|
||||
{
|
||||
char c = mInput[mIndex];
|
||||
|
||||
if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' ||
|
||||
(!string.empty() && string[0]=='"'))
|
||||
string += c;
|
||||
else
|
||||
break;
|
||||
|
||||
if (c=='"' && string.size()>1)
|
||||
{
|
||||
++mIndex;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (!string.empty())
|
||||
{
|
||||
if (string[0]=='"' && (string[string.size()-1]!='"' || string.size()<2) )
|
||||
{
|
||||
error();
|
||||
return Token (Token::Type_None);
|
||||
}
|
||||
|
||||
if (string[0]!='"' && string[string.size()-1]=='"')
|
||||
{
|
||||
error();
|
||||
return Token (Token::Type_None);
|
||||
}
|
||||
|
||||
if (string[0]=='"')
|
||||
return string.substr (1, string.size()-2);
|
||||
}
|
||||
|
||||
return checkKeywords (string);
|
||||
}
|
||||
|
||||
CSMFilter::Token CSMFilter::Parser::getNumberToken()
|
||||
{
|
||||
std::string string;
|
||||
|
||||
int size = static_cast<int> (mInput.size());
|
||||
|
||||
bool hasDecimalPoint = false;
|
||||
bool hasDigit = false;
|
||||
|
||||
for (; mIndex<size; ++mIndex)
|
||||
{
|
||||
char c = mInput[mIndex];
|
||||
|
||||
if (std::isdigit (c))
|
||||
{
|
||||
string += c;
|
||||
hasDigit = true;
|
||||
}
|
||||
else if (c=='.' && !hasDecimalPoint)
|
||||
{
|
||||
string += c;
|
||||
hasDecimalPoint = true;
|
||||
}
|
||||
else if (string.empty() && c=='-')
|
||||
string += c;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasDigit)
|
||||
{
|
||||
error();
|
||||
return Token (Token::Type_None);
|
||||
}
|
||||
|
||||
float value;
|
||||
std::istringstream stream (string.c_str());
|
||||
stream >> value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token)
|
||||
{
|
||||
static const char *sKeywords[] =
|
||||
{
|
||||
"true", "false",
|
||||
"and", "or", "not",
|
||||
"string", "value",
|
||||
0
|
||||
};
|
||||
|
||||
std::string string = Misc::StringUtils::lowerCase (token.mString);
|
||||
|
||||
for (int i=0; sKeywords[i]; ++i)
|
||||
if (sKeywords[i]==string || (string.size()==1 && sKeywords[i][0]==string[0]))
|
||||
return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True), token.mString);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
CSMFilter::Token CSMFilter::Parser::getNextToken()
|
||||
{
|
||||
int size = static_cast<int> (mInput.size());
|
||||
|
||||
char c = 0;
|
||||
|
||||
for (; mIndex<size; ++mIndex)
|
||||
{
|
||||
c = mInput[mIndex];
|
||||
|
||||
if (c!=' ')
|
||||
break;
|
||||
}
|
||||
|
||||
if (mIndex>=size)
|
||||
return Token (Token::Type_EOS);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '(': ++mIndex; return Token (Token::Type_Open);
|
||||
case ')': ++mIndex; return Token (Token::Type_Close);
|
||||
case '[': ++mIndex; return Token (Token::Type_OpenSquare);
|
||||
case ']': ++mIndex; return Token (Token::Type_CloseSquare);
|
||||
case ',': ++mIndex; return Token (Token::Type_Comma);
|
||||
case '!': ++mIndex; return Token (Token::Type_OneShot);
|
||||
}
|
||||
|
||||
if (c=='"' || c=='_' || std::isalpha (c) || c==':')
|
||||
return getStringToken();
|
||||
|
||||
if (c=='-' || c=='.' || std::isdigit (c))
|
||||
return getNumberToken();
|
||||
|
||||
error();
|
||||
return Token (Token::Type_None);
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseImp (bool allowEmpty, bool ignoreOneShot)
|
||||
{
|
||||
if (Token token = getNextToken())
|
||||
{
|
||||
if (token==Token (Token::Type_OneShot))
|
||||
token = getNextToken();
|
||||
|
||||
if (token)
|
||||
switch (token.mType)
|
||||
{
|
||||
case Token::Type_Keyword_True:
|
||||
|
||||
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (true));
|
||||
|
||||
case Token::Type_Keyword_False:
|
||||
|
||||
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (false));
|
||||
|
||||
case Token::Type_Keyword_And:
|
||||
case Token::Type_Keyword_Or:
|
||||
|
||||
return parseNAry (token);
|
||||
|
||||
case Token::Type_Keyword_Not:
|
||||
{
|
||||
boost::shared_ptr<CSMFilter::Node> node = parseImp();
|
||||
|
||||
if (mError)
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
return boost::shared_ptr<CSMFilter::Node> (new NotNode (node));
|
||||
}
|
||||
|
||||
case Token::Type_Keyword_Text:
|
||||
|
||||
return parseText();
|
||||
|
||||
case Token::Type_Keyword_Value:
|
||||
|
||||
return parseValue();
|
||||
|
||||
case Token::Type_EOS:
|
||||
|
||||
if (!allowEmpty)
|
||||
error();
|
||||
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
default:
|
||||
|
||||
error();
|
||||
}
|
||||
}
|
||||
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseNAry (const Token& keyword)
|
||||
{
|
||||
std::vector<boost::shared_ptr<Node> > nodes;
|
||||
|
||||
Token token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Open)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
boost::shared_ptr<Node> node = parseImp();
|
||||
|
||||
if (mError)
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
nodes.push_back (node);
|
||||
|
||||
Token token = getNextToken();
|
||||
|
||||
if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma))
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
if (token.mType==Token::Type_Close)
|
||||
break;
|
||||
}
|
||||
|
||||
if (nodes.empty())
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
switch (keyword.mType)
|
||||
{
|
||||
case Token::Type_Keyword_And: return boost::shared_ptr<CSMFilter::Node> (new AndNode (nodes));
|
||||
case Token::Type_Keyword_Or: return boost::shared_ptr<CSMFilter::Node> (new OrNode (nodes));
|
||||
default: error(); return boost::shared_ptr<Node>();
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseText()
|
||||
{
|
||||
Token token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Open)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (!token)
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
// parse column ID
|
||||
int columnId = -1;
|
||||
|
||||
if (token.mType==Token::Type_Number)
|
||||
{
|
||||
if (static_cast<int> (token.mNumber)==token.mNumber)
|
||||
columnId = static_cast<int> (token.mNumber);
|
||||
}
|
||||
else if (token.isString())
|
||||
{
|
||||
columnId = CSMWorld::Columns::getId (token.mString);
|
||||
}
|
||||
|
||||
if (columnId<0)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Comma)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
// parse text pattern
|
||||
token = getNextToken();
|
||||
|
||||
if (!token.isString())
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
std::string text = token.mString;
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Close)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
return boost::shared_ptr<Node> (new TextNode (columnId, text));
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue()
|
||||
{
|
||||
Token token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Open)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (!token)
|
||||
return boost::shared_ptr<Node>();
|
||||
|
||||
// parse column ID
|
||||
int columnId = -1;
|
||||
|
||||
if (token.mType==Token::Type_Number)
|
||||
{
|
||||
if (static_cast<int> (token.mNumber)==token.mNumber)
|
||||
columnId = static_cast<int> (token.mNumber);
|
||||
}
|
||||
else if (token.isString())
|
||||
{
|
||||
columnId = CSMWorld::Columns::getId (token.mString);
|
||||
}
|
||||
|
||||
if (columnId<0)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Comma)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
// parse value
|
||||
double lower = 0;
|
||||
double upper = 0;
|
||||
ValueNode::Type lowerType = ValueNode::Type_Open;
|
||||
ValueNode::Type upperType = ValueNode::Type_Open;
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType==Token::Type_Number)
|
||||
{
|
||||
// single value
|
||||
lower = upper = token.mNumber;
|
||||
lowerType = upperType = ValueNode::Type_Closed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// interval
|
||||
if (token.mType==Token::Type_OpenSquare)
|
||||
lowerType = ValueNode::Type_Closed;
|
||||
else if (token.mType!=Token::Type_CloseSquare && token.mType!=Token::Type_Open)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType==Token::Type_Number)
|
||||
{
|
||||
lower = token.mNumber;
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Comma)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
}
|
||||
else if (token.mType==Token::Type_Comma)
|
||||
{
|
||||
lowerType = ValueNode::Type_Infinite;
|
||||
}
|
||||
else
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType==Token::Type_Number)
|
||||
{
|
||||
upper = token.mNumber;
|
||||
|
||||
token = getNextToken();
|
||||
}
|
||||
else
|
||||
upperType = ValueNode::Type_Infinite;
|
||||
|
||||
if (token.mType==Token::Type_CloseSquare)
|
||||
{
|
||||
if (upperType!=ValueNode::Type_Infinite)
|
||||
upperType = ValueNode::Type_Closed;
|
||||
}
|
||||
else if (token.mType!=Token::Type_OpenSquare && token.mType!=Token::Type_Close)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
}
|
||||
|
||||
token = getNextToken();
|
||||
|
||||
if (token.mType!=Token::Type_Close)
|
||||
{
|
||||
error();
|
||||
return boost::shared_ptr<Node>();
|
||||
}
|
||||
|
||||
return boost::shared_ptr<Node> (new ValueNode (columnId, lowerType, upperType, lower, upper));
|
||||
}
|
||||
|
||||
void CSMFilter::Parser::error()
|
||||
{
|
||||
mError = true;
|
||||
}
|
||||
|
||||
CSMFilter::Parser::Parser (const CSMWorld::Data& data)
|
||||
: mIndex (0), mError (false), mData (data) {}
|
||||
|
||||
bool CSMFilter::Parser::parse (const std::string& filter, bool allowPredefined)
|
||||
{
|
||||
// reset
|
||||
mFilter.reset();
|
||||
mError = false;
|
||||
mInput = filter;
|
||||
mIndex = 0;
|
||||
|
||||
Token token;
|
||||
|
||||
if (allowPredefined)
|
||||
token = getNextToken();
|
||||
|
||||
if (!allowPredefined || token==Token (Token::Type_OneShot))
|
||||
{
|
||||
boost::shared_ptr<Node> node = parseImp (true, token!=Token (Token::Type_OneShot));
|
||||
|
||||
if (mError)
|
||||
return false;
|
||||
|
||||
if (getNextToken()!=Token (Token::Type_EOS))
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node)
|
||||
mFilter = node;
|
||||
else
|
||||
{
|
||||
// Empty filter string equals to filter "true".
|
||||
mFilter.reset (new BooleanNode (true));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// We do not use isString() here, because there could be a pre-defined filter with an ID that is
|
||||
// equal a filter keyword.
|
||||
else if (token.mType==Token::Type_String && allowPredefined)
|
||||
{
|
||||
if (getNextToken()!=Token (Token::Type_EOS))
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = mData.getFilters().searchId (token.mString);
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
|
||||
const CSMWorld::Record<CSMFilter::Filter>& record = mData.getFilters().getRecord (index);
|
||||
|
||||
if (record.isDeleted())
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
|
||||
return parse (record.get().mFilter, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const
|
||||
{
|
||||
if (mError)
|
||||
throw std::logic_error ("No filter available");
|
||||
|
||||
return mFilter;
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
#ifndef CSM_FILTER_PARSER_H
|
||||
#define CSM_FILTER_PARSER_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Data;
|
||||
}
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
struct Token;
|
||||
|
||||
class Parser
|
||||
{
|
||||
boost::shared_ptr<Node> mFilter;
|
||||
std::string mInput;
|
||||
int mIndex;
|
||||
bool mError;
|
||||
const CSMWorld::Data& mData;
|
||||
|
||||
Token getStringToken();
|
||||
|
||||
Token getNumberToken();
|
||||
|
||||
Token getNextToken();
|
||||
|
||||
Token checkKeywords (const Token& token);
|
||||
///< Turn string token into keyword token, if possible.
|
||||
|
||||
boost::shared_ptr<Node> parseImp (bool allowEmpty = false, bool ignoreOneShot = false);
|
||||
///< Will return a null-pointer, if there is nothing more to parse.
|
||||
|
||||
boost::shared_ptr<Node> parseNAry (const Token& keyword);
|
||||
|
||||
boost::shared_ptr<Node> parseText();
|
||||
|
||||
boost::shared_ptr<Node> parseValue();
|
||||
|
||||
void error();
|
||||
|
||||
public:
|
||||
|
||||
Parser (const CSMWorld::Data& data);
|
||||
|
||||
bool parse (const std::string& filter, bool allowPredefined = true);
|
||||
///< Discards any previous calls to parse
|
||||
///
|
||||
/// \return Success?
|
||||
|
||||
boost::shared_ptr<Node> getFilter() const;
|
||||
///< Throws an exception if the last call to parse did not return true.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,83 @@
|
||||
|
||||
#include "textnode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QRegExp>
|
||||
|
||||
#include "../world/columns.hpp"
|
||||
#include "../world/idtable.hpp"
|
||||
|
||||
CSMFilter::TextNode::TextNode (int columnId, const std::string& text)
|
||||
: mColumnId (columnId), mText (text)
|
||||
{}
|
||||
|
||||
bool CSMFilter::TextNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
const std::map<int, int>::const_iterator iter = columns.find (mColumnId);
|
||||
|
||||
if (iter==columns.end())
|
||||
throw std::logic_error ("invalid column in text node test");
|
||||
|
||||
if (iter->second==-1)
|
||||
return true;
|
||||
|
||||
QModelIndex index = table.index (row, iter->second);
|
||||
|
||||
QVariant data = table.data (index);
|
||||
|
||||
QString string;
|
||||
|
||||
if (data.type()==QVariant::String)
|
||||
{
|
||||
string = data.toString();
|
||||
}
|
||||
else if (data.type()==QVariant::Int || data.type()==QVariant::UInt ||
|
||||
CSMWorld::Columns::hasEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId)))
|
||||
{
|
||||
int value = data.toInt();
|
||||
|
||||
std::vector<std::string> enums =
|
||||
CSMWorld::Columns::getEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId));
|
||||
|
||||
if (value>=0 && value<static_cast<int> (enums.size()))
|
||||
string = QString::fromUtf8 (enums[value].c_str());
|
||||
}
|
||||
else if (data.type()==QVariant::Bool)
|
||||
{
|
||||
string = data.toBool() ? "true" : " false";
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
/// \todo make pattern syntax configurable
|
||||
QRegExp regExp (QString::fromUtf8 (mText.c_str()), Qt::CaseInsensitive);
|
||||
|
||||
return regExp.exactMatch (string);
|
||||
}
|
||||
|
||||
std::vector<int> CSMFilter::TextNode::getReferencedColumns() const
|
||||
{
|
||||
return std::vector<int> (1, mColumnId);
|
||||
}
|
||||
|
||||
std::string CSMFilter::TextNode::toString (bool numericColumns) const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "text (";
|
||||
|
||||
if (numericColumns)
|
||||
stream << mColumnId;
|
||||
else
|
||||
stream
|
||||
<< "\""
|
||||
<< CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))
|
||||
<< "\"";
|
||||
|
||||
stream << ", \"" << mText << "\")";
|
||||
|
||||
return stream.str();
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
#ifndef CSM_FILTER_TEXTNODE_H
|
||||
#define CSM_FILTER_TEXTNODE_H
|
||||
|
||||
#include "leafnode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class TextNode : public LeafNode
|
||||
{
|
||||
int mColumnId;
|
||||
std::string mText;
|
||||
|
||||
public:
|
||||
|
||||
TextNode (int columnId, const std::string& text);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,26 @@
|
||||
|
||||
#include "unarynode.hpp"
|
||||
|
||||
CSMFilter::UnaryNode::UnaryNode (boost::shared_ptr<Node> child, const std::string& name)
|
||||
: mChild (child), mName (name)
|
||||
{}
|
||||
|
||||
const CSMFilter::Node& CSMFilter::UnaryNode::getChild() const
|
||||
{
|
||||
return *mChild;
|
||||
}
|
||||
|
||||
CSMFilter::Node& CSMFilter::UnaryNode::getChild()
|
||||
{
|
||||
return *mChild;
|
||||
}
|
||||
|
||||
std::vector<int> CSMFilter::UnaryNode::getReferencedColumns() const
|
||||
{
|
||||
return mChild->getReferencedColumns();
|
||||
}
|
||||
|
||||
std::string CSMFilter::UnaryNode::toString (bool numericColumns) const
|
||||
{
|
||||
return mName + " " + mChild->toString (numericColumns);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef CSM_FILTER_UNARYNODE_H
|
||||
#define CSM_FILTER_UNARYNODE_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "node.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class UnaryNode : public Node
|
||||
{
|
||||
boost::shared_ptr<Node> mChild;
|
||||
std::string mName;
|
||||
|
||||
public:
|
||||
|
||||
UnaryNode (boost::shared_ptr<Node> child, const std::string& name);
|
||||
|
||||
const Node& getChild() const;
|
||||
|
||||
Node& getChild();
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,97 @@
|
||||
|
||||
#include "valuenode.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../world/columns.hpp"
|
||||
#include "../world/idtable.hpp"
|
||||
|
||||
CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType,
|
||||
double lower, double upper)
|
||||
: mColumnId (columnId), mLowerType (lowerType), mUpperType (upperType), mLower (lower), mUpper (upper){}
|
||||
|
||||
bool CSMFilter::ValueNode::test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const
|
||||
{
|
||||
const std::map<int, int>::const_iterator iter = columns.find (mColumnId);
|
||||
|
||||
if (iter==columns.end())
|
||||
throw std::logic_error ("invalid column in test value test");
|
||||
|
||||
if (iter->second==-1)
|
||||
return true;
|
||||
|
||||
QModelIndex index = table.index (row, iter->second);
|
||||
|
||||
QVariant data = table.data (index);
|
||||
|
||||
if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int &&
|
||||
data.type()!=QVariant::UInt)
|
||||
return false;
|
||||
|
||||
double value = data.toDouble();
|
||||
|
||||
switch (mLowerType)
|
||||
{
|
||||
case Type_Closed: if (value<mLower) return false; break;
|
||||
case Type_Open: if (value<=mLower) return false; break;
|
||||
case Type_Infinite: break;
|
||||
}
|
||||
|
||||
switch (mUpperType)
|
||||
{
|
||||
case Type_Closed: if (value>mUpper) return false; break;
|
||||
case Type_Open: if (value>=mUpper) return false; break;
|
||||
case Type_Infinite: break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<int> CSMFilter::ValueNode::getReferencedColumns() const
|
||||
{
|
||||
return std::vector<int> (1, mColumnId);
|
||||
}
|
||||
|
||||
std::string CSMFilter::ValueNode::toString (bool numericColumns) const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "value (";
|
||||
|
||||
if (numericColumns)
|
||||
stream << mColumnId;
|
||||
else
|
||||
stream
|
||||
<< "\""
|
||||
<< CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))
|
||||
<< "\"";
|
||||
|
||||
stream << ", \"";
|
||||
|
||||
if (mLower==mUpper && mLowerType!=Type_Infinite && mUpperType!=Type_Infinite)
|
||||
stream << mLower;
|
||||
else
|
||||
{
|
||||
switch (mLowerType)
|
||||
{
|
||||
case Type_Closed: stream << "[" << mLower; break;
|
||||
case Type_Open: stream << "(" << mLower; break;
|
||||
case Type_Infinite: stream << "("; break;
|
||||
}
|
||||
|
||||
stream << ", ";
|
||||
|
||||
switch (mUpperType)
|
||||
{
|
||||
case Type_Closed: stream << mUpper << "]"; break;
|
||||
case Type_Open: stream << mUpper << ")"; break;
|
||||
case Type_Infinite: stream << ")"; break;
|
||||
}
|
||||
}
|
||||
|
||||
stream << ")";
|
||||
|
||||
return stream.str();
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
#ifndef CSM_FILTER_VALUENODE_H
|
||||
#define CSM_FILTER_VALUENODE_H
|
||||
|
||||
#include "leafnode.hpp"
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
class ValueNode : public LeafNode
|
||||
{
|
||||
public:
|
||||
|
||||
enum Type
|
||||
{
|
||||
Type_Closed, Type_Open, Type_Infinite
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
int mColumnId;
|
||||
std::string mText;
|
||||
double mLower;
|
||||
double mUpper;
|
||||
Type mLowerType;
|
||||
Type mUpperType;
|
||||
|
||||
public:
|
||||
|
||||
ValueNode (int columnId, Type lowerType, Type upperType, double lower, double upper);
|
||||
|
||||
virtual bool test (const CSMWorld::IdTable& table, int row,
|
||||
const std::map<int, int>& columns) const;
|
||||
///< \return Can the specified table row pass through to filter?
|
||||
/// \param columns column ID to column index mapping
|
||||
|
||||
virtual std::vector<int> getReferencedColumns() const;
|
||||
///< Return a list of the IDs of the columns referenced by this node. The column mapping
|
||||
/// passed into test as columns must contain all columns listed here.
|
||||
|
||||
virtual std::string toString (bool numericColumns) const;
|
||||
///< Return a string that represents this node.
|
||||
///
|
||||
/// \param numericColumns Use numeric IDs instead of string to represent columns.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,82 @@
|
||||
#include "settingcontainer.hpp"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
CSMSettings::SettingContainer::SettingContainer(QObject *parent) :
|
||||
QObject(parent), mValue (0), mValues (0)
|
||||
{
|
||||
}
|
||||
|
||||
CSMSettings::SettingContainer::SettingContainer(const QString &value, QObject *parent) :
|
||||
QObject(parent), mValue (new QString (value)), mValues (0)
|
||||
{
|
||||
}
|
||||
|
||||
void CSMSettings::SettingContainer::insert (const QString &value)
|
||||
{
|
||||
if (mValue)
|
||||
{
|
||||
mValues = new QStringList;
|
||||
mValues->push_back (*mValue);
|
||||
mValues->push_back (value);
|
||||
|
||||
delete mValue;
|
||||
mValue = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mValue;
|
||||
mValue = new QString (value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CSMSettings::SettingContainer::update (const QString &value, int index)
|
||||
{
|
||||
if (isEmpty())
|
||||
mValue = new QString(value);
|
||||
|
||||
else if (mValue)
|
||||
*mValue = value;
|
||||
|
||||
else if (mValues)
|
||||
mValues->replace(index, value);
|
||||
}
|
||||
|
||||
QString CSMSettings::SettingContainer::getValue (int index) const
|
||||
{
|
||||
QString retVal("");
|
||||
|
||||
//if mValue is valid, it's a single-value property.
|
||||
//ignore the index and return the value
|
||||
if (mValue)
|
||||
retVal = *mValue;
|
||||
|
||||
//otherwise, if it's a multivalued property
|
||||
//return the appropriate value at the index
|
||||
else if (mValues)
|
||||
{
|
||||
if (index == -1)
|
||||
retVal = mValues->at(0);
|
||||
|
||||
else if (index < mValues->size())
|
||||
retVal = mValues->at(index);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int CSMSettings::SettingContainer::count () const
|
||||
{
|
||||
int retVal = 0;
|
||||
|
||||
if (!isEmpty())
|
||||
{
|
||||
if (mValues)
|
||||
retVal = mValues->size();
|
||||
else
|
||||
retVal = 1;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
#ifndef SETTINGCONTAINER_HPP
|
||||
#define SETTINGCONTAINER_HPP
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QStringList;
|
||||
|
||||
namespace CSMSettings
|
||||
{
|
||||
class SettingContainer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QString *mValue;
|
||||
QStringList *mValues;
|
||||
|
||||
public:
|
||||
|
||||
explicit SettingContainer (QObject *parent = 0);
|
||||
explicit SettingContainer (const QString &value, QObject *parent = 0);
|
||||
|
||||
/// add a value to the container
|
||||
/// multiple values supported
|
||||
void insert (const QString &value);
|
||||
|
||||
/// update an existing value
|
||||
/// index specifies multiple values
|
||||
void update (const QString &value, int index = 0);
|
||||
|
||||
/// return value at specified index
|
||||
QString getValue (int index = -1) const;
|
||||
|
||||
/// retrieve list of all values
|
||||
inline QStringList *getValues() const { return mValues; }
|
||||
|
||||
/// return size of list
|
||||
int count() const;
|
||||
|
||||
/// test for empty container
|
||||
/// useful for default-constructed containers returned by QMap when invalid key is passed
|
||||
inline bool isEmpty() const { return (!mValue && !mValues); }
|
||||
|
||||
inline bool isMultiValue() const { return (mValues); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SETTINGCONTAINER_HPP
|
@ -0,0 +1,104 @@
|
||||
#include "settingsitem.hpp"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
bool CSMSettings::SettingsItem::updateItem (const QStringList *values)
|
||||
{
|
||||
QStringList::ConstIterator it = values->begin();
|
||||
|
||||
//if the item is not multivalued,
|
||||
//save the last value passed in the container
|
||||
if (!mIsMultiValue)
|
||||
{
|
||||
it = values->end();
|
||||
it--;
|
||||
}
|
||||
|
||||
bool isValid = true;
|
||||
QString value ("");
|
||||
|
||||
for (; it != values->end(); ++it)
|
||||
{
|
||||
value = *it;
|
||||
isValid = validate(value);
|
||||
|
||||
//skip only the invalid values
|
||||
if (!isValid)
|
||||
continue;
|
||||
|
||||
insert(value);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
bool CSMSettings::SettingsItem::updateItem (const QString &value)
|
||||
{
|
||||
//takes a value or a SettingsContainer and updates itself accordingly
|
||||
//after validating the data against it's own definition
|
||||
|
||||
QString newValue = value;
|
||||
|
||||
if (!validate (newValue))
|
||||
newValue = mDefaultValue;
|
||||
|
||||
bool success = (getValue() != newValue);
|
||||
|
||||
if (success)
|
||||
{
|
||||
if (mIsMultiValue)
|
||||
insert (newValue);
|
||||
else
|
||||
update (newValue);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool CSMSettings::SettingsItem::updateItem(int valueListIndex)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
if (mValueList)
|
||||
{
|
||||
if (mValueList->size() > valueListIndex)
|
||||
success = updateItem (mValueList->at(valueListIndex));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool CSMSettings::SettingsItem::validate (const QString &value)
|
||||
{
|
||||
//if there is no value list or value pair, there is no validation to do
|
||||
bool isValid = !(!mValueList->isEmpty() || mValuePair);
|
||||
|
||||
if (!isValid && !mValueList->isEmpty())
|
||||
{
|
||||
for (QStringList::Iterator it = mValueList->begin(); it != mValueList->end(); ++it)
|
||||
// foreach (QString listItem, *mValueList)
|
||||
{
|
||||
isValid = (value == *it);
|
||||
|
||||
if (isValid)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!isValid && mValuePair)
|
||||
{
|
||||
int numVal = value.toInt();
|
||||
|
||||
isValid = (numVal > mValuePair->left.toInt() && numVal < mValuePair->right.toInt());
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
void CSMSettings::SettingsItem::setDefaultValue (const QString &value)
|
||||
{
|
||||
mDefaultValue = value;
|
||||
update (value);
|
||||
}
|
||||
|
||||
QString CSMSettings::SettingsItem::getDefaultValue() const
|
||||
{
|
||||
return mDefaultValue;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
#ifndef SETTINGSITEM_HPP
|
||||
#define SETTINGSITEM_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include "support.hpp"
|
||||
#include "settingcontainer.hpp"
|
||||
|
||||
namespace CSMSettings
|
||||
{
|
||||
/// Represents a setting including metadata
|
||||
/// (valid values, ranges, defaults, and multivalue status
|
||||
class SettingsItem : public SettingContainer
|
||||
{
|
||||
QStringPair *mValuePair;
|
||||
QStringList *mValueList;
|
||||
bool mIsMultiValue;
|
||||
QString mDefaultValue;
|
||||
|
||||
public:
|
||||
explicit SettingsItem(QString name, bool isMultiValue,
|
||||
const QString& defaultValue, QObject *parent = 0)
|
||||
: SettingContainer(defaultValue, parent),
|
||||
mIsMultiValue (isMultiValue), mValueList (0),
|
||||
mValuePair (0), mDefaultValue (defaultValue)
|
||||
{
|
||||
QObject::setObjectName(name);
|
||||
}
|
||||
|
||||
/// updateItem overloads for updating setting value
|
||||
/// provided a list of values (multi-valued),
|
||||
/// a specific value
|
||||
/// or an index value corresponding to the mValueList
|
||||
bool updateItem (const QStringList *values);
|
||||
bool updateItem (const QString &value);
|
||||
bool updateItem (int valueListIndex);
|
||||
|
||||
/// retrieve list of valid values for setting
|
||||
inline QStringList *getValueList() { return mValueList; }
|
||||
|
||||
/// write list of valid values for setting
|
||||
inline void setValueList (QStringList *valueList) { mValueList = valueList; }
|
||||
|
||||
/// valuePair used for spin boxes (max / min)
|
||||
inline QStringPair *getValuePair() { return mValuePair; }
|
||||
|
||||
/// set value range (spinbox / integer use)
|
||||
inline void setValuePair (QStringPair valuePair) { mValuePair = new QStringPair(valuePair); }
|
||||
|
||||
inline bool isMultivalue () { return mIsMultiValue; }
|
||||
|
||||
void setDefaultValue (const QString &value);
|
||||
QString getDefaultValue () const;
|
||||
|
||||
private:
|
||||
|
||||
/// Verifies that the supplied value is one of the following:
|
||||
/// 1. Within the limits of the value pair (min / max)
|
||||
/// 2. One of the values indicated in the value list
|
||||
bool validate (const QString &value);
|
||||
};
|
||||
}
|
||||
#endif // SETTINGSITEM_HPP
|
||||
|
@ -0,0 +1 @@
|
||||
#include "support.hpp"
|
@ -0,0 +1,39 @@
|
||||
#ifndef MODEL_SUPPORT_HPP
|
||||
#define MODEL_SUPPORT_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
class QLayout;
|
||||
class QWidget;
|
||||
class QListWidgetItem;
|
||||
|
||||
namespace CSMSettings
|
||||
{
|
||||
class SettingContainer;
|
||||
|
||||
typedef QList<SettingContainer *> SettingList;
|
||||
typedef QMap<QString, SettingContainer *> SettingMap;
|
||||
typedef QMap<QString, SettingMap *> SectionMap;
|
||||
|
||||
struct QStringPair
|
||||
{
|
||||
QStringPair(): left (""), right ("")
|
||||
{}
|
||||
|
||||
QStringPair (const QString &leftValue, const QString &rightValue)
|
||||
: left (leftValue), right(rightValue)
|
||||
{}
|
||||
|
||||
QStringPair (const QStringPair &pair)
|
||||
: left (pair.left), right (pair.right)
|
||||
{}
|
||||
|
||||
QString left;
|
||||
QString right;
|
||||
|
||||
bool isEmpty() const
|
||||
{ return (left.isEmpty() && right.isEmpty()); }
|
||||
};
|
||||
}
|
||||
#endif // MODEL_SUPPORT_HPP
|
@ -0,0 +1,355 @@
|
||||
#include "usersettings.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
#include <QMessageBox>
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include "settingcontainer.hpp"
|
||||
#include <boost/version.hpp>
|
||||
|
||||
/**
|
||||
* Workaround for problems with whitespaces in paths in older versions of Boost library
|
||||
*/
|
||||
#if (BOOST_VERSION <= 104600)
|
||||
namespace boost
|
||||
{
|
||||
|
||||
template<>
|
||||
inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
|
||||
{
|
||||
return boost::filesystem::path(arg);
|
||||
}
|
||||
|
||||
} /* namespace boost */
|
||||
#endif /* (BOOST_VERSION <= 104600) */
|
||||
|
||||
CSMSettings::UserSettings *CSMSettings::UserSettings::mUserSettingsInstance = 0;
|
||||
|
||||
CSMSettings::UserSettings::UserSettings()
|
||||
{
|
||||
assert(!mUserSettingsInstance);
|
||||
mUserSettingsInstance = this;
|
||||
|
||||
mReadWriteMessage = QObject::tr("<br><b>Could not open or create file for writing</b><br><br> \
|
||||
Please make sure you have the right permissions and try again.<br>");
|
||||
|
||||
mReadOnlyMessage = QObject::tr("<br><b>Could not open file for reading</b><br><br> \
|
||||
Please make sure you have the right permissions and try again.<br>");
|
||||
|
||||
buildEditorSettingDefaults();
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::buildEditorSettingDefaults()
|
||||
{
|
||||
SettingContainer *windowHeight = new SettingContainer("768", this);
|
||||
SettingContainer *windowWidth = new SettingContainer("1024", this);
|
||||
SettingContainer *rsDelegate = new SettingContainer("Icon and Text", this);
|
||||
SettingContainer *refIdTypeDelegate = new SettingContainer("Icon and Text", this);
|
||||
|
||||
windowHeight->setObjectName ("Height");
|
||||
windowWidth->setObjectName ("Width");
|
||||
rsDelegate->setObjectName ("Record Status Display");
|
||||
refIdTypeDelegate->setObjectName ("Referenceable ID Type Display");
|
||||
|
||||
SettingMap *displayFormatMap = new SettingMap;
|
||||
SettingMap *windowSizeMap = new SettingMap;
|
||||
|
||||
displayFormatMap->insert (rsDelegate->objectName(), rsDelegate );
|
||||
displayFormatMap->insert (refIdTypeDelegate->objectName(), refIdTypeDelegate);
|
||||
|
||||
windowSizeMap->insert (windowWidth->objectName(), windowWidth );
|
||||
windowSizeMap->insert (windowHeight->objectName(), windowHeight );
|
||||
|
||||
mEditorSettingDefaults.insert ("Display Format", displayFormatMap);
|
||||
mEditorSettingDefaults.insert ("Window Size", windowSizeMap);
|
||||
}
|
||||
|
||||
CSMSettings::UserSettings::~UserSettings()
|
||||
{
|
||||
mUserSettingsInstance = 0;
|
||||
}
|
||||
|
||||
QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, bool isReadOnly) const
|
||||
{
|
||||
QIODevice::OpenMode openFlags = QIODevice::Text;
|
||||
|
||||
if (isReadOnly)
|
||||
openFlags = QIODevice::ReadOnly | openFlags;
|
||||
else
|
||||
openFlags = QIODevice::ReadWrite | QIODevice::Truncate | openFlags;
|
||||
|
||||
QFile *file = new QFile(filePath);
|
||||
QTextStream *stream = 0;
|
||||
|
||||
if (file->open(openFlags))
|
||||
{
|
||||
stream = new QTextStream(file);
|
||||
stream->setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
}
|
||||
|
||||
return stream;
|
||||
|
||||
}
|
||||
|
||||
bool CSMSettings::UserSettings::writeSettings(QMap<QString, CSMSettings::SettingList *> &settings)
|
||||
{
|
||||
QTextStream *stream = openFileStream(mUserFilePath);
|
||||
|
||||
bool success = (stream);
|
||||
|
||||
if (success)
|
||||
{
|
||||
QList<QString> keyList = settings.keys();
|
||||
|
||||
foreach (QString key, keyList)
|
||||
{
|
||||
SettingList *sectionSettings = settings[key];
|
||||
|
||||
*stream << "[" << key << "]" << '\n';
|
||||
|
||||
foreach (SettingContainer *item, *sectionSettings)
|
||||
*stream << item->objectName() << " = " << item->getValue() << '\n';
|
||||
}
|
||||
|
||||
stream->device()->close();
|
||||
delete stream;
|
||||
stream = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayFileErrorMessage(mReadWriteMessage, false);
|
||||
}
|
||||
|
||||
return (success);
|
||||
}
|
||||
|
||||
|
||||
const CSMSettings::SectionMap &CSMSettings::UserSettings::getSectionMap() const
|
||||
{
|
||||
return mSectionSettings;
|
||||
}
|
||||
|
||||
const CSMSettings::SettingMap *CSMSettings::UserSettings::getSettings(const QString §ionName) const
|
||||
{
|
||||
return getValidSettings(sectionName);
|
||||
}
|
||||
|
||||
bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
|
||||
{
|
||||
if (filePath.isEmpty())
|
||||
return false;
|
||||
|
||||
SectionMap loadedSettings;
|
||||
|
||||
QTextStream *stream = openFileStream (filePath, true);
|
||||
|
||||
bool success = (stream);
|
||||
|
||||
if (success)
|
||||
{
|
||||
//looks for a square bracket, "'\\["
|
||||
//that has one or more "not nothing" in it, "([^]]+)"
|
||||
//and is closed with a square bracket, "\\]"
|
||||
|
||||
QRegExp sectionRe("^\\[([^]]+)\\]");
|
||||
|
||||
//Find any character(s) that is/are not equal sign(s), "[^=]+"
|
||||
//followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*"
|
||||
//and one or more periods, "(.+)"
|
||||
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
CSMSettings::SettingMap *settings = 0;
|
||||
QString section = "none";
|
||||
|
||||
while (!stream->atEnd())
|
||||
{
|
||||
QString line = stream->readLine().simplified();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
||||
//if a section is found, push it onto a new QStringList
|
||||
//and push the QStringList onto
|
||||
if (sectionRe.exactMatch(line))
|
||||
{
|
||||
//add the previous section's settings to the member map
|
||||
if (settings)
|
||||
loadedSettings.insert(section, settings);
|
||||
|
||||
//save new section and create a new list
|
||||
section = sectionRe.cap(1);
|
||||
settings = new SettingMap;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keyRe.indexIn(line) != -1)
|
||||
{
|
||||
SettingContainer *sc = new SettingContainer (keyRe.cap(2).simplified());
|
||||
sc->setObjectName(keyRe.cap(1).simplified());
|
||||
(*settings)[keyRe.cap(1).simplified()] = sc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
loadedSettings.insert(section, settings);
|
||||
|
||||
stream->device()->close();
|
||||
delete stream;
|
||||
stream = 0;
|
||||
}
|
||||
|
||||
mergeMap (loadedSettings);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::mergeMap (const CSMSettings::SectionMap §ionSettings)
|
||||
{
|
||||
foreach (QString key, sectionSettings.uniqueKeys())
|
||||
{
|
||||
// insert entire section if it does not already exist in the loaded files
|
||||
if (mSectionSettings.find(key) == mSectionSettings.end())
|
||||
mSectionSettings.insert(key, sectionSettings.value(key));
|
||||
else
|
||||
{
|
||||
SettingMap *passedSettings = sectionSettings.value(key);
|
||||
SettingMap *settings = mSectionSettings.value(key);
|
||||
|
||||
foreach (QString key2, passedSettings->uniqueKeys())
|
||||
{
|
||||
//insert section settings individially if they do not already exist
|
||||
if (settings->find(key2) == settings->end())
|
||||
settings->insert(key2, passedSettings->value(key2));
|
||||
else
|
||||
{
|
||||
settings->value(key2)->update(passedSettings->value(key2)->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::loadSettings (const QString &fileName)
|
||||
{
|
||||
mSectionSettings.clear();
|
||||
|
||||
//global
|
||||
QString globalFilePath = QString::fromStdString(mCfgMgr.getGlobalPath().string()) + fileName;
|
||||
bool globalOk = loadFromFile(globalFilePath);
|
||||
|
||||
|
||||
//local
|
||||
QString localFilePath = QString::fromStdString(mCfgMgr.getLocalPath().string()) + fileName;
|
||||
bool localOk = loadFromFile(localFilePath);
|
||||
|
||||
//user
|
||||
mUserFilePath = QString::fromStdString(mCfgMgr.getUserPath().string()) + fileName;
|
||||
loadFromFile(mUserFilePath);
|
||||
|
||||
if (!(localOk || globalOk))
|
||||
{
|
||||
QString message = QObject::tr("<br><b>Could not open user settings files for reading</b><br><br> \
|
||||
Global and local settings files could not be read.\
|
||||
You may have incorrect file permissions or the OpenCS installation may be corrupted.<br>");
|
||||
|
||||
message += QObject::tr("<br>Global filepath: ") + globalFilePath;
|
||||
message += QObject::tr("<br>Local filepath: ") + localFilePath;
|
||||
|
||||
displayFileErrorMessage ( message, true);
|
||||
}
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::updateSettings (const QString §ionName, const QString &settingName)
|
||||
{
|
||||
|
||||
SettingMap *settings = getValidSettings(sectionName);
|
||||
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
if (settingName.isEmpty())
|
||||
{
|
||||
foreach (const SettingContainer *setting, *settings)
|
||||
emit signalUpdateEditorSetting (setting->objectName(), setting->getValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settings->find(settingName) != settings->end())
|
||||
{
|
||||
const SettingContainer *setting = settings->value(settingName);
|
||||
emit signalUpdateEditorSetting (setting->objectName(), setting->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString CSMSettings::UserSettings::getSetting (const QString §ion, const QString &setting) const
|
||||
{
|
||||
SettingMap *settings = getValidSettings(section);
|
||||
|
||||
QString retVal = "";
|
||||
|
||||
if (settings->find(setting) != settings->end())
|
||||
retVal = settings->value(setting)->getValue();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
CSMSettings::UserSettings& CSMSettings::UserSettings::instance()
|
||||
{
|
||||
assert(mUserSettingsInstance);
|
||||
return *mUserSettingsInstance;
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::displayFileErrorMessage(const QString &message, bool isReadOnly)
|
||||
{
|
||||
// File cannot be opened or created
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(QObject::tr("OpenCS configuration file I/O error"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
|
||||
if (!isReadOnly)
|
||||
msgBox.setText (mReadWriteMessage + message);
|
||||
else
|
||||
msgBox.setText (message);
|
||||
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
CSMSettings::SettingMap *
|
||||
CSMSettings::UserSettings::getValidSettings (const QString §ionName) const
|
||||
{
|
||||
SettingMap *settings = 0;
|
||||
|
||||
//copy the default values for the entire section if it's not found
|
||||
if (mSectionSettings.find(sectionName) == mSectionSettings.end())
|
||||
{
|
||||
if (mEditorSettingDefaults.find(sectionName) != mEditorSettingDefaults.end())
|
||||
settings = mEditorSettingDefaults.value (sectionName);
|
||||
}
|
||||
//otherwise, iterate the section's settings, looking for missing values and replacing them with defaults.
|
||||
else
|
||||
{
|
||||
SettingMap *loadedSettings = mSectionSettings[sectionName];
|
||||
SettingMap *defaultSettings = mEditorSettingDefaults[sectionName];
|
||||
|
||||
foreach (QString key, defaultSettings->uniqueKeys())
|
||||
{
|
||||
//write the default value to the loaded settings
|
||||
if (loadedSettings->find((key))==loadedSettings->end())
|
||||
loadedSettings->insert(key, defaultSettings->value(key));
|
||||
}
|
||||
|
||||
settings = mSectionSettings.value (sectionName);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
#ifndef USERSETTINGS_HPP
|
||||
#define USERSETTINGS_HPP
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include "support.hpp"
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#endif
|
||||
|
||||
namespace Files { typedef std::vector<boost::filesystem::path> PathContainer;
|
||||
struct ConfigurationManager;}
|
||||
|
||||
class QFile;
|
||||
|
||||
namespace CSMSettings {
|
||||
|
||||
struct UserSettings: public QObject
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
SectionMap mSectionSettings;
|
||||
SectionMap mEditorSettingDefaults;
|
||||
|
||||
static UserSettings *mUserSettingsInstance;
|
||||
QString mUserFilePath;
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
QString mReadOnlyMessage;
|
||||
QString mReadWriteMessage;
|
||||
|
||||
public:
|
||||
|
||||
/// Singleton implementation
|
||||
static UserSettings& instance();
|
||||
|
||||
UserSettings();
|
||||
~UserSettings();
|
||||
|
||||
UserSettings (UserSettings const &); //not implemented
|
||||
void operator= (UserSettings const &); //not implemented
|
||||
|
||||
/// Writes settings to the last loaded settings file
|
||||
bool writeSettings(QMap<QString, SettingList *> §ions);
|
||||
|
||||
/// Called from editor to trigger signal to update the specified setting.
|
||||
/// If no setting name is specified, all settings found in the specified section are updated.
|
||||
void updateSettings (const QString §ionName, const QString &settingName = "");
|
||||
|
||||
/// Retrieves the settings file at all three levels (global, local and user).
|
||||
|
||||
/// \todo Multi-valued settings are not fully implemented. Setting values
|
||||
/// \todo loaded in later files will always overwrite previously loaded values.
|
||||
void loadSettings (const QString &fileName);
|
||||
|
||||
/// Returns the entire map of settings across all sections
|
||||
const SectionMap &getSectionMap () const;
|
||||
|
||||
const SettingMap *getSettings (const QString §ionName) const;
|
||||
|
||||
/// Retrieves the value as a QString of the specified setting in the specified section
|
||||
QString getSetting(const QString §ion, const QString &setting) const;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/// Opens a QTextStream from the provided path as read-only or read-write.
|
||||
QTextStream *openFileStream (const QString &filePath, bool isReadOnly = false) const;
|
||||
|
||||
/// Parses a setting file specified in filePath from the provided text stream.
|
||||
bool loadFromFile (const QString &filePath = "");
|
||||
|
||||
/// merge the passed map into mSectionSettings
|
||||
void mergeMap (const SectionMap &);
|
||||
|
||||
void displayFileErrorMessage(const QString &message, bool isReadOnly);
|
||||
|
||||
void buildEditorSettingDefaults();
|
||||
|
||||
SettingMap *getValidSettings (const QString §ionName) const;
|
||||
|
||||
signals:
|
||||
|
||||
void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
|
||||
};
|
||||
}
|
||||
#endif // USERSETTINGS_HPP
|
@ -0,0 +1,39 @@
|
||||
|
||||
#include "birthsigncheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadbsgn.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns)
|
||||
: mBirthsigns (birthsigns)
|
||||
{}
|
||||
|
||||
int CSMTools::BirthsignCheckStage::setup()
|
||||
{
|
||||
return mBirthsigns.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::BirthsignCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::BirthSign& birthsign = mBirthsigns.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId);
|
||||
|
||||
// test for empty name, description and texture
|
||||
if (birthsign.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty name");
|
||||
|
||||
if (birthsign.mDescription.empty())
|
||||
messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty description");
|
||||
|
||||
if (birthsign.mTexture.empty())
|
||||
messages.push_back (id.toString() + "|" + birthsign.mId + " is missing a texture");
|
||||
|
||||
/// \todo test if the texture exists
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef CSM_TOOLS_BIRTHSIGNCHECK_H
|
||||
#define CSM_TOOLS_BIRTHSIGNCHECK_H
|
||||
|
||||
#include <components/esm/loadbsgn.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that birthsign records are internally consistent
|
||||
class BirthsignCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;
|
||||
|
||||
public:
|
||||
|
||||
BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns);
|
||||
|
||||
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,72 @@
|
||||
|
||||
#include "classcheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadclas.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes)
|
||||
: mClasses (classes)
|
||||
{}
|
||||
|
||||
int CSMTools::ClassCheckStage::setup()
|
||||
{
|
||||
return mClasses.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::ClassCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Class& class_= mClasses.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId);
|
||||
|
||||
// test for empty name and description
|
||||
if (class_.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + class_.mId + " has an empty name");
|
||||
|
||||
if (class_.mDescription.empty())
|
||||
messages.push_back (id.toString() + "|" + class_.mId + " has an empty description");
|
||||
|
||||
// test for invalid attributes
|
||||
for (int i=0; i<2; ++i)
|
||||
if (class_.mData.mAttribute[i]==-1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << id.toString() << "|Attribute #" << i << " of " << class_.mId << " is not set";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
|
||||
if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << id.toString() << "|Class lists same attribute twice";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
|
||||
// test for non-unique skill
|
||||
std::map<int, int> skills; // ID, number of occurrences
|
||||
|
||||
for (int i=0; i<5; ++i)
|
||||
for (int i2=0; i2<2; ++i2)
|
||||
++skills[class_.mData.mSkills[i][i2]];
|
||||
|
||||
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
|
||||
if (iter->second>1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream
|
||||
<< id.toString() << "|"
|
||||
<< ESM::Skill::indexToId (iter->first) << " is listed more than once";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef CSM_TOOLS_CLASSCHECK_H
|
||||
#define CSM_TOOLS_CLASSCHECK_H
|
||||
|
||||
#include <components/esm/loadclas.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that class records are internally consistent
|
||||
class ClassCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Class>& mClasses;
|
||||
|
||||
public:
|
||||
|
||||
ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes);
|
||||
|
||||
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,61 @@
|
||||
|
||||
#include "factioncheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadfact.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions)
|
||||
: mFactions (factions)
|
||||
{}
|
||||
|
||||
int CSMTools::FactionCheckStage::setup()
|
||||
{
|
||||
return mFactions.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::FactionCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Faction& faction = mFactions.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId);
|
||||
|
||||
// test for empty name
|
||||
if (faction.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + faction.mId + " has an empty name");
|
||||
|
||||
// test for invalid attributes
|
||||
if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << id.toString() << "|Faction lists same attribute twice";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
|
||||
// test for non-unique skill
|
||||
std::map<int, int> skills; // ID, number of occurrences
|
||||
|
||||
for (int i=0; i<6; ++i)
|
||||
if (faction.mData.mSkills[i]!=-1)
|
||||
++skills[faction.mData.mSkills[i]];
|
||||
|
||||
for (std::map<int, int>::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter)
|
||||
if (iter->second>1)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream
|
||||
<< id.toString() << "|"
|
||||
<< ESM::Skill::indexToId (iter->first) << " is listed more than once";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef CSM_TOOLS_FACTIONCHECK_H
|
||||
#define CSM_TOOLS_FACTIONCHECK_H
|
||||
|
||||
#include <components/esm/loadfact.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that faction records are internally consistent
|
||||
class FactionCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
|
||||
public:
|
||||
|
||||
FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions);
|
||||
|
||||
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,68 @@
|
||||
|
||||
#include "racecheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <components/esm/loadrace.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
void CSMTools::RaceCheckStage::performPerRecord (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Race& race = mRaces.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId);
|
||||
|
||||
// test for empty name and description
|
||||
if (race.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + race.mId + " has an empty name");
|
||||
|
||||
if (race.mDescription.empty())
|
||||
messages.push_back (id.toString() + "|" + race.mId + " has an empty description");
|
||||
|
||||
// test for positive height
|
||||
if (race.mData.mHeight.mMale<=0)
|
||||
messages.push_back (id.toString() + "|male " + race.mId + " has non-positive height");
|
||||
|
||||
if (race.mData.mHeight.mFemale<=0)
|
||||
messages.push_back (id.toString() + "|female " + race.mId + " has non-positive height");
|
||||
|
||||
// test for non-negative weight
|
||||
if (race.mData.mWeight.mMale<0)
|
||||
messages.push_back (id.toString() + "|male " + race.mId + " has negative weight");
|
||||
|
||||
if (race.mData.mWeight.mFemale<0)
|
||||
messages.push_back (id.toString() + "|female " + race.mId + " has negative weight");
|
||||
|
||||
// remember playable flag
|
||||
if (race.mData.mFlags & 0x1)
|
||||
mPlayable = true;
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
||||
|
||||
void CSMTools::RaceCheckStage::performFinal (std::vector<std::string>& messages)
|
||||
{
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races);
|
||||
|
||||
if (!mPlayable)
|
||||
messages.push_back (id.toString() + "|No playable race");
|
||||
}
|
||||
|
||||
CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)
|
||||
: mRaces (races), mPlayable (false)
|
||||
{}
|
||||
|
||||
int CSMTools::RaceCheckStage::setup()
|
||||
{
|
||||
mPlayable = false;
|
||||
return mRaces.getSize()+1;
|
||||
}
|
||||
|
||||
void CSMTools::RaceCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
if (stage==mRaces.getSize())
|
||||
performFinal (messages);
|
||||
else
|
||||
performPerRecord (stage, messages);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef CSM_TOOLS_RACECHECK_H
|
||||
#define CSM_TOOLS_RACECHECK_H
|
||||
|
||||
#include <components/esm/loadrace.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that race records are internally consistent
|
||||
class RaceCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Race>& mRaces;
|
||||
bool mPlayable;
|
||||
|
||||
void performPerRecord (int stage, std::vector<std::string>& messages);
|
||||
|
||||
void performFinal (std::vector<std::string>& messages);
|
||||
|
||||
public:
|
||||
|
||||
RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races);
|
||||
|
||||
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,33 @@
|
||||
|
||||
#include "regioncheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadregn.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions)
|
||||
: mRegions (regions)
|
||||
{}
|
||||
|
||||
int CSMTools::RegionCheckStage::setup()
|
||||
{
|
||||
return mRegions.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::RegionCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Region& region = mRegions.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId);
|
||||
|
||||
// test for empty name
|
||||
if (region.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + region.mId + " has an empty name");
|
||||
|
||||
/// \todo test that the ID in mSleeplist exists
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef CSM_TOOLS_REGIONCHECK_H
|
||||
#define CSM_TOOLS_REGIONCHECK_H
|
||||
|
||||
#include <components/esm/loadregn.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that region records are internally consistent
|
||||
class RegionCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Region>& mRegions;
|
||||
|
||||
public:
|
||||
|
||||
RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions);
|
||||
|
||||
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,37 @@
|
||||
|
||||
#include "skillcheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills)
|
||||
: mSkills (skills)
|
||||
{}
|
||||
|
||||
int CSMTools::SkillCheckStage::setup()
|
||||
{
|
||||
return mSkills.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::SkillCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Skill& skill = mSkills.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId);
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
if (skill.mData.mUseValue[i]<0)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << id.toString() << "|Use value #" << i << " of " << skill.mId << " is negative";
|
||||
|
||||
messages.push_back (stream.str());
|
||||
}
|
||||
|
||||
if (skill.mDescription.empty())
|
||||
messages.push_back (id.toString() + "|" + skill.mId + " has an empty description");
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef CSM_TOOLS_SKILLCHECK_H
|
||||
#define CSM_TOOLS_SKILLCHECK_H
|
||||
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that skill records are internally consistent
|
||||
class SkillCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Skill>& mSkills;
|
||||
|
||||
public:
|
||||
|
||||
SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this tage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,29 @@
|
||||
|
||||
#include "soundcheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds)
|
||||
: mSounds (sounds)
|
||||
{}
|
||||
|
||||
int CSMTools::SoundCheckStage::setup()
|
||||
{
|
||||
return mSounds.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::SoundCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Sound& sound = mSounds.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId);
|
||||
|
||||
if (sound.mData.mMinRange>sound.mData.mMaxRange)
|
||||
messages.push_back (id.toString() + "|Maximum range larger than minimum range");
|
||||
|
||||
/// \todo check, if the sound file exists
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef CSM_TOOLS_SOUNDCHECK_H
|
||||
#define CSM_TOOLS_SOUNDCHECK_H
|
||||
|
||||
#include <components/esm/loadsoun.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that sound records are internally consistent
|
||||
class SoundCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Sound>& mSounds;
|
||||
|
||||
public:
|
||||
|
||||
SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds);
|
||||
|
||||
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,35 @@
|
||||
|
||||
#include "spellcheck.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <components/esm/loadspel.hpp>
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells)
|
||||
: mSpells (spells)
|
||||
{}
|
||||
|
||||
int CSMTools::SpellCheckStage::setup()
|
||||
{
|
||||
return mSpells.getSize();
|
||||
}
|
||||
|
||||
void CSMTools::SpellCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const ESM::Spell& spell = mSpells.getRecord (stage).get();
|
||||
|
||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId);
|
||||
|
||||
// test for empty name and description
|
||||
if (spell.mName.empty())
|
||||
messages.push_back (id.toString() + "|" + spell.mId + " has an empty name");
|
||||
|
||||
// test for invalid cost values
|
||||
if (spell.mData.mCost<0)
|
||||
messages.push_back (id.toString() + "|" + spell.mId + " has a negative spell costs");
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef CSM_TOOLS_SPELLCHECK_H
|
||||
#define CSM_TOOLS_SPELLCHECK_H
|
||||
|
||||
#include <components/esm/loadspel.hpp>
|
||||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that spell records are internally consistent
|
||||
class SpellCheckStage : public Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Spell>& mSpells;
|
||||
|
||||
public:
|
||||
|
||||
SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells);
|
||||
|
||||
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,25 @@
|
||||
|
||||
#include "cell.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
void CSMWorld::Cell::load (ESM::ESMReader &esm)
|
||||
{
|
||||
mName = mId;
|
||||
|
||||
ESM::Cell::load (esm, false);
|
||||
|
||||
if (!(mData.mFlags & Interior))
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "#" << mData.mX << " " << mData.mY;
|
||||
|
||||
mId = stream.str();
|
||||
}
|
||||
}
|
||||
|
||||
void CSMWorld::Cell::addRef (const std::string& id)
|
||||
{
|
||||
mRefs.push_back (std::make_pair (id, false));
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#ifndef CSM_WOLRD_CELL_H
|
||||
#define CSM_WOLRD_CELL_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
/// \brief Wrapper for Cell record
|
||||
struct Cell : public ESM::Cell
|
||||
{
|
||||
std::string mId;
|
||||
std::vector<std::pair<std::string, bool> > mRefs; // ID, modified
|
||||
std::vector<std::string> mDeletedRefs;
|
||||
|
||||
void load (ESM::ESMReader &esm);
|
||||
|
||||
void addRef (const std::string& id);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,319 @@
|
||||
#ifndef CSM_WOLRD_COLLECTION_H
|
||||
#define CSM_WOLRD_COLLECTION_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <stdexcept>
|
||||
#include <functional>
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "columnbase.hpp"
|
||||
|
||||
#include "collectionbase.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
/// \brief Access to ID field in records
|
||||
template<typename ESXRecordT>
|
||||
struct IdAccessor
|
||||
{
|
||||
std::string& getId (ESXRecordT& record);
|
||||
|
||||
const std::string getId (const ESXRecordT& record) const;
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
std::string& IdAccessor<ESXRecordT>::getId (ESXRecordT& record)
|
||||
{
|
||||
return record.mId;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT>
|
||||
const std::string IdAccessor<ESXRecordT>::getId (const ESXRecordT& record) const
|
||||
{
|
||||
return record.mId;
|
||||
}
|
||||
|
||||
/// \brief Single-type record collection
|
||||
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
|
||||
class Collection : public CollectionBase
|
||||
{
|
||||
std::vector<Record<ESXRecordT> > mRecords;
|
||||
std::map<std::string, int> mIndex;
|
||||
std::vector<Column<ESXRecordT> *> mColumns;
|
||||
|
||||
// not implemented
|
||||
Collection (const Collection&);
|
||||
Collection& operator= (const Collection&);
|
||||
|
||||
public:
|
||||
|
||||
Collection();
|
||||
|
||||
virtual ~Collection();
|
||||
|
||||
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,
|
||||
UniversalId::Type type = UniversalId::Type_None);
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
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,
|
||||
UniversalId::Type type = UniversalId::Type_None);
|
||||
///< If the record type does not match, an exception is thrown.
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
virtual const Record<ESXRecordT>& getRecord (const std::string& id) const;
|
||||
|
||||
virtual const Record<ESXRecordT>& getRecord (int index) const;
|
||||
|
||||
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const;
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
void addColumn (Column<ESXRecordT> *column);
|
||||
|
||||
void setRecord (int index, const Record<ESXRecordT>& record);
|
||||
///< \attention This function must not change the ID.
|
||||
};
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
Collection<ESXRecordT, IdAccessorT>::Collection()
|
||||
{}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
Collection<ESXRecordT, IdAccessorT>::~Collection()
|
||||
{
|
||||
for (typename std::vector<Column<ESXRecordT> *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)
|
||||
delete *iter;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::add (const ESXRecordT& record)
|
||||
{
|
||||
std::string id = Misc::StringUtils::lowerCase (IdAccessorT().getId (record));
|
||||
|
||||
std::map<std::string, int>::iterator iter = mIndex.find (id);
|
||||
|
||||
if (iter==mIndex.end())
|
||||
{
|
||||
Record<ESXRecordT> record2;
|
||||
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||
record2.mModified = record;
|
||||
|
||||
mRecords.push_back (record2);
|
||||
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1));
|
||||
}
|
||||
else
|
||||
{
|
||||
mRecords[iter->second].setModified (record);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
int Collection<ESXRecordT, IdAccessorT>::getSize() const
|
||||
{
|
||||
return mRecords.size();
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const
|
||||
{
|
||||
return IdAccessorT().getId (mRecords.at (index).get());
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
int Collection<ESXRecordT, IdAccessorT>::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, typename IdAccessorT>
|
||||
int Collection<ESXRecordT, IdAccessorT>::getColumns() const
|
||||
{
|
||||
return mColumns.size();
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const
|
||||
{
|
||||
return mColumns.at (column)->get (mRecords.at (index));
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)
|
||||
{
|
||||
return mColumns.at (column)->set (mRecords.at (index), data);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
const ColumnBase& Collection<ESXRecordT, IdAccessorT>::getColumn (int column) const
|
||||
{
|
||||
return *mColumns.at (column);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::addColumn (Column<ESXRecordT> *column)
|
||||
{
|
||||
mColumns.push_back (column);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::merge()
|
||||
{
|
||||
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
|
||||
iter->merge();
|
||||
|
||||
purge();
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::purge()
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (i<static_cast<int> (mRecords.size()))
|
||||
{
|
||||
if (mRecords[i].isErased())
|
||||
removeRows (i, 1);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::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;
|
||||
++iter;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIndex.erase (iter++);
|
||||
}
|
||||
}
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::appendBlankRecord (const std::string& id,
|
||||
UniversalId::Type type)
|
||||
{
|
||||
ESXRecordT record;
|
||||
IdAccessorT().getId (record) = id;
|
||||
record.blank();
|
||||
add (record);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
int Collection<ESXRecordT, IdAccessorT>::searchId (const std::string& id) const
|
||||
{
|
||||
std::string id2 = Misc::StringUtils::lowerCase(id);
|
||||
|
||||
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
|
||||
|
||||
if (iter==mIndex.end())
|
||||
return -1;
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::replace (int index, const RecordBase& record)
|
||||
{
|
||||
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,
|
||||
UniversalId::Type type)
|
||||
{
|
||||
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
|
||||
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
|
||||
dynamic_cast<const Record<ESXRecordT>&> (record).get())),
|
||||
mRecords.size()-1));
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
int Collection<ESXRecordT, IdAccessorT>::getAppendIndex (UniversalId::Type type) const
|
||||
{
|
||||
return static_cast<int> (mRecords.size());
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const
|
||||
{
|
||||
int index = getIndex (id);
|
||||
return mRecords.at (index);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const
|
||||
{
|
||||
return mRecords.at (index);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)
|
||||
{
|
||||
if (IdAccessorT().getId (mRecords.at (index).get())!=IdAccessorT().getId (record.get()))
|
||||
throw std::runtime_error ("attempt to change the ID of a record");
|
||||
|
||||
mRecords.at (index) = record;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,6 @@
|
||||
|
||||
#include "collectionbase.hpp"
|
||||
|
||||
CSMWorld::CollectionBase::CollectionBase() {}
|
||||
|
||||
CSMWorld::CollectionBase::~CollectionBase() {}
|
@ -0,0 +1,85 @@
|
||||
#ifndef CSM_WOLRD_COLLECTIONBASE_H
|
||||
#define CSM_WOLRD_COLLECTIONBASE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "universalid.hpp"
|
||||
|
||||
class QVariant;
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
struct ColumnBase;
|
||||
struct RecordBase;
|
||||
|
||||
/// \brief Base class for record collections
|
||||
///
|
||||
/// \attention Modifying records through the interface does not update connected views.
|
||||
/// Such modifications should be done through the table model interface instead unless no views
|
||||
/// are connected to the model or special precautions have been taken to send update signals
|
||||
/// manually.
|
||||
class CollectionBase
|
||||
{
|
||||
// not implemented
|
||||
CollectionBase (const CollectionBase&);
|
||||
CollectionBase& operator= (const CollectionBase&);
|
||||
|
||||
public:
|
||||
|
||||
CollectionBase();
|
||||
|
||||
virtual ~CollectionBase();
|
||||
|
||||
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;
|
||||
|
||||
// Not in use. Temporarily removed so that the implementation of RefIdCollection can continue without
|
||||
// these functions for now.
|
||||
// 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,
|
||||
UniversalId::Type type = UniversalId::Type_None) = 0;
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
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.
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
virtual void appendRecord (const RecordBase& record,
|
||||
UniversalId::Type type = UniversalId::Type_None) = 0;
|
||||
///< If the record type does not match, an exception is thrown.
|
||||
|
||||
virtual const RecordBase& getRecord (const std::string& id) const = 0;
|
||||
|
||||
virtual const RecordBase& getRecord (int index) const = 0;
|
||||
|
||||
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0;
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,301 @@
|
||||
|
||||
#include "columns.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "universalid.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
namespace Columns
|
||||
{
|
||||
struct ColumnDesc
|
||||
{
|
||||
int mId;
|
||||
const char *mName;
|
||||
};
|
||||
|
||||
const ColumnDesc sNames[] =
|
||||
{
|
||||
{ ColumnId_Value, "Value" },
|
||||
{ ColumnId_Id, "ID" },
|
||||
{ ColumnId_Modification, "Modified" },
|
||||
{ ColumnId_RecordType, "Record Type" },
|
||||
{ ColumnId_ValueType, "Value Type" },
|
||||
{ ColumnId_Description, "Description" },
|
||||
{ ColumnId_Specialisation, "Specialisation" },
|
||||
{ ColumnId_Attribute, "Attribute" },
|
||||
{ ColumnId_Name, "Name" },
|
||||
{ ColumnId_Playable, "Playable" },
|
||||
{ ColumnId_Hidden, "Hidden" },
|
||||
{ ColumnId_MaleWeight, "Male Weight" },
|
||||
{ ColumnId_FemaleWeight, "Female Weight" },
|
||||
{ ColumnId_MaleHeight, "Male Height" },
|
||||
{ ColumnId_FemaleHeight, "Female Height" },
|
||||
{ ColumnId_Volume, "Volume" },
|
||||
{ ColumnId_MinRange, "Min Range" },
|
||||
{ ColumnId_MaxRange, "Max Range" },
|
||||
{ ColumnId_SoundFile, "Sound File" },
|
||||
{ ColumnId_MapColour, "Map Colour" },
|
||||
{ ColumnId_SleepEncounter, "Sleep Encounter" },
|
||||
{ ColumnId_Texture, "Texture" },
|
||||
{ ColumnId_SpellType, "Spell Type" },
|
||||
{ ColumnId_Cost, "Cost" },
|
||||
{ ColumnId_ScriptText, "Script Text" },
|
||||
{ ColumnId_Region, "Region" },
|
||||
{ ColumnId_Cell, "Cell" },
|
||||
{ ColumnId_Scale, "Scale" },
|
||||
{ ColumnId_Owner, "Owner" },
|
||||
{ ColumnId_Soul, "Soul" },
|
||||
{ ColumnId_Faction, "Faction" },
|
||||
{ ColumnId_FactionIndex, "Faction Index" },
|
||||
{ ColumnId_Charges, "Charges" },
|
||||
{ ColumnId_Enchantment, "Enchantment" },
|
||||
{ ColumnId_Value, "Coin Value" },
|
||||
{ ColumnId_Teleport, "Teleport" },
|
||||
{ ColumnId_TeleportCell, "Teleport Cell" },
|
||||
{ ColumnId_LockLevel, "Lock Level" },
|
||||
{ ColumnId_Key, "Key" },
|
||||
{ ColumnId_Trap, "Trap" },
|
||||
{ ColumnId_BeastRace, "Beast Race" },
|
||||
{ ColumnId_AutoCalc, "Auto Calc" },
|
||||
{ ColumnId_StarterSpell, "Starter Spell" },
|
||||
{ ColumnId_AlwaysSucceeds, "Always Succeeds" },
|
||||
{ ColumnId_SleepForbidden, "Sleep Forbidden" },
|
||||
{ ColumnId_InteriorWater, "Interior Water" },
|
||||
{ ColumnId_InteriorSky, "Interior Sky" },
|
||||
{ ColumnId_Model, "Model" },
|
||||
{ ColumnId_Script, "Script" },
|
||||
{ ColumnId_Icon, "Icon" },
|
||||
{ ColumnId_Weight, "Weight" },
|
||||
{ ColumnId_EnchantmentPoints, "Enchantment Points" },
|
||||
{ ColumnId_Quality, "Quality" },
|
||||
{ ColumnId_Ai, "AI" },
|
||||
{ ColumnId_AiHello, "AI Hello" },
|
||||
{ ColumnId_AiFlee, "AI Flee" },
|
||||
{ ColumnId_AiFight, "AI Fight" },
|
||||
{ ColumnId_AiAlarm, "AI Alarm" },
|
||||
{ ColumnId_BuysWeapons, "Buys Weapons" },
|
||||
{ ColumnId_BuysArmor, "Buys Armor" },
|
||||
{ ColumnId_BuysClothing, "Buys Clothing" },
|
||||
{ ColumnId_BuysBooks, "Buys Books" },
|
||||
{ ColumnId_BuysIngredients, "Buys Ingredients" },
|
||||
{ ColumnId_BuysLockpicks, "Buys Lockpicks" },
|
||||
{ ColumnId_BuysProbes, "Buys Probes" },
|
||||
{ ColumnId_BuysLights, "Buys Lights" },
|
||||
{ ColumnId_BuysApparati, "Buys Apparati" },
|
||||
{ ColumnId_BuysRepairItems, "Buys Repair Items" },
|
||||
{ ColumnId_BuysMiscItems, "Buys Misc Items" },
|
||||
{ ColumnId_BuysPotions, "Buys Potions" },
|
||||
{ ColumnId_BuysMagicItems, "Buys Magic Items" },
|
||||
{ ColumnId_SellsSpells, "Sells Spells" },
|
||||
{ ColumnId_Trainer, "Trainer" },
|
||||
{ ColumnId_Spellmaking, "Spellmaking" },
|
||||
{ ColumnId_EnchantingService, "Enchanting Service" },
|
||||
{ ColumnId_RepairService, "Repair Serivce" },
|
||||
{ ColumnId_ApparatusType, "Apparatus Type" },
|
||||
{ ColumnId_ArmorType, "Armor Type" },
|
||||
{ ColumnId_Health, "Health" },
|
||||
{ ColumnId_ArmorValue, "Armor Value" },
|
||||
{ ColumnId_Scroll, "Scroll" },
|
||||
{ ColumnId_ClothingType, "Clothing Type" },
|
||||
{ ColumnId_WeightCapacity, "Weight Capacity" },
|
||||
{ ColumnId_OrganicContainer, "Organic Container" },
|
||||
{ ColumnId_Respawn, "Respawn" },
|
||||
{ ColumnId_CreatureType, "Creature Type" },
|
||||
{ ColumnId_SoulPoints, "Soul Points" },
|
||||
{ ColumnId_OriginalCreature, "Original Creature" },
|
||||
{ ColumnId_Biped, "Biped" },
|
||||
{ ColumnId_HasWeapon, "Has Weapon" },
|
||||
{ ColumnId_NoMovement, "No Movement" },
|
||||
{ ColumnId_Swims, "Swims" },
|
||||
{ ColumnId_Flies, "Flies" },
|
||||
{ ColumnId_Walks, "Walks" },
|
||||
{ ColumnId_Essential, "Essential" },
|
||||
{ ColumnId_SkeletonBlood, "Skeleton Blood" },
|
||||
{ ColumnId_MetalBlood, "Metal Blood" },
|
||||
{ ColumnId_OpenSound, "Open Sound" },
|
||||
{ ColumnId_CloseSound, "Close Sound" },
|
||||
{ ColumnId_Duration, "Duration" },
|
||||
{ ColumnId_Radius, "Radius" },
|
||||
{ ColumnId_Colour, "Colour" },
|
||||
{ ColumnId_Sound, "Sound" },
|
||||
{ ColumnId_Dynamic, "Dynamic" },
|
||||
{ ColumnId_Portable, "Portable" },
|
||||
{ ColumnId_NegativeLight, "Negative Light" },
|
||||
{ ColumnId_Flickering, "Flickering" },
|
||||
{ ColumnId_SlowFlickering, "Slow Flickering" },
|
||||
{ ColumnId_Pulsing, "Pulsing" },
|
||||
{ ColumnId_SlowPulsing, "Slow Pulsing" },
|
||||
{ ColumnId_Fire, "Fire" },
|
||||
{ ColumnId_OffByDefault, "Off by default" },
|
||||
{ ColumnId_IsKey, "Is Key" },
|
||||
{ ColumnId_Race, "Race" },
|
||||
{ ColumnId_Class, "Class" },
|
||||
{ Columnid_Hair, "Hair" },
|
||||
{ ColumnId_Head, "Head" },
|
||||
{ ColumnId_Female, "Female" },
|
||||
{ ColumnId_WeaponType, "Weapon Type" },
|
||||
{ ColumnId_WeaponSpeed, "Weapon Speed" },
|
||||
{ ColumnId_WeaponReach, "Weapon Reach" },
|
||||
{ ColumnId_MinChop, "Min Chop" },
|
||||
{ ColumnId_MaxChip, "Max Chop" },
|
||||
{ Columnid_MinSlash, "Min Slash" },
|
||||
{ ColumnId_MaxSlash, "Max Slash" },
|
||||
{ ColumnId_MinThrust, "Min Thrust" },
|
||||
{ ColumnId_MaxThrust, "Max Thrust" },
|
||||
{ ColumnId_Magical, "Magical" },
|
||||
{ ColumnId_Silver, "Silver" },
|
||||
{ ColumnId_Filter, "Filter" },
|
||||
|
||||
{ ColumnId_UseValue1, "Use value 1" },
|
||||
{ ColumnId_UseValue2, "Use value 2" },
|
||||
{ ColumnId_UseValue3, "Use value 3" },
|
||||
{ ColumnId_UseValue4, "Use value 4" },
|
||||
|
||||
{ ColumnId_Attribute1, "Attribute 1" },
|
||||
{ ColumnId_Attribute2, "Attribute 2" },
|
||||
|
||||
{ ColumnId_MajorSkill1, "Major Skill 1" },
|
||||
{ ColumnId_MajorSkill2, "Major Skill 2" },
|
||||
{ ColumnId_MajorSkill3, "Major Skill 3" },
|
||||
{ ColumnId_MajorSkill4, "Major Skill 4" },
|
||||
{ ColumnId_MajorSkill5, "Major Skill 5" },
|
||||
|
||||
{ ColumnId_MinorSkill1, "Minor Skill 1" },
|
||||
{ ColumnId_MinorSkill2, "Minor Skill 2" },
|
||||
{ ColumnId_MinorSkill3, "Minor Skill 3" },
|
||||
{ ColumnId_MinorSkill4, "Minor Skill 4" },
|
||||
{ ColumnId_MinorSkill5, "Minor Skill 5" },
|
||||
|
||||
{ ColumnId_Skill1, "Skill 1" },
|
||||
{ ColumnId_Skill2, "Skill 2" },
|
||||
{ ColumnId_Skill3, "Skill 3" },
|
||||
{ ColumnId_Skill4, "Skill 4" },
|
||||
{ ColumnId_Skill5, "Skill 5" },
|
||||
{ ColumnId_Skill6, "Skill 6" },
|
||||
|
||||
{ -1, 0 } // end marker
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
std::string CSMWorld::Columns::getName (ColumnId column)
|
||||
{
|
||||
for (int i=0; sNames[i].mName; ++i)
|
||||
if (column==sNames[i].mId)
|
||||
return sNames[i].mName;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int CSMWorld::Columns::getId (const std::string& name)
|
||||
{
|
||||
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||
|
||||
for (int i=0; sNames[i].mName; ++i)
|
||||
if (name2==Misc::StringUtils::lowerCase (sNames[i].mName))
|
||||
return sNames[i].mId;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
static const char *sSpecialisations[] =
|
||||
{
|
||||
"Combat", "Magic", "Stealth", 0
|
||||
};
|
||||
|
||||
static const char *sAttributes[] =
|
||||
{
|
||||
"Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality",
|
||||
"Luck", 0
|
||||
};
|
||||
|
||||
static const char *sSpellTypes[] =
|
||||
{
|
||||
"Spell", "Ability", "Blight", "Disease", "Curse", "Power", 0
|
||||
};
|
||||
|
||||
static const char *sApparatusTypes[] =
|
||||
{
|
||||
"Mortar & Pestle", "Albemic", "Calcinator", "Retort", 0
|
||||
};
|
||||
|
||||
static const char *sArmorTypes[] =
|
||||
{
|
||||
"Helmet", "Cuirass", "Left Pauldron", "Right Pauldron", "Greaves", "Boots", "Left Gauntlet",
|
||||
"Right Gauntlet", "Shield", "Left Bracer", "Right Bracer", 0
|
||||
};
|
||||
|
||||
static const char *sClothingTypes[] =
|
||||
{
|
||||
"Pants", "Shoes", "Shirt", "Belt", "Robe", "Right Glove", "Left Glove", "Skirt", "Ring",
|
||||
"Amulet", 0
|
||||
};
|
||||
|
||||
static const char *sCreatureTypes[] =
|
||||
{
|
||||
"Creature", "Deadra", "Undead", "Humanoid", 0
|
||||
};
|
||||
|
||||
static const char *sWeaponTypes[] =
|
||||
{
|
||||
"Short Blade 1H", "Long Blade 1H", "Long Blade 2H", "Blunt 1H", "Blunt 2H Close",
|
||||
"Blunt 2H Wide", "Spear 2H", "Axe 1H", "Axe 2H", "Bow", "Crossbow", "Thrown", "Arrow",
|
||||
"Bolt", 0
|
||||
};
|
||||
|
||||
static const char *sModificationEnums[] =
|
||||
{
|
||||
"Base", "Modified", "Added", "Deleted", "Deleted", 0
|
||||
};
|
||||
|
||||
static const char *sVarTypeEnums[] =
|
||||
{
|
||||
"unknown", "none", "short", "integer", "long", "float", "string", 0
|
||||
};
|
||||
|
||||
const char **getEnumNames (CSMWorld::Columns::ColumnId column)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case CSMWorld::Columns::ColumnId_Specialisation: return sSpecialisations;
|
||||
case CSMWorld::Columns::ColumnId_Attribute: return sAttributes;
|
||||
case CSMWorld::Columns::ColumnId_SpellType: return sSpellTypes;
|
||||
case CSMWorld::Columns::ColumnId_ApparatusType: return sApparatusTypes;
|
||||
case CSMWorld::Columns::ColumnId_ArmorType: return sArmorTypes;
|
||||
case CSMWorld::Columns::ColumnId_ClothingType: return sClothingTypes;
|
||||
case CSMWorld::Columns::ColumnId_CreatureType: return sCreatureTypes;
|
||||
case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes;
|
||||
case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums;
|
||||
case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums;
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CSMWorld::Columns::hasEnums (ColumnId column)
|
||||
{
|
||||
return getEnumNames (column)!=0 || column==ColumnId_RecordType;
|
||||
}
|
||||
|
||||
std::vector<std::string> CSMWorld::Columns::getEnums (ColumnId column)
|
||||
{
|
||||
std::vector<std::string> enums;
|
||||
|
||||
if (const char **table = getEnumNames (column))
|
||||
for (int i=0; table[i]; ++i)
|
||||
enums.push_back (table[i]);
|
||||
else if (column==ColumnId_RecordType)
|
||||
{
|
||||
enums.push_back (""); // none
|
||||
|
||||
for (int i=UniversalId::Type_None+1; i<UniversalId::NumberOfTypes; ++i)
|
||||
enums.push_back (UniversalId (static_cast<UniversalId::Type> (i)).getTypeName());
|
||||
}
|
||||
|
||||
return enums;
|
||||
}
|
@ -1,182 +1,192 @@
|
||||
#ifndef CSM_WOLRD_COLUMNS_H
|
||||
#define CSM_WOLRD_COLUMNS_H
|
||||
|
||||
#include "columnbase.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
template<typename ESXRecordT>
|
||||
struct FloatValueColumn : public Column<ESXRecordT>
|
||||
namespace Columns
|
||||
{
|
||||
FloatValueColumn() : Column<ESXRecordT> ("Value", ColumnBase::Display_Float) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return record.get().mValue.getFloat();
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
record2.mValue.setFloat (data.toFloat());
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct StringIdColumn : public Column<ESXRecordT>
|
||||
{
|
||||
StringIdColumn() : Column<ESXRecordT> ("ID", ColumnBase::Display_String) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return record.get().mId.c_str();
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct RecordStateColumn : public Column<ESXRecordT>
|
||||
{
|
||||
RecordStateColumn() : Column<ESXRecordT> ("*", ColumnBase::Display_Integer) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
if (record.mState==Record<ESXRecordT>::State_Erased)
|
||||
return static_cast<int> (Record<ESXRecordT>::State_Deleted);
|
||||
|
||||
return static_cast<int> (record.mState);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
record.mState = static_cast<RecordBase::State> (data.toInt());
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool isUserEditable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct FixedRecordTypeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
int mType;
|
||||
|
||||
FixedRecordTypeColumn (int type)
|
||||
: Column<ESXRecordT> ("Record Type", ColumnBase::Display_Integer, 0), mType (type) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/// \attention A var type column must be immediately followed by a suitable value column.
|
||||
template<typename ESXRecordT>
|
||||
struct VarTypeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
VarTypeColumn (ColumnBase::Display display) : Column<ESXRecordT> ("Type", display) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return static_cast<int> (record.get().mValue.getType());
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
record2.mValue.setType (static_cast<ESM::VarType> (data.toInt()));
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct VarValueColumn : public Column<ESXRecordT>
|
||||
{
|
||||
VarValueColumn() : Column<ESXRecordT> ("Value", ColumnBase::Display_Var) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
switch (record.get().mValue.getType())
|
||||
{
|
||||
case ESM::VT_String:
|
||||
|
||||
return record.get().mValue.getString().c_str(); break;
|
||||
|
||||
case ESM::VT_Int:
|
||||
case ESM::VT_Short:
|
||||
case ESM::VT_Long:
|
||||
|
||||
return record.get().mValue.getInteger(); break;
|
||||
|
||||
case ESM::VT_Float:
|
||||
|
||||
return record.get().mValue.getFloat(); break;
|
||||
|
||||
default: return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
switch (record2.mValue.getType())
|
||||
{
|
||||
case ESM::VT_String:
|
||||
|
||||
record2.mValue.setString (data.toString().toUtf8().constData());
|
||||
break;
|
||||
|
||||
case ESM::VT_Int:
|
||||
case ESM::VT_Short:
|
||||
case ESM::VT_Long:
|
||||
|
||||
record2.mValue.setInteger (data.toInt());
|
||||
break;
|
||||
|
||||
case ESM::VT_Float:
|
||||
|
||||
record2.mValue.setFloat (data.toFloat());
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
enum ColumnId
|
||||
{
|
||||
ColumnId_Value = 0,
|
||||
ColumnId_Id = 1,
|
||||
ColumnId_Modification = 2,
|
||||
ColumnId_RecordType = 3,
|
||||
ColumnId_ValueType = 4,
|
||||
ColumnId_Description = 5,
|
||||
ColumnId_Specialisation = 6,
|
||||
ColumnId_Attribute = 7,
|
||||
ColumnId_Name = 8,
|
||||
ColumnId_Playable = 9,
|
||||
ColumnId_Hidden = 10,
|
||||
ColumnId_MaleWeight = 11,
|
||||
ColumnId_FemaleWeight = 12,
|
||||
ColumnId_MaleHeight = 13,
|
||||
ColumnId_FemaleHeight = 14,
|
||||
ColumnId_Volume = 15,
|
||||
ColumnId_MinRange = 16,
|
||||
ColumnId_MaxRange = 17,
|
||||
ColumnId_SoundFile = 18,
|
||||
ColumnId_MapColour = 19,
|
||||
ColumnId_SleepEncounter = 20,
|
||||
ColumnId_Texture = 21,
|
||||
ColumnId_SpellType = 22,
|
||||
ColumnId_Cost = 23,
|
||||
ColumnId_ScriptText = 24,
|
||||
ColumnId_Region = 25,
|
||||
ColumnId_Cell = 26,
|
||||
ColumnId_Scale = 27,
|
||||
ColumnId_Owner = 28,
|
||||
ColumnId_Soul = 29,
|
||||
ColumnId_Faction = 30,
|
||||
ColumnId_FactionIndex = 31,
|
||||
ColumnId_Charges = 32,
|
||||
ColumnId_Enchantment = 33,
|
||||
ColumnId_CoinValue = 34,
|
||||
ColumnId_Teleport = 25,
|
||||
ColumnId_TeleportCell = 26,
|
||||
ColumnId_LockLevel = 27,
|
||||
ColumnId_Key = 28,
|
||||
ColumnId_Trap = 29,
|
||||
ColumnId_BeastRace = 30,
|
||||
ColumnId_AutoCalc = 31,
|
||||
ColumnId_StarterSpell = 32,
|
||||
ColumnId_AlwaysSucceeds = 33,
|
||||
ColumnId_SleepForbidden = 34,
|
||||
ColumnId_InteriorWater = 35,
|
||||
ColumnId_InteriorSky = 36,
|
||||
ColumnId_Model = 37,
|
||||
ColumnId_Script = 38,
|
||||
ColumnId_Icon = 39,
|
||||
ColumnId_Weight = 40,
|
||||
ColumnId_EnchantmentPoints = 31,
|
||||
ColumnId_Quality = 32,
|
||||
ColumnId_Ai = 33,
|
||||
ColumnId_AiHello = 34,
|
||||
ColumnId_AiFlee = 35,
|
||||
ColumnId_AiFight = 36,
|
||||
ColumnId_AiAlarm = 37,
|
||||
ColumnId_BuysWeapons = 38,
|
||||
ColumnId_BuysArmor = 39,
|
||||
ColumnId_BuysClothing = 40,
|
||||
ColumnId_BuysBooks = 41,
|
||||
ColumnId_BuysIngredients = 42,
|
||||
ColumnId_BuysLockpicks = 43,
|
||||
ColumnId_BuysProbes = 44,
|
||||
ColumnId_BuysLights = 45,
|
||||
ColumnId_BuysApparati = 46,
|
||||
ColumnId_BuysRepairItems = 47,
|
||||
ColumnId_BuysMiscItems = 48,
|
||||
ColumnId_BuysPotions = 49,
|
||||
ColumnId_BuysMagicItems = 50,
|
||||
ColumnId_SellsSpells = 51,
|
||||
ColumnId_Trainer = 52,
|
||||
ColumnId_Spellmaking = 53,
|
||||
ColumnId_EnchantingService = 54,
|
||||
ColumnId_RepairService = 55,
|
||||
ColumnId_ApparatusType = 56,
|
||||
ColumnId_ArmorType = 57,
|
||||
ColumnId_Health = 58,
|
||||
ColumnId_ArmorValue = 59,
|
||||
ColumnId_Scroll = 60,
|
||||
ColumnId_ClothingType = 61,
|
||||
ColumnId_WeightCapacity = 62,
|
||||
ColumnId_OrganicContainer = 63,
|
||||
ColumnId_Respawn = 64,
|
||||
ColumnId_CreatureType = 65,
|
||||
ColumnId_SoulPoints = 66,
|
||||
ColumnId_OriginalCreature = 67,
|
||||
ColumnId_Biped = 68,
|
||||
ColumnId_HasWeapon = 69,
|
||||
ColumnId_NoMovement = 70,
|
||||
ColumnId_Swims = 71,
|
||||
ColumnId_Flies = 72,
|
||||
ColumnId_Walks = 73,
|
||||
ColumnId_Essential = 74,
|
||||
ColumnId_SkeletonBlood = 75,
|
||||
ColumnId_MetalBlood = 76,
|
||||
ColumnId_OpenSound = 77,
|
||||
ColumnId_CloseSound = 78,
|
||||
ColumnId_Duration = 79,
|
||||
ColumnId_Radius = 80,
|
||||
ColumnId_Colour = 81,
|
||||
ColumnId_Sound = 82,
|
||||
ColumnId_Dynamic = 83,
|
||||
ColumnId_Portable = 84,
|
||||
ColumnId_NegativeLight = 85,
|
||||
ColumnId_Flickering = 86,
|
||||
ColumnId_SlowFlickering = 87,
|
||||
ColumnId_Pulsing = 88,
|
||||
ColumnId_SlowPulsing = 89,
|
||||
ColumnId_Fire = 90,
|
||||
ColumnId_OffByDefault = 91,
|
||||
ColumnId_IsKey = 92,
|
||||
ColumnId_Race = 93,
|
||||
ColumnId_Class = 94,
|
||||
Columnid_Hair = 95,
|
||||
ColumnId_Head = 96,
|
||||
ColumnId_Female = 97,
|
||||
ColumnId_WeaponType = 98,
|
||||
ColumnId_WeaponSpeed = 99,
|
||||
ColumnId_WeaponReach = 100,
|
||||
ColumnId_MinChop = 101,
|
||||
ColumnId_MaxChip = 102,
|
||||
Columnid_MinSlash = 103,
|
||||
ColumnId_MaxSlash = 104,
|
||||
ColumnId_MinThrust = 105,
|
||||
ColumnId_MaxThrust = 106,
|
||||
ColumnId_Magical = 107,
|
||||
ColumnId_Silver = 108,
|
||||
ColumnId_Filter = 109,
|
||||
|
||||
// Allocated to a separate value range, so we don't get a collision should we ever need
|
||||
// to extend the number of use values.
|
||||
ColumnId_UseValue1 = 0x10000,
|
||||
ColumnId_UseValue2 = 0x10001,
|
||||
ColumnId_UseValue3 = 0x10002,
|
||||
ColumnId_UseValue4 = 0x10003,
|
||||
|
||||
// Allocated to a separate value range, so we don't get a collision should we ever need
|
||||
// to extend the number of attributes. Note that this is not the number of different
|
||||
// attributes, but the number of attributes that can be references from a record.
|
||||
ColumnId_Attribute1 = 0x20000,
|
||||
ColumnId_Attribute2 = 0x20001,
|
||||
|
||||
// Allocated to a separate value range, so we don't get a collision should we ever need
|
||||
// to extend the number of skills. Note that this is not the number of different
|
||||
// skills, but the number of skills that can be references from a record.
|
||||
ColumnId_MajorSkill1 = 0x30000,
|
||||
ColumnId_MajorSkill2 = 0x30001,
|
||||
ColumnId_MajorSkill3 = 0x30002,
|
||||
ColumnId_MajorSkill4 = 0x30003,
|
||||
ColumnId_MajorSkill5 = 0x30004,
|
||||
|
||||
ColumnId_MinorSkill1 = 0x40000,
|
||||
ColumnId_MinorSkill2 = 0x40001,
|
||||
ColumnId_MinorSkill3 = 0x40002,
|
||||
ColumnId_MinorSkill4 = 0x40003,
|
||||
ColumnId_MinorSkill5 = 0x40004,
|
||||
|
||||
ColumnId_Skill1 = 0x50000,
|
||||
ColumnId_Skill2 = 0x50001,
|
||||
ColumnId_Skill3 = 0x50002,
|
||||
ColumnId_Skill4 = 0x50003,
|
||||
ColumnId_Skill5 = 0x50004,
|
||||
ColumnId_Skill6 = 0x50005
|
||||
};
|
||||
|
||||
std::string getName (ColumnId column);
|
||||
|
||||
int getId (const std::string& name);
|
||||
///< Will return -1 for an invalid name.
|
||||
|
||||
bool hasEnums (ColumnId column);
|
||||
|
||||
std::vector<std::string> getEnums (ColumnId column);
|
||||
///< Returns an empty vector, if \æ column isn't an enum type column.
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -1,6 +0,0 @@
|
||||
|
||||
#include "idcollection.hpp"
|
||||
|
||||
CSMWorld::IdCollectionBase::IdCollectionBase() {}
|
||||
|
||||
CSMWorld::IdCollectionBase::~IdCollectionBase() {}
|
@ -1,18 +1,48 @@
|
||||
|
||||
#include "idtableproxymodel.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "idtable.hpp"
|
||||
|
||||
void CSMWorld::IdTableProxyModel::updateColumnMap()
|
||||
{
|
||||
mColumnMap.clear();
|
||||
|
||||
if (mFilter)
|
||||
{
|
||||
std::vector<int> columns = mFilter->getReferencedColumns();
|
||||
|
||||
const IdTable& table = dynamic_cast<const IdTable&> (*sourceModel());
|
||||
|
||||
for (std::vector<int>::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter)
|
||||
mColumnMap.insert (std::make_pair (*iter,
|
||||
table.searchColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (*iter))));
|
||||
}
|
||||
}
|
||||
|
||||
bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent)
|
||||
const
|
||||
{
|
||||
if (!mFilter)
|
||||
return true;
|
||||
|
||||
return mFilter->test (
|
||||
dynamic_cast<IdTable&> (*sourceModel()), sourceRow, mColumnMap);
|
||||
}
|
||||
|
||||
CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
|
||||
: QSortFilterProxyModel (parent)
|
||||
{}
|
||||
|
||||
void CSMWorld::IdTableProxyModel::addRecord (const std::string& id)
|
||||
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
|
||||
{
|
||||
dynamic_cast<IdTable&> (*sourceModel()).addRecord (id);
|
||||
return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column));
|
||||
}
|
||||
|
||||
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
|
||||
void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::Node>& filter)
|
||||
{
|
||||
return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column));
|
||||
mFilter = filter;
|
||||
updateColumnMap();
|
||||
invalidateFilter();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue