Merge branch 'master' into esxSelector

actorid
graffy76 11 years ago
commit 7b7dfa122d

1
.gitignore vendored

@ -21,3 +21,4 @@ CMakeLists.txt.user
.cproject
.project
.settings/
.directory

@ -15,7 +15,7 @@ before_install:
- 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
- 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

@ -19,7 +19,7 @@ include (OpenMWMacros)
# Version
set (OPENMW_VERSION_MAJOR 0)
set (OPENMW_VERSION_MINOR 25)
set (OPENMW_VERSION_MINOR 26)
set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
@ -161,6 +161,20 @@ if (NOT FFMPEG_FOUND)
message(WARNING "--------------------")
endif (NOT FFMPEG_FOUND)
# TinyXML
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
if(USE_SYSTEM_TINYXML)
find_library(TINYXML_LIBRARIES tinyxml)
find_path(TINYXML_INCLUDE_DIR tinyxml.h)
message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}")
add_definitions (-DTIXML_USE_STL)
if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR)
include_directories(${TINYXML_INCLUDE_DIR})
message(STATUS "Using system TinyXML library.")
else()
message(FATAL_ERROR "Detection of system TinyXML incomplete.")
endif()
endif()
# Platform specific
if (WIN32)
@ -216,7 +230,6 @@ ENDIF(WIN32)
ENDIF(OGRE_STATIC)
include_directories("."
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS}
${OGRE_Terrain_INCLUDE_DIR}
${SDL2_INCLUDE_DIR}
${Boost_INCLUDE_DIR}
${PLATFORM_INCLUDE_DIR}
@ -334,12 +347,18 @@ IF(NOT WIN32 AND NOT APPLE)
IF (DPKG_PROGRAM)
## Debian specific
SET(CMAKE_INSTALL_PREFIX "/usr")
SET(DATAROOTDIR "share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(DATADIR "share/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
SET(ICONDIR "share/pixmaps" CACHE PATH "Set icon dir")
SET(SYSCONFDIR "../etc/openmw" CACHE PATH "Set config dir")
ELSE ()
## Non debian specific
SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir")
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
SET(LICDIR "${CMAKE_INSTALL_PREFIX}/share/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir")
# Install binaries
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
@ -367,11 +386,11 @@ IF(NOT WIN32 AND NOT APPLE)
ENDIF (DPKG_PROGRAM)
# Install icon and desktop file
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "share/applications/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "share/pixmaps/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
IF(BUILD_OPENCS)
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.desktop" DESTINATION "share/applications/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/opencs.png" DESTINATION "share/pixmaps/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/opencs.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs")
ENDIF(BUILD_OPENCS)
# Install global configuration files
@ -383,8 +402,8 @@ IF(NOT WIN32 AND NOT APPLE)
ENDIF(BUILD_OPENCS)
# Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "share/games/openmw/" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources")
INSTALL(DIRECTORY DESTINATION "share/games/openmw/data/" COMPONENT "Resources")
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources")
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
IF (DPKG_PROGRAM)
## Debian Specific
@ -515,6 +534,13 @@ if (BUILD_ESMTOOL)
endif()
if (BUILD_LAUNCHER)
if(NOT WIN32)
find_package(LIBUNSHIELD REQUIRED)
if(NOT LIBUNSHIELD_FOUND)
message(SEND_ERROR "Failed to find libunshield")
endif(NOT LIBUNSHIELD_FOUND)
endif(NOT WIN32)
add_subdirectory( apps/launcher )
endif()

@ -4,6 +4,7 @@ set(LAUNCHER
main.cpp
maindialog.cpp
playpage.cpp
textslotmsgbox.cpp
settings/gamesettings.cpp
settings/graphicssettings.cpp
@ -14,12 +15,16 @@ set(LAUNCHER
${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc
)
if(NOT WIN32)
LIST(APPEND LAUNCHER unshieldthread.cpp)
endif(NOT WIN32)
set(LAUNCHER_HEADER
datafilespage.hpp
graphicspage.hpp
maindialog.hpp
playpage.hpp
textslotmsgbox.hpp
settings/gamesettings.hpp
settings/graphicssettings.hpp
@ -30,6 +35,10 @@ set(LAUNCHER_HEADER
utils/textinputdialog.hpp
)
if(NOT WIN32)
LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp)
endif(NOT WIN32)
# Headers that must be pre-processed
set(LAUNCHER_HEADER_MOC
@ -37,11 +46,17 @@ set(LAUNCHER_HEADER_MOC
graphicspage.hpp
maindialog.hpp
playpage.hpp
textslotmsgbox.hpp
utils/checkablemessagebox.hpp
utils/textinputdialog.hpp
)
if(NOT WIN32)
LIST(APPEND LAUNCHER_HEADER_MOC unshieldthread.hpp)
endif(NOT WIN32)
set(LAUNCHER_UI
${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
@ -64,8 +79,12 @@ QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
include(${QT_USE_FILE})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
if(NOT WIN32)
include_directories(${LIBUNSHIELD_INCLUDE})
endif(NOT WIN32)
# Main executable
IF(OGRE_STATIC)
@ -94,6 +113,13 @@ target_link_libraries(omwlauncher
${QT_LIBRARIES}
components
)
if(NOT WIN32)
target_link_libraries(omwlauncher
${LIBUNSHIELD_LIBRARY}
)
endif(NOT WIN32)
if(DPKG_PROGRAM)
INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher)

@ -3,6 +3,11 @@
#include <QDesktopWidget>
#include <QMessageBox>
#include <QDir>
#ifdef __APPLE__
// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif
#include <SDL.h>
#include <cstdlib>

@ -3,6 +3,10 @@
#include <QDir>
#include <QDebug>
#ifdef __APPLE__
// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif
#include <SDL.h>
#include "maindialog.hpp"

@ -11,6 +11,12 @@
#include <QDebug>
#ifndef WIN32
#include "unshieldthread.hpp"
#endif
#include "textslotmsgbox.hpp"
#include "utils/checkablemessagebox.hpp"
#include "playpage.hpp"
@ -128,11 +134,16 @@ bool MainDialog::showFirstRunDialog()
QDir dir(path);
dir.setPath(dir.canonicalPath()); // Resolve symlinks
if (!dir.cdUp())
continue; // Cannot move from Data Files
if (dir.exists(QString("Morrowind.ini")))
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
else
{
if (!dir.cdUp())
continue; // Cannot move from Data Files
if (dir.exists(QString("Morrowind.ini")))
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
}
}
// Ask the user where the Morrowind.ini is
@ -344,6 +355,78 @@ bool MainDialog::setupLauncherSettings()
return true;
}
#ifndef WIN32
bool expansions(UnshieldThread& cd)
{
if(cd.BloodmoonDone())
{
cd.Done();
return false;
}
QMessageBox expansionsBox;
expansionsBox.setText(QObject::tr("<br>Would you like to install expansions now ? (make sure you have the disc)<br> \
If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.<br>"));
QAbstractButton* tribunalButton = NULL;
if(!cd.TribunalDone())
tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole);
QAbstractButton* bloodmoonButton = expansionsBox.addButton(QObject::tr("&Bloodmoon"), QMessageBox::ActionRole);
QAbstractButton* noneButton = expansionsBox.addButton(QObject::tr("&None"), QMessageBox::ActionRole);
expansionsBox.exec();
if(expansionsBox.clickedButton() == noneButton)
{
cd.Done();
return false;
}
else if(expansionsBox.clickedButton() == tribunalButton)
{
TextSlotMsgBox cdbox;
cdbox.setStandardButtons(QMessageBox::Cancel);
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
cd.SetTribunalPath(
QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select data1.hdr from Tribunal Installation CD (Tribunal/data1.hdr on GOTY CDs)"),
QDir::currentPath(),
QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData());
cd.start();
cdbox.exec();
}
else if(expansionsBox.clickedButton() == bloodmoonButton)
{
TextSlotMsgBox cdbox;
cdbox.setStandardButtons(QMessageBox::Cancel);
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
cd.SetBloodmoonPath(
QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select data1.hdr from Bloodmoon Installation CD (Bloodmoon/data1.hdr on GOTY CDs)"),
QDir::currentPath(),
QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData());
cd.start();
cdbox.exec();
}
return true;
}
#endif // WIN32
bool MainDialog::setupGameSettings()
{
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
@ -401,9 +484,15 @@ bool MainDialog::setupGameSettings()
Press \"Browse...\" to specify the location manually.<br>"));
QAbstractButton *dirSelectButton =
msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole);
msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole);
#ifndef WIN32
QAbstractButton *cdSelectButton =
msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole);
#endif
msgBox.exec();
msgBox.exec();
QString selectedFile;
if (msgBox.clickedButton() == dirSelectButton) {
@ -413,6 +502,40 @@ bool MainDialog::setupGameSettings()
QDir::currentPath(),
QString(tr("Morrowind master file (*.esm)")));
}
#ifndef WIN32
else if(msgBox.clickedButton() == cdSelectButton) {
UnshieldThread cd;
{
TextSlotMsgBox cdbox;
cdbox.setStandardButtons(QMessageBox::Cancel);
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
cd.SetMorrowindPath(
QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select data1.hdr from Morrowind Installation CD"),
QDir::currentPath(),
QString(tr("Installshield hdr file (*.hdr)"))).toUtf8().constData());
cd.SetOutputPath(
QFileDialog::getExistingDirectory(
NULL,
QObject::tr("Select where to extract files to"),
QDir::currentPath(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData());
cd.start();
cdbox.exec();
}
while(expansions(cd));
selectedFile = QString::fromStdString(cd.GetMWEsmPath());
}
#endif // WIN32
if (selectedFile.isEmpty())
return false; // Cancel was clicked;

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

@ -43,7 +43,8 @@ opencs_units_noqt (model/tools
opencs_units (view/doc
viewmanager view operations operation subview startup filedialog
viewmanager view operations operation subview startup filedialog newgame filewidget
adjusterwidget
)
@ -107,10 +108,18 @@ opencs_units_noqt (model/settings
settingsitem
)
opencs_units_noqt (model/filter
node unarynode narynode leafnode booleannode parser andnode ornode notnode textnode valuenode
)
opencs_hdrs_noqt (model/filter
filter
)
opencs_units (view/filter
filtercreator filterbox recordfilterbox editwidget
)
set (OPENCS_US
)
@ -127,7 +136,7 @@ if(WIN32)
set(QT_USE_QTMAIN TRUE)
endif(WIN32)
find_package(Qt4 COMPONENTS QtCore QtGui QtXml QtXmlPatterns REQUIRED)
find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork QtXml QtXmlPatterns REQUIRED)
include(${QT_USE_FILE})
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})

@ -1,23 +1,38 @@
#include "editor.hpp"
#include <QtGui/QApplication>
#include <QApplication>
#include <QLocalServer>
#include <QLocalSocket>
#include <QMessageBox>
#include "model/doc/document.hpp"
#include "model/world/data.hpp"
CS::Editor::Editor() : mViewManager (mDocumentManager)
{
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ()));
mIpcServerName = "org.openmw.OpenCS";
setupDataFiles();
mNewGame.setLocalData (mLocal);
connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ()));
connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ()));
connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ()));
connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ()));
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ()));
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles()));
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile()));
setupDataFiles();
connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)),
this, SLOT (createNewGame (const boost::filesystem::path&)));
}
void CS::Editor::setupDataFiles()
@ -35,26 +50,39 @@ void CS::Editor::setupDataFiles()
mCfgMgr.readConfiguration(variables, desc);
Files::PathContainer mDataDirs, mDataLocal;
Files::PathContainer dataDirs, dataLocal;
if (!variables["data"].empty()) {
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
dataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
}
std::string local = variables["data-local"].as<std::string>();
if (!local.empty()) {
mDataLocal.push_back(Files::PathContainer::value_type(local));
dataLocal.push_back(Files::PathContainer::value_type(local));
}
mCfgMgr.processPaths(mDataDirs);
mCfgMgr.processPaths(mDataLocal);
mCfgMgr.processPaths (dataDirs);
mCfgMgr.processPaths (dataLocal, true);
if (!dataLocal.empty())
mLocal = dataLocal[0];
else
{
QMessageBox messageBox;
messageBox.setWindowTitle (tr ("No local data path available"));
messageBox.setIcon (QMessageBox::Critical);
messageBox.setStandardButtons (QMessageBox::Ok);
messageBox.setText(tr("<br><b>OpenCS is unable to access the local data directory. This may indicate a faulty configuration or a broken install.</b>"));
messageBox.exec();
QApplication::exit (1);
return;
}
// Set the charset for reading the esm/esp files
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
mFileDialog.setEncoding(encoding);
Files::PathContainer dataDirs;
dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end());
dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end());
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
@ -65,10 +93,20 @@ void CS::Editor::setupDataFiles()
//load the settings into the userSettings instance.
const QString settingFileName = "opencs.cfg";
CSMSettings::UserSettings::instance().loadSettings(settingFileName);
}
void CS::Editor::createDocument()
void CS::Editor::createGame()
{
mStartup.hide();
if (mNewGame.isHidden())
mNewGame.show();
mNewGame.raise();
mNewGame.activateWindow();
}
void CS::Editor::createAddon()
{
mStartup.hide();
@ -91,7 +129,9 @@ void CS::Editor::openFiles()
files.push_back(path.toStdString());
}
CSMDoc::Document *document = mDocumentManager.addDocument(files, false);
/// \todo Get the save path from the file dialogue
CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false);
mViewManager.addView (document);
mFileDialog.hide();
@ -108,14 +148,70 @@ void CS::Editor::createNewFile()
files.push_back(mFileDialog.fileName().toStdString());
CSMDoc::Document *document = mDocumentManager.addDocument (files, true);
/// \todo Get the save path from the file dialogue.
CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), true);
mViewManager.addView (document);
mFileDialog.hide();
}
void CS::Editor::createNewGame (const boost::filesystem::path& file)
{
std::vector<boost::filesystem::path> files;
files.push_back (file);
CSMDoc::Document *document = mDocumentManager.addDocument (files, file, true);
mViewManager.addView (document);
mNewGame.hide();
}
void CS::Editor::showStartup()
{
if(mStartup.isHidden())
mStartup.show();
mStartup.raise();
mStartup.activateWindow();
}
void CS::Editor::showSettings()
{
if (mSettings.isHidden())
mSettings.show();
mSettings.raise();
mSettings.activateWindow();
}
bool CS::Editor::makeIPCServer()
{
mServer = new QLocalServer(this);
if(mServer->listen(mIpcServerName))
{
connect(mServer, SIGNAL(newConnection()), this, SLOT(showStartup()));
return true;
}
mServer->close();
return false;
}
void CS::Editor::connectToIPCServer()
{
mClientSocket = new QLocalSocket(this);
mClientSocket->connectToServer(mIpcServerName);
mClientSocket->close();
}
int CS::Editor::run()
{
if (mLocal.empty())
return 1;
mStartup.show();
QApplication::setQuitOnLastWindowClosed (true);

@ -2,15 +2,23 @@
#define CS_EDITOR_H
#include <QObject>
#include <QString>
#include <QLocalServer>
#include <QLocalSocket>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
#include "model/settings/usersettings.hpp"
#include "model/doc/documentmanager.hpp"
#include "view/doc/viewmanager.hpp"
#include "view/doc/startup.hpp"
#include "view/doc/filedialog.hpp"
#include "model/settings/usersettings.hpp"
#include "view/doc/newgame.hpp"
#include "view/settings/usersettingsdialog.hpp"
namespace CS
{
@ -22,9 +30,13 @@ namespace CS
CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup;
CSVDoc::NewGameDialogue mNewGame;
CSVSettings::UserSettingsDialog mSettings;
CSVDoc::FileDialog mFileDialog;
Files::ConfigurationManager mCfgMgr;
boost::filesystem::path mLocal;
void setupDataFiles();
// not implemented
@ -35,16 +47,31 @@ namespace CS
Editor();
bool makeIPCServer();
void connectToIPCServer();
int run();
///< \return error status
private slots:
void createDocument();
void createGame();
void createAddon();
void loadDocument();
void openFiles();
void createNewFile();
void createNewGame (const boost::filesystem::path& file);
void showStartup();
void showSettings();
private:
QString mIpcServerName;
QLocalServer *mServer;
QLocalSocket *mClientSocket;
};
}

@ -4,7 +4,7 @@
#include <exception>
#include <iostream>
#include <QtGui/QApplication>
#include <QApplication>
#include <QIcon>
class Application : public QApplication
@ -39,5 +39,11 @@ int main(int argc, char *argv[])
CS::Editor editor;
if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
return editor.run();
}

@ -2139,18 +2139,13 @@ void CSMDoc::Document::createBase()
}
}
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, bool new_)
: mTools (mData)
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
const boost::filesystem::path& savePath, bool new_)
: mSavePath (savePath), mTools (mData)
{
if (files.empty())
throw std::runtime_error ("Empty content file sequence");
/// \todo adjust last file name:
/// \li make sure it is located in the data-local directory (adjust path if necessary)
/// \li make sure the extension matches the new scheme (change it if necesarry)
mName = files.back().filename().string();
if (new_ && files.size()==1)
createBase();
else
@ -2201,9 +2196,9 @@ int CSMDoc::Document::getState() const
return state;
}
const std::string& CSMDoc::Document::getName() const
const boost::filesystem::path& CSMDoc::Document::getSavePath() const
{
return mName;
return mSavePath;
}
void CSMDoc::Document::save()

@ -31,7 +31,7 @@ namespace CSMDoc
private:
std::string mName; ///< \todo replace name with ESX list
boost::filesystem::path mSavePath;
CSMWorld::Data mData;
CSMTools::Tools mTools;
@ -64,15 +64,16 @@ namespace CSMDoc
public:
Document (const std::vector<boost::filesystem::path>& files, bool new_);
Document (const std::vector<boost::filesystem::path>& files,
const boost::filesystem::path& savePath, bool new_);
~Document();
QUndoStack& getUndoStack();
int getState() const;
const std::string& getName() const;
///< \todo replace with ESX list
const boost::filesystem::path& getSavePath() const;
void save();

@ -14,10 +14,10 @@ CSMDoc::DocumentManager::~DocumentManager()
delete *iter;
}
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files,
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_)
{
Document *document = new Document (files, new_);
Document *document = new Document (files, savePath, new_);
mDocuments.push_back (document);

@ -23,7 +23,8 @@ namespace CSMDoc
~DocumentManager();
Document *addDocument (const std::vector<boost::filesystem::path>& files, bool new_);
Document *addDocument (const std::vector<boost::filesystem::path>& files,
const boost::filesystem::path& savePath, bool new_);
///< The ownership of the returned document is not transferred to the caller.
///
/// \param new_ Do not load the last content file in \a files and instead create in an

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

@ -11,15 +11,14 @@ namespace CSMFilter
/// \brief Wrapper for Filter record
struct Filter : public ESM::Filter
{
enum scope
enum Scope
{
Global = 0,
Local = 1,
Session = 2,
Content = 3
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;
Scope mScope;
};
}

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

@ -7,7 +7,7 @@ void CSMWorld::Cell::load (ESM::ESMReader &esm)
{
mName = mId;
ESM::Cell::load (esm, true); /// \todo set this to false, once the bug in ESM::Cell::load is fixed
ESM::Cell::load (esm, false);
if (!(mData.mFlags & Interior))
{

@ -236,14 +236,15 @@ namespace CSMWorld
if (iter->second>=index+count)
{
iter->second -= count;
++iter;
}
else
{
mIndex.erase (iter++);
}
}
++iter;
else
++iter;
}
}

@ -257,7 +257,7 @@ namespace CSMWorld
int mIndex;
UseValueColumn (int index)
: Column<ESXRecordT> (Columns::ColumnId_UseValue1 + index - 1, ColumnBase::Display_Float),
: Column<ESXRecordT> (Columns::ColumnId_UseValue1 + index, ColumnBase::Display_Float),
mIndex (index)
{}
@ -339,7 +339,7 @@ namespace CSMWorld
int mIndex;
AttributesColumn (int index)
: Column<ESXRecordT> (Columns::ColumnId_Attribute1 + index - 1, ColumnBase::Display_Attribute),
: Column<ESXRecordT> (Columns::ColumnId_Attribute1 + index, ColumnBase::Display_Attribute),
mIndex (index)
{}
@ -372,7 +372,7 @@ namespace CSMWorld
SkillsColumn (int index, bool typePrefix = false, bool major = false)
: Column<ESXRecordT> ((typePrefix ? (
major ? Columns::ColumnId_MajorSkill1 : Columns::ColumnId_MinorSkill1) :
Columns::ColumnId_Skill1) + index - 1, ColumnBase::Display_String),
Columns::ColumnId_Skill1) + index, ColumnBase::Display_String),
mIndex (index), mMajor (major)
{}
@ -1191,6 +1191,31 @@ namespace CSMWorld
return true;
}
};
template<typename ESXRecordT>
struct FilterColumn : public Column<ESXRecordT>
{
FilterColumn() : Column<ESXRecordT> (Columns::ColumnId_Filter, ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mFilter.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mFilter = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
}
#endif

@ -3,6 +3,8 @@
#include <components/misc/stringops.hpp>
#include "universalid.hpp"
namespace CSMWorld
{
namespace Columns
@ -144,6 +146,7 @@ namespace CSMWorld
{ ColumnId_MaxThrust, "Max Thrust" },
{ ColumnId_Magical, "Magical" },
{ ColumnId_Silver, "Silver" },
{ ColumnId_Filter, "Filter" },
{ ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" },
@ -166,10 +169,11 @@ namespace CSMWorld
{ ColumnId_MinorSkill5, "Minor Skill 5" },
{ ColumnId_Skill1, "Skill 1" },
{ ColumnId_Skill1, "Skill 2" },
{ ColumnId_Skill1, "Skill 3" },
{ ColumnId_Skill1, "Skill 4" },
{ ColumnId_Skill1, "Skill 5" },
{ ColumnId_Skill2, "Skill 2" },
{ ColumnId_Skill3, "Skill 3" },
{ ColumnId_Skill4, "Skill 4" },
{ ColumnId_Skill5, "Skill 5" },
{ ColumnId_Skill6, "Skill 6" },
{ -1, 0 } // end marker
};
@ -194,4 +198,104 @@ int CSMWorld::Columns::getId (const std::string& name)
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;
}

@ -2,6 +2,7 @@
#define CSM_WOLRD_COLUMNS_H
#include <string>
#include <vector>
namespace CSMWorld
{
@ -138,6 +139,7 @@ namespace CSMWorld
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.
@ -171,13 +173,19 @@ namespace CSMWorld
ColumnId_Skill2 = 0x50001,
ColumnId_Skill3 = 0x50002,
ColumnId_Skill4 = 0x50003,
ColumnId_Skill5 = 0x50004
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.
}
}

@ -69,7 +69,9 @@ CSMWorld::RevertCommand::~RevertCommand()
void CSMWorld::RevertCommand::redo()
{
QModelIndex index = mModel.getModelIndex (mId, 1);
int column = mModel.findColumnIndex (Columns::ColumnId_Modification);
QModelIndex index = mModel.getModelIndex (mId, column);
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
if (state==RecordBase::State_ModifiedOnly)
@ -102,7 +104,9 @@ CSMWorld::DeleteCommand::~DeleteCommand()
void CSMWorld::DeleteCommand::redo()
{
QModelIndex index = mModel.getModelIndex (mId, 1);
int column = mModel.findColumnIndex (Columns::ColumnId_Modification);
QModelIndex index = mModel.getModelIndex (mId, column);
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
if (state==RecordBase::State_ModifiedOnly)

@ -150,6 +150,8 @@ CSMWorld::Data::Data() : mRefs (mCells)
mFilters.addColumn (new StringIdColumn<CSMFilter::Filter>);
mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>);
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
@ -315,6 +317,16 @@ CSMWorld::RefCollection& CSMWorld::Data::getReferences()
return mRefs;
}
const CSMWorld::IdCollection<CSMFilter::Filter>& CSMWorld::Data::getFilters() const
{
return mFilters;
}
CSMWorld::IdCollection<CSMFilter::Filter>& CSMWorld::Data::getFilters()
{
return mFilters;
}
QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id)
{
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());

@ -119,6 +119,10 @@ namespace CSMWorld
RefCollection& getReferences();
const IdCollection<CSMFilter::Filter>& getFilters() const;
IdCollection<CSMFilter::Filter>& getFilters();
QAbstractItemModel *getTableModel (const UniversalId& id);
///< If no table model is available for \a id, an exception is thrown.
///

@ -1,8 +1,36 @@
#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)
{}
@ -10,4 +38,11 @@ CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
{
return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column));
}
void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::Node>& filter)
{
mFilter = filter;
updateColumnMap();
invalidateFilter();
}

@ -1,9 +1,15 @@
#ifndef CSM_WOLRD_IDTABLEPROXYMODEL_H
#define CSM_WOLRD_IDTABLEPROXYMODEL_H
#include <string>
#include <boost/shared_ptr.hpp>
#include <map>
#include <QSortFilterProxyModel>
#include <string>
#include "../filter/node.hpp"
namespace CSMWorld
{
@ -11,11 +17,22 @@ namespace CSMWorld
{
Q_OBJECT
boost::shared_ptr<CSMFilter::Node> mFilter;
std::map<int, int> mColumnMap; // column ID, column index in this model (or -1)
private:
void updateColumnMap();
bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const;
public:
IdTableProxyModel (QObject *parent = 0);
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
void setFilter (const boost::shared_ptr<CSMFilter::Node>& filter);
};
}

@ -8,8 +8,6 @@ void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string
mId = id;
mCellId = cell.mId;
cell.getNextRef (esm, *this);
if (!mDeleted)
cell.addRef (mId);
}

@ -6,10 +6,6 @@
#include "ref.hpp"
#include "cell.hpp"
CSMWorld::RefCollection::RefCollection (Collection<Cell>& cells)
: mCells (cells), mNextId (0)
{}
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base)
{
Record<Cell> cell = mCells.getRecord (cellIndex);

@ -16,8 +16,10 @@ namespace CSMWorld
int mNextId;
public:
RefCollection (Collection<Cell>& cells);
// MSVC needs the constructor for a class inheriting a template to be defined in header
RefCollection (Collection<Cell>& cells)
: mCells (cells), mNextId (0)
{}
void load (ESM::ESMReader& reader, int cellIndex, bool base);
///< Load a sequence of references.

@ -80,7 +80,7 @@ namespace
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Filter, "Filter", 0 },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
};
@ -90,8 +90,6 @@ namespace
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
};
static const unsigned int IDARG_SIZE = sizeof (sIdArg) / sizeof (TypeData);
}
CSMWorld::UniversalId::UniversalId (const std::string& universalId)
@ -151,6 +149,22 @@ CSMWorld::UniversalId::UniversalId (Type type) : mArgumentType (ArgumentType_Non
return;
}
for (int i=0; sIdArg[i].mName; ++i)
if (type==sIdArg[i].mType)
{
mArgumentType = ArgumentType_Id;
mClass = sIdArg[i].mClass;
return;
}
for (int i=0; sIndexArg[i].mName; ++i)
if (type==sIndexArg[i].mType)
{
mArgumentType = ArgumentType_Index;
mClass = sIndexArg[i].mClass;
return;
}
throw std::logic_error ("invalid argument-less UniversalId type");
}
@ -293,25 +307,6 @@ std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listReferenceabl
return list;
}
std::pair<int, const char *> CSMWorld::UniversalId::getIdArgPair (unsigned int index)
{
std::pair<int, const char *> retPair;
if ( index < IDARG_SIZE )
{
retPair.first = sIdArg[index].mType;
retPair.second = sIdArg[index].mName;
}
return retPair;
}
unsigned int CSMWorld::UniversalId::getIdArgSize()
{
return IDARG_SIZE;
}
bool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)
{
return left.isEqual (right);

@ -34,7 +34,7 @@ namespace CSMWorld
enum Type
{
Type_None,
Type_None = 0,
Type_Globals,
Type_Global,
Type_VerificationResults,
@ -89,6 +89,8 @@ namespace CSMWorld
Type_Filters
};
enum { NumberOfTypes = Type_Filters+1 };
private:
Class mClass;
@ -102,7 +104,6 @@ namespace CSMWorld
UniversalId (const std::string& universalId);
UniversalId (Type type = Type_None);
///< Using a type for a non-argument-less UniversalId will throw an exception.
UniversalId (Type type, const std::string& id);
///< Using a type for a non-ID-argument UniversalId will throw an exception.
@ -134,9 +135,6 @@ namespace CSMWorld
///< Will return an empty string, if no icon is available.
static std::vector<Type> listReferenceableTypes();
static std::pair<int, const char *> getIdArgPair (unsigned int index);
static unsigned int getIdArgSize ();
};
bool operator== (const UniversalId& left, const UniversalId& right);

@ -0,0 +1,91 @@
#include "adjusterwidget.hpp"
#include <stdexcept>
#include <string>
#include <boost/filesystem.hpp>
#include <QHBoxLayout>
#include <QLabel>
#include <QStyle>
CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent)
: QWidget (parent), mValid (false)
{
QHBoxLayout *layout = new QHBoxLayout (this);
mIcon = new QLabel (this);
layout->addWidget (mIcon, 0);
mMessage = new QLabel (this);
mMessage->setWordWrap (true);
mMessage->setSizePolicy (QSizePolicy (QSizePolicy::Minimum, QSizePolicy::Minimum));
layout->addWidget (mMessage, 1);
setName ("", false);
setLayout (layout);
}
void CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData)
{
mLocalData = localData;
}
boost::filesystem::path CSVDoc::AdjusterWidget::getPath() const
{
if (!mValid)
throw std::logic_error ("invalid content file path");
return mResultPath;
}
void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
{
QString message;
if (name.isEmpty())
{
mValid = false;
message = "No name.";
}
else
{
boost::filesystem::path path (name.toUtf8().data());
path.replace_extension (addon ? ".omwaddon" : ".omwgame");
if (path.parent_path().string()==mLocalData.string())
{
// path already points to the local data directory
message = QString::fromUtf8 (("Will be saved as: " + path.native()).c_str());
mResultPath = path;
mValid = true;
}
else
{
// path points somewhere else or is a leaf name.
path = mLocalData / path.filename();
message = QString::fromUtf8 (("Will be saved as: " + path.native()).c_str());
mResultPath = path;
mValid = true;
if (boost::filesystem::exists (path))
{
/// \todo add an user setting to make this an error.
message += "<p>But a file with the same name already exists. If you continue, it will be overwritten.";
}
}
}
mMessage->setText (message);
mIcon->setPixmap (style()->standardIcon (
mValid ? QStyle::SP_MessageBoxInformation : QStyle::SP_MessageBoxWarning).
pixmap (QSize (16, 16)));
emit stateChanged (mValid);
}

@ -0,0 +1,41 @@
#ifndef CSV_DOC_ADJUSTERWIDGET_H
#define CSV_DOC_ADJUSTERWIDGET_H
#include <boost/filesystem/path.hpp>
#include <QWidget>
class QLabel;
namespace CSVDoc
{
class AdjusterWidget : public QWidget
{
Q_OBJECT
boost::filesystem::path mLocalData;
QLabel *mMessage;
QLabel *mIcon;
bool mValid;
boost::filesystem::path mResultPath;
public:
AdjusterWidget (QWidget *parent = 0);
void setLocalData (const boost::filesystem::path& localData);
boost::filesystem::path getPath() const;
///< This function must not be called if there is no valid path.
public slots:
void setName (const QString& name, bool addon);
signals:
void stateChanged (bool valid);
};
}
#endif

@ -0,0 +1,53 @@
#include "filewidget.hpp"
#include <QHBoxLayout>
#include <QLineEdit>
#include <QLabel>
#include <QRegExpValidator>
#include <QRegExp>
QString CSVDoc::FileWidget::getExtension() const
{
return mAddon ? ".omwaddon" : ".omwgame";
}
CSVDoc::FileWidget::FileWidget (QWidget *parent) : QWidget (parent), mAddon (false)
{
QHBoxLayout *layout = new QHBoxLayout (this);
mInput = new QLineEdit (this);
mInput->setValidator (new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$")));
layout->addWidget (mInput, 1);
mType = new QLabel (this);
layout ->addWidget (mType);
connect (mInput, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
setLayout (layout);
}
void CSVDoc::FileWidget::setType (bool addon)
{
mAddon = addon;
mType->setText (getExtension());
}
QString CSVDoc::FileWidget::getName() const
{
QString text = mInput->text();
if (text.isEmpty())
return "";
return text + getExtension();
}
void CSVDoc::FileWidget::textChanged (const QString& text)
{
emit nameChanged (getName(), mAddon);
}

@ -0,0 +1,40 @@
#ifndef CSV_DOC_FILEWIDGET_H
#define CSV_DOC_FILEWIDGET_H
#include <QWidget>
class QLabel;
class QString;
class QLineEdit;
namespace CSVDoc
{
class FileWidget : public QWidget
{
Q_OBJECT
bool mAddon;
QLineEdit *mInput;
QLabel *mType;
QString getExtension() const;
public:
FileWidget (QWidget *parent = 0);
void setType (bool addon);
QString getName() const;
private slots:
void textChanged (const QString& text);
signals:
void nameChanged (const QString& file, bool addon);
};
}
#endif

@ -0,0 +1,68 @@
#include "newgame.hpp"
#include <QApplication>
#include <QDesktopWidget>
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QPushButton>
#include "filewidget.hpp"
#include "adjusterwidget.hpp"
CSVDoc::NewGameDialogue::NewGameDialogue()
{
setWindowTitle ("Create New Game");
QVBoxLayout *layout = new QVBoxLayout (this);
mFileWidget = new FileWidget (this);
mFileWidget->setType (false);
layout->addWidget (mFileWidget, 1);
mAdjusterWidget = new AdjusterWidget (this);
layout->addWidget (mAdjusterWidget, 1);
QDialogButtonBox *buttons = new QDialogButtonBox (this);
mCreate = new QPushButton ("Create", this);
mCreate->setDefault (true);
mCreate->setEnabled (false);
buttons->addButton (mCreate, QDialogButtonBox::AcceptRole);
QPushButton *cancel = new QPushButton ("Cancel", this);
buttons->addButton (cancel, QDialogButtonBox::RejectRole);
layout->addWidget (buttons);
setLayout (layout);
connect (mAdjusterWidget, SIGNAL (stateChanged (bool)), this, SLOT (stateChanged (bool)));
connect (mCreate, SIGNAL (clicked()), this, SLOT (create()));
connect (cancel, SIGNAL (clicked()), this, SLOT (reject()));
connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)),
mAdjusterWidget, SLOT (setName (const QString&, bool)));
QRect scr = QApplication::desktop()->screenGeometry();
QRect rect = geometry();
move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y());
}
void CSVDoc::NewGameDialogue::setLocalData (const boost::filesystem::path& localData)
{
mAdjusterWidget->setLocalData (localData);
}
void CSVDoc::NewGameDialogue::stateChanged (bool valid)
{
mCreate->setEnabled (valid);
}
void CSVDoc::NewGameDialogue::create()
{
emit createRequest (mAdjusterWidget->getPath());
}

@ -0,0 +1,44 @@
#ifndef CSV_DOC_NEWGAME_H
#define CSV_DOC_NEWGAME_H
#include <boost/filesystem/path.hpp>
#include <QDialog>
#include <QMetaType>
Q_DECLARE_METATYPE (boost::filesystem::path)
class QPushButton;
namespace CSVDoc
{
class FileWidget;
class AdjusterWidget;
class NewGameDialogue : public QDialog
{
Q_OBJECT
QPushButton *mCreate;
FileWidget *mFileWidget;
AdjusterWidget *mAdjusterWidget;
public:
NewGameDialogue();
void setLocalData (const boost::filesystem::path& localData);
signals:
void createRequest (const boost::filesystem::path& file);
private slots:
void stateChanged (bool valid);
void create();
};
}
#endif

@ -3,21 +3,106 @@
#include <QApplication>
#include <QDesktopWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QRect>
#include <QGridLayout>
#include <QLabel>
#include <QIcon>
#include <QPushButton>
QPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon)
{
int column = mColumn--;
QPushButton *button = new QPushButton (this);
button->setIcon (QIcon (icon));
button->setSizePolicy (QSizePolicy (QSizePolicy::Preferred, QSizePolicy::Preferred));
mLayout->addWidget (button, 0, column);
mLayout->addWidget (new QLabel (label, this), 1, column, Qt::AlignCenter);
int width = mLayout->itemAtPosition (1, column)->widget()->sizeHint().width();
CSVDoc::StartupDialogue::StartupDialogue()
if (width>mWidth)
mWidth = width;
return button;
}
QWidget *CSVDoc::StartupDialogue::createButtons()
{
QHBoxLayout *layout = new QHBoxLayout (this);
QWidget *widget = new QWidget (this);
QPushButton *createDocument = new QPushButton ("new", this);
connect (createDocument, SIGNAL (clicked()), this, SIGNAL (createDocument()));
layout->addWidget (createDocument);
mLayout = new QGridLayout (widget);
QPushButton *loadDocument = new QPushButton ("load", this);
/// \todo add icons
QPushButton *loadDocument = addButton ("Edit A Content File", QIcon (":startup/edit-content"));
connect (loadDocument, SIGNAL (clicked()), this, SIGNAL (loadDocument()));
layout->addWidget (loadDocument);
QPushButton *createAddon = addButton ("Create A New Addon", QIcon (":startup/create-addon"));
connect (createAddon, SIGNAL (clicked()), this, SIGNAL (createAddon()));
QPushButton *createGame = addButton ("Create A New Game", QIcon (":startup/create-game"));
connect (createGame, SIGNAL (clicked()), this, SIGNAL (createGame()));
for (int i=0; i<3; ++i)
mLayout->setColumnMinimumWidth (i, mWidth);
mLayout->setRowMinimumHeight (0, mWidth);
mLayout->setSizeConstraint (QLayout::SetMinimumSize);
mLayout->setHorizontalSpacing (32);
mLayout->setContentsMargins (16, 16, 16, 8);
loadDocument->setIconSize (QSize (mWidth, mWidth));
createGame->setIconSize (QSize (mWidth, mWidth));
createAddon->setIconSize (QSize (mWidth, mWidth));
widget->setLayout (mLayout);
return widget;
}
QWidget *CSVDoc::StartupDialogue::createTools()
{
QWidget *widget = new QWidget (this);
QHBoxLayout *layout = new QHBoxLayout (widget);
layout->setDirection (QBoxLayout::RightToLeft);
layout->setContentsMargins (4, 4, 4, 4);
QPushButton *config = new QPushButton (widget);
config->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
config->setIcon (QIcon (":startup/configure"));
layout->addWidget (config);
layout->addWidget (new QWidget, 1); // dummy widget; stops buttons from taking all the space
widget->setLayout (layout);
connect (config, SIGNAL (clicked()), this, SIGNAL (editConfig()));
return widget;
}
CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
{
setWindowTitle ("Open CS");
QVBoxLayout *layout = new QVBoxLayout (this);
layout->setContentsMargins (0, 0, 0, 0);
layout->addWidget (createButtons());
layout->addWidget (createTools());
setLayout (layout);

@ -3,21 +3,43 @@
#include <QWidget>
class QGridLayout;
class QString;
class QPushButton;
class QWidget;
class QIcon;
namespace CSVDoc
{
class StartupDialogue : public QWidget
{
Q_OBJECT
private:
int mWidth;
int mColumn;
QGridLayout *mLayout;
QPushButton *addButton (const QString& label, const QIcon& icon);
QWidget *createButtons();
QWidget *createTools();
public:
StartupDialogue();
signals:
void createDocument();
void createGame();
void createAddon();
void loadDocument();
void editConfig();
};
}

@ -27,9 +27,13 @@ void CSVDoc::View::setupFileMenu()
{
QMenu *file = menuBar()->addMenu (tr ("&File"));
QAction *new_ = new QAction (tr ("New"), this);
connect (new_, SIGNAL (triggered()), this, SIGNAL (newDocumentRequest()));
file->addAction (new_);
QAction *newGame = new QAction (tr ("New Game"), this);
connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest()));
file->addAction (newGame);
QAction *newAddon = new QAction (tr ("New Addon"), this);
connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest()));
file->addAction (newAddon);
QAction *open = new QAction (tr ("&Open"), this);
connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest()));
@ -67,7 +71,7 @@ void CSVDoc::View::setupEditMenu()
edit->addAction (mRedo);
QAction *userSettings = new QAction (tr ("&Preferences"), this);
connect (userSettings, SIGNAL (triggered()), this, SLOT (showUserSettings()));
connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest()));
edit->addAction (userSettings);
}
@ -180,7 +184,7 @@ void CSVDoc::View::updateTitle()
{
std::ostringstream stream;
stream << mDocument->getName();
stream << mDocument->getSavePath().filename().string();
if (mDocument->getState() & CSMDoc::State_Modified)
stream << " *";
@ -415,13 +419,6 @@ void CSVDoc::View::exit()
emit exitApplicationRequest (this);
}
void CSVDoc::View::showUserSettings()
{
CSVSettings::UserSettingsDialog *settingsDialog = new CSVSettings::UserSettingsDialog(this);
settingsDialog->show();
}
void CSVDoc::View::resizeViewWidth (int width)
{
if (width >= 0)

@ -106,12 +106,16 @@ namespace CSVDoc
signals:
void newDocumentRequest();
void newGameRequest();
void newAddonRequest();
void loadDocumentRequest();
void exitApplicationRequest (CSVDoc::View *view);
void editSettingsRequest();
public slots:
void addSubView (const CSMWorld::UniversalId& id);
@ -160,8 +164,6 @@ namespace CSVDoc
void addFiltersSubView();
void showUserSettings();
void toggleShowStatusBar (bool show);
};
}

@ -8,6 +8,7 @@
#include "../../model/doc/documentmanager.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/world/columns.hpp"
#include "../world/util.hpp"
#include "../world/enumdelegate.hpp"
@ -43,51 +44,6 @@ void CSVDoc::ViewManager::updateIndices()
CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
: mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false)
{
static const char *sSpecialisations[] =
{
"Combat", "Magic", "Stealth", 0
};
static const char *sAttributes[] =
{
"Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality",
"Luck", 0
};
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
};
mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection;
mDelegateFactories->add (CSMWorld::ColumnBase::Display_GmstVarType,
@ -96,38 +52,37 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType,
new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_Specialisation,
new CSVWorld::EnumDelegateFactory (sSpecialisations));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_Attribute,
new CSVWorld::EnumDelegateFactory (sAttributes, true));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_SpellType,
new CSVWorld::EnumDelegateFactory (sSpellTypes));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_ApparatusType,
new CSVWorld::EnumDelegateFactory (sApparatusTypes));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_ArmorType,
new CSVWorld::EnumDelegateFactory (sArmorTypes));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_ClothingType,
new CSVWorld::EnumDelegateFactory (sClothingTypes));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_RecordState,
new CSVWorld::RecordStatusDelegateFactory());
mDelegateFactories->add (CSMWorld::ColumnBase::Display_CreatureType,
new CSVWorld::EnumDelegateFactory (sCreatureTypes));
mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType,
new CSVWorld::RefIdTypeDelegateFactory());
mDelegateFactories->add (CSMWorld::ColumnBase::Display_WeaponType,
new CSVWorld::EnumDelegateFactory (sWeaponTypes));
struct Mapping
{
CSMWorld::ColumnBase::Display mDisplay;
CSMWorld::Columns::ColumnId mColumnId;
bool mAllowNone;
};
mDelegateFactories->add (CSMWorld::ColumnBase::Display_RecordState,
new CSVWorld::RecordStatusDelegateFactory() );
static const Mapping sMapping[] =
{
{ CSMWorld::ColumnBase::Display_Specialisation, CSMWorld::Columns::ColumnId_Specialisation, false },
{ CSMWorld::ColumnBase::Display_Attribute, CSMWorld::Columns::ColumnId_Attribute, true },
{ CSMWorld::ColumnBase::Display_SpellType, CSMWorld::Columns::ColumnId_SpellType, false },
{ CSMWorld::ColumnBase::Display_ApparatusType, CSMWorld::Columns::ColumnId_ApparatusType, false },
{ CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false },
{ CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false },
{ CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false },
{ CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false }
};
mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType,
new CSVWorld::RefIdTypeDelegateFactory() );
for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)
mDelegateFactories->add (sMapping[i].mDisplay, new CSVWorld::EnumDelegateFactory (
CSMWorld::Columns::getEnums (sMapping[i].mColumnId), sMapping[i].mAllowNone));
connect (&CSMSettings::UserSettings::instance(), SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)),
this, SLOT (slotUpdateEditorSetting (const QString &, const QString &)));
this, SLOT (slotUpdateEditorSetting (const QString &, const QString &)));
}
CSVDoc::ViewManager::~ViewManager()
@ -152,13 +107,14 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document)
View *view = new View (*this, document, countViews (document)+1);
mViews.push_back (view);
view->show();
connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest()));
connect (view, SIGNAL (newGameRequest ()), this, SIGNAL (newGameRequest()));
connect (view, SIGNAL (newAddonRequest ()), this, SIGNAL (newAddonRequest()));
connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest()));
connect (view, SIGNAL (editSettingsRequest()), this, SIGNAL (editSettingsRequest()));
updateIndices();

@ -55,12 +55,16 @@ namespace CSVDoc
signals:
void newDocumentRequest();
void newGameRequest();
void newAddonRequest();
void loadDocumentRequest();
void closeMessageBox();
void editSettingsRequest();
public slots:
void exitApplication (CSVDoc::View *view);

@ -0,0 +1,58 @@
#include "editwidget.hpp"
#include <QAbstractItemModel>
#include "../../model/world/data.hpp"
CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent)
: QLineEdit (parent), mParser (data)
{
mPalette = palette();
connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters);
connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)),
this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)),
Qt::QueuedConnection);
connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)),
Qt::QueuedConnection);
connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (filterRowsInserted (const QModelIndex&, int, int)),
Qt::QueuedConnection);
}
void CSVFilter::EditWidget::textChanged (const QString& text)
{
if (mParser.parse (text.toUtf8().constData()))
{
setPalette (mPalette);
emit filterChanged (mParser.getFilter());
}
else
{
QPalette palette (mPalette);
palette.setColor (QPalette::Text, Qt::red);
setPalette (palette);
/// \todo improve error reporting; mark only the faulty part
}
}
void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
textChanged (text());
}
void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end)
{
textChanged (text());
}
void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end)
{
textChanged (text());
}

@ -0,0 +1,48 @@
#ifndef CSV_FILTER_EDITWIDGET_H
#define CSV_FILTER_EDITWIDGET_H
#include <boost/shared_ptr.hpp>
#include <QLineEdit>
#include <QPalette>
#include "../../model/filter/parser.hpp"
#include "../../model/filter/node.hpp"
class QModelIndex;
namespace CSMWorld
{
class Data;
}
namespace CSVFilter
{
class EditWidget : public QLineEdit
{
Q_OBJECT
CSMFilter::Parser mParser;
QPalette mPalette;
public:
EditWidget (CSMWorld::Data& data, QWidget *parent = 0);
signals:
void filterChanged (boost::shared_ptr<CSMFilter::Node> filter);
private slots:
void textChanged (const QString& text);
void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void filterRowsRemoved (const QModelIndex& parent, int start, int end);
void filterRowsInserted (const QModelIndex& parent, int start, int end);
};
}
#endif

@ -0,0 +1,24 @@
#include "filterbox.hpp"
#include <QHBoxLayout>
#include "recordfilterbox.hpp"
CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent)
: QWidget (parent)
{
QHBoxLayout *layout = new QHBoxLayout (this);
layout->setContentsMargins (0, 0, 0, 0);
RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this);
layout->addWidget (recordFilterBox);
setLayout (layout);
connect (recordFilterBox,
SIGNAL (filterChanged (boost::shared_ptr<CSMFilter::Node>)),
this, SIGNAL (recordFilterChanged (boost::shared_ptr<CSMFilter::Node>)));
}

@ -0,0 +1,30 @@
#ifndef CSV_FILTER_FILTERBOX_H
#define CSV_FILTER_FILTERBOX_H
#include <QWidget>
#include "../../model/filter/node.hpp"
namespace CSMWorld
{
class Data;
}
namespace CSVFilter
{
class FilterBox : public QWidget
{
Q_OBJECT
public:
FilterBox (CSMWorld::Data& data, QWidget *parent = 0);
signals:
void recordFilterChanged (boost::shared_ptr<CSMFilter::Node> filter);
};
}
#endif

@ -0,0 +1,63 @@
#include "filtercreator.hpp"
#include <QComboBox>
#include <QLabel>
#include "../../model/filter/filter.hpp"
std::string CSVFilter::FilterCreator::getNamespace() const
{
switch (mScope->currentIndex())
{
case CSMFilter::Filter::Scope_Project: return "project::";
case CSMFilter::Filter::Scope_Session: return "session::";
}
return "";
}
void CSVFilter::FilterCreator::update()
{
mNamespace->setText (QString::fromUtf8 (getNamespace().c_str()));
GenericCreator::update();
}
std::string CSVFilter::FilterCreator::getId() const
{
return getNamespace() + GenericCreator::getId();
}
CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id)
: GenericCreator (data, undoStack, id)
{
mNamespace = new QLabel ("::", this);
insertAtBeginning (mNamespace, false);
mScope = new QComboBox (this);
mScope->addItem ("Project");
mScope->addItem ("Session");
/// \ŧodo re-enable for OpenMW 1.1
// mScope->addItem ("Content");
connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int)));
insertAtBeginning (mScope, false);
QLabel *label = new QLabel ("Scope", this);
insertAtBeginning (label, false);
mScope->setCurrentIndex (1);
}
void CSVFilter::FilterCreator::reset()
{
GenericCreator::reset();
}
void CSVFilter::FilterCreator::setScope (int index)
{
update();
}

@ -0,0 +1,41 @@
#ifndef CSV_FILTER_FILTERCREATOR_H
#define CSV_FILTER_FILTERCREATOR_H
class QComboBox;
class QLabel;
#include "../world/genericcreator.hpp"
namespace CSVFilter
{
class FilterCreator : public CSVWorld::GenericCreator
{
Q_OBJECT
QComboBox *mScope;
QLabel *mNamespace;
private:
std::string getNamespace() const;
protected:
void update();
virtual std::string getId() const;
public:
FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id);
virtual void reset();
private slots:
void setScope (int index);
};
}
#endif

@ -0,0 +1,27 @@
#include "recordfilterbox.hpp"
#include <QHBoxLayout>
#include <QLabel>
#include "editwidget.hpp"
CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent)
: QWidget (parent)
{
QHBoxLayout *layout = new QHBoxLayout (this);
layout->setContentsMargins (0, 0, 0, 0);
layout->addWidget (new QLabel ("Record Filter", this));
EditWidget *editWidget = new EditWidget (data, this);
layout->addWidget (editWidget);
setLayout (layout);
connect (
editWidget, SIGNAL (filterChanged (boost::shared_ptr<CSMFilter::Node>)),
this, SIGNAL (filterChanged (boost::shared_ptr<CSMFilter::Node>)));
}

@ -0,0 +1,34 @@
#ifndef CSV_FILTER_RECORDFILTERBOX_H
#define CSV_FILTER_RECORDFILTERBOX_H
#include <boost/shared_ptr.hpp>
#include <QWidget>
#include <QHBoxLayout>
#include "../../model/filter/node.hpp"
namespace CSMWorld
{
class Data;
}
namespace CSVFilter
{
class RecordFilterBox : public QWidget
{
Q_OBJECT
public:
RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0);
signals:
void filterChanged (boost::shared_ptr<CSMFilter::Node> filter);
};
}
#endif

@ -1,5 +1,7 @@
#include "usersettingsdialog.hpp"
#include <boost/filesystem/path.hpp>
#include <QApplication>
#include <QDesktopWidget>
#include <QWidget>
@ -9,14 +11,14 @@
#include <QFile>
#include <QPushButton>
#include <QDockWidget>
#include <QGridLayout>
#include <QApplication>
#include <QDesktopWidget>
#include "../../model/settings/support.hpp"
#include "datadisplayformatpage.hpp"
#include "windowpage.hpp"
#include "../../model/settings/support.hpp"
#include <boost/filesystem/path.hpp>
#include "settingwidget.hpp"
CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) :
@ -29,7 +31,11 @@ CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) :
connect (mListWidget,
SIGNAL (currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
this,
SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*)));
SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*)));
QRect scr = QApplication::desktop()->screenGeometry();
QRect rect = geometry();
move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y());
}
CSVSettings::UserSettingsDialog::~UserSettingsDialog()

@ -109,6 +109,18 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names, bool all
add (i, names[i]);
}
CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::string>& names,
bool allowNone)
{
if (allowNone)
add (-1, "");
int size = static_cast<int> (names.size());
for (int i=0; i<size; ++i)
add (i, names[i].c_str());
}
CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (QUndoStack& undoStack,
QObject *parent) const
{

@ -54,6 +54,9 @@ namespace CSVWorld
///< \param names Array of char pointer with a 0-pointer as end mark
/// \param allowNone Use value of -1 for "none selected" (empty string)
EnumDelegateFactory (const std::vector<std::string>& names, bool allowNone = false);
/// \param allowNone Use value of -1 for "none selected" (empty string)
virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const;
///< The ownership of the returned CommandDelegate is transferred to the caller.

@ -1,8 +1,11 @@
#include "recordstatusdelegate.hpp"
#include <QPainter>
#include <QApplication>
#include <QUndoStack>
#include "../../model/settings/usersettings.hpp"
#include "../../model/world/columns.hpp"
CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values,
const IconList & icons,
@ -37,9 +40,14 @@ bool CSVWorld::RecordStatusDelegate::updateEditorSetting (const QString &setting
CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()
{
DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_BaseOnly, "Base", ":./base.png");
DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_Deleted, "Deleted", ":./removed.png");
DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_Erased, "Deleted", ":./removed.png");
DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_Modified, "Modified", ":./modified.png");
DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_ModifiedOnly, "Added", ":./added.png");
std::vector<std::string> enums =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);
static const char *sIcons[] =
{
":./base.png", ":./modified.png", ":./added.png", ":./removed.png", ":./removed.png", 0
};
for (int i=0; sIcons[i]; ++i)
add (i, enums.at (i).c_str(), sIcons[i]);
}

@ -1,4 +1,5 @@
#include "refidtypedelegate.hpp"
#include "../../model/world/universalid.hpp"
CSVWorld::RefIdTypeDelegate::RefIdTypeDelegate
@ -6,6 +7,26 @@ CSVWorld::RefIdTypeDelegate::RefIdTypeDelegate
: DataDisplayDelegate (values, icons, undoStack, parent)
{}
bool CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue)
{
if (settingName == "Referenceable ID Type Display")
{
if (settingValue == "Icon and Text")
mDisplayMode = Mode_IconAndText;
else if (settingValue == "Icon Only")
mDisplayMode = Mode_IconOnly;
else if (settingValue == "Text Only")
mDisplayMode = Mode_TextOnly;
return true;
}
return false;
}
CSVWorld::RefIdTypeDelegateFactory::RefIdTypeDelegateFactory()
{
UidTypeList uIdList = buildUidTypeList();
@ -39,22 +60,3 @@ CSVWorld::RefIdTypeDelegateFactory::UidTypeList CSVWorld::RefIdTypeDelegateFacto
return list;
}
bool CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue)
{
if (settingName == "Referenceable ID Type Display")
{
if (settingValue == "Icon and Text")
mDisplayMode = Mode_IconAndText;
else if (settingValue == "Icon Only")
mDisplayMode = Mode_IconOnly;
else if (settingValue == "Text Only")
mDisplayMode = Mode_TextOnly;
return true;
}
return false;
}

@ -1,25 +0,0 @@
#include "refrecordtypedelegate.hpp"
#include "../../model/world/universalid.hpp"
CSVWorld::RefRecordTypeDelegate::RefRecordTypeDelegate
(const std::vector<std::pair<int, QString> > &values, QUndoStack& undoStack, QObject *parent)
: EnumDelegate (values, undoStack, parent)
{}
CSVWorld::RefRecordTypeDelegateFactory::RefRecordTypeDelegateFactory()
{
unsigned int argSize = CSMWorld::UniversalId::getIdArgSize();
for (unsigned int i = 0; i < argSize; i++)
{
std::pair<int, const char *> idPair = CSMWorld::UniversalId::getIdArgPair(i);
mValues.push_back (std::pair<int, QString>(idPair.first, QString::fromUtf8(idPair.second)));
}
}
CSVWorld::CommandDelegate *CSVWorld::RefRecordTypeDelegateFactory::makeDelegate (QUndoStack& undoStack,
QObject *parent) const
{
return new RefRecordTypeDelegate (mValues, undoStack, parent);
}

@ -1,58 +0,0 @@
#ifndef REFRECORDTYPEDELEGATE_HPP
#define REFRECORDTYPEDELEGATE_HPP
#include "enumdelegate.hpp"
#include "util.hpp"
namespace CSVWorld
{
class RefRecordTypeDelegate : public EnumDelegate
{
public:
RefRecordTypeDelegate (const std::vector<std::pair<int, QString> > &mValues, QUndoStack& undoStack, QObject *parent);
};
class RefRecordTypeDelegateFactory : public CommandDelegateFactory
{
std::vector<std::pair<int, QString> > mValues;
public:
RefRecordTypeDelegateFactory();
virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const;
///< The ownership of the returned CommandDelegate is transferred to the caller.
};
}
/*
class VarTypeDelegate : public EnumDelegate
{
private:
virtual void addCommands (QAbstractItemModel *model,
const QModelIndex& index, int type) const;
public:
VarTypeDelegate (const std::vector<std::pair<int, QString> >& values,
QUndoStack& undoStack, QObject *parent);
};
class VarTypeDelegateFactory : public CommandDelegateFactory
{
std::vector<std::pair<int, QString> > mValues;
public:
VarTypeDelegateFactory (ESM::VarType type0 = ESM::VT_Unknown,
ESM::VarType type1 = ESM::VT_Unknown, ESM::VarType type2 = ESM::VT_Unknown,
ESM::VarType type3 = ESM::VT_Unknown);
virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const;
///< The ownership of the returned CommandDelegate is transferred to the caller.
void add (ESM::VarType type);
};
*/
#endif // REFRECORDTYPEDELEGATE_HPP

@ -3,6 +3,8 @@
#include "../doc/subviewfactoryimp.hpp"
#include "../filter/filtercreator.hpp"
#include "tablesubview.hpp"
#include "dialoguesubview.hpp"
#include "scriptsubview.hpp"
@ -33,7 +35,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
CSMWorld::UniversalId::Type_Regions,
CSMWorld::UniversalId::Type_Birthsigns,
CSMWorld::UniversalId::Type_Spells,
CSMWorld::UniversalId::Type_Filters,
CSMWorld::UniversalId::Type_None // end marker
};
@ -56,4 +57,9 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
// Other stuff (combined record tables)
manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory<RegionMapSubView>);
manager.add (CSMWorld::UniversalId::Type_Filters,
new CSVDoc::SubViewFactoryWithCreator<TableSubView,
CreatorFactory<CSVFilter::FilterCreator> >);
}

@ -257,9 +257,9 @@ void CSVWorld::Table::tableSizeUpdate()
int deleted = 0;
int modified = 0;
if (mModel->columnCount()>0)
if (mProxyModel->columnCount()>0)
{
int rows = mModel->rowCount();
int rows = mProxyModel->rowCount();
for (int i=0; i<rows; ++i)
{
@ -292,4 +292,9 @@ void CSVWorld::Table::requestFocus (const std::string& id)
if (index.isValid())
scrollTo (index, QAbstractItemView::PositionAtTop);
}
void CSVWorld::Table::recordFilterChanged (boost::shared_ptr<CSMFilter::Node> filter)
{
mProxyModel->setFilter (filter);
}

@ -6,6 +6,8 @@
#include <QTableView>
#include "../../model/filter/node.hpp"
class QUndoStack;
class QAction;
@ -85,6 +87,7 @@ namespace CSVWorld
void requestFocus (const std::string& id);
void recordFilterChanged (boost::shared_ptr<CSMFilter::Node> filter);
};
}

@ -63,12 +63,15 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto
mCreator = creatorFactory.makeCreator (data, undoStack, id);
mLayout->addWidget (mCreator);
if (mCreator)
{
mLayout->addWidget (mCreator);
connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone()));
connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone()));
connect (mCreator, SIGNAL (requestFocus (const std::string&)),
this, SIGNAL (requestFocus (const std::string&)));
connect (mCreator, SIGNAL (requestFocus (const std::string&)),
this, SIGNAL (requestFocus (const std::string&)));
}
}
void CSVWorld::TableBottomBox::setEditLock (bool locked)

@ -5,6 +5,8 @@
#include "../../model/doc/document.hpp"
#include "../filter/filterbox.hpp"
#include "table.hpp"
#include "tablebottombox.hpp"
#include "creator.hpp"
@ -23,6 +25,10 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
layout->insertWidget (0, mTable =
new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2);
CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this);
layout->insertWidget (0, filterBox);
QWidget *widget = new QWidget;
widget->setLayout (layout);
@ -44,6 +50,10 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
mTable, SLOT (requestFocus (const std::string&)));
connect (filterBox,
SIGNAL (recordFilterChanged (boost::shared_ptr<CSMFilter::Node>)),
mTable, SLOT (recordFilterChanged (boost::shared_ptr<CSMFilter::Node>)));
}
void CSVWorld::TableSubView::setEditLock (bool locked)

@ -4,6 +4,7 @@
#include <QUndoStack>
#include "../../model/world/commands.hpp"
#include "../../model/world/columns.hpp"
void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QModelIndex& index, int type)
const
@ -75,29 +76,11 @@ CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate (QUndo
void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type)
{
struct Name
{
ESM::VarType mType;
const char *mName;
};
std::vector<std::string> enums =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_ValueType);
static const Name sNames[] =
{
{ ESM::VT_None, "empty" },
{ ESM::VT_Short, "short" },
{ ESM::VT_Int, "integer" },
{ ESM::VT_Long, "long" },
{ ESM::VT_Float, "float" },
{ ESM::VT_String, "string" },
{ ESM::VT_Unknown, 0 } // end marker
};
for (int i=0; sNames[i].mName; ++i)
if (sNames[i].mType==type)
{
mValues.push_back (std::make_pair (type, sNames[i].mName));
return;
}
throw std::logic_error ("Unsupported variable type");
if (type<0 && type>=enums.size())
throw std::logic_error ("Unsupported variable type");
mValues.push_back (std::make_pair (type, QString::fromUtf8 (enums[type].c_str())));
}

@ -15,8 +15,9 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
actors objects renderinginterface localmap occlusionquery water shadows
compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction
terrainstorage
)
add_openmw_dir (mwinput
@ -104,7 +105,6 @@ add_definitions(${SOUND_DEFINE})
target_link_libraries(openmw
${OGRE_LIBRARIES}
${OGRE_Terrain_LIBRARY}
${OGRE_STATIC_PLUGINS}
${Boost_LIBRARIES}
${OPENAL_LIBRARY}
@ -119,6 +119,10 @@ target_link_libraries(openmw
components
)
if (USE_SYSTEM_TINYXML)
target_link_libraries(openmw ${TINYXML_LIBRARIES})
endif()
if (NOT UNIX)
target_link_libraries(openmw ${SDL2MAIN_LIBRARY})
endif()

@ -69,8 +69,8 @@ void OMW::Engine::setAnimationVerbose(bool animverbose)
bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt)
{
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame);
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused);
MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame);
return true;
}
@ -123,6 +123,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch);
MWBase::Environment::get().getWindowManager()->onFrame(frametime);
MWBase::Environment::get().getWindowManager()->update();
}
catch (const std::exception& e)
{
@ -385,26 +386,39 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
loadBSA();
// Create input and UI first to set up a bootstrapping environment for
// showing a loading screen and keeping the window responsive while doing so
std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string();
bool keybinderUserExists = boost::filesystem::exists(keybinderUser);
MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists);
mEnvironment.setInputManager (input);
MWGui::WindowManager* window = new MWGui::WindowManager(
mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"),
mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding);
mEnvironment.setWindowManager (window);
if (mNewGame)
mEnvironment.getWindowManager()->setNewGame(true);
// Create the world
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins,
mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap,
mActivationDistanceOverride));
MWBase::Environment::get().getWorld()->setupPlayer();
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
window->initUI();
window->renderWorldMap();
//Load translation data
mTranslationDataStorage.setEncoder(mEncoder);
for (size_t i = 0; i < mMaster.size(); i++)
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[i]);
// Create window manager - this manages all the MW-specific GUI windows
Compiler::registerExtensions (mExtensions);
mEnvironment.setWindowManager (new MWGui::WindowManager(
mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"),
mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding));
if (mNewGame)
mEnvironment.getWindowManager()->setNewGame(true);
// Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound));
@ -422,16 +436,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setJournal (new MWDialogue::Journal);
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage));
// Sets up the input system
// Get the path for the keybinder xml file
std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string();
bool keybinderUserExists = boost::filesystem::exists(keybinderUser);
mEnvironment.setInputManager (new MWInput::InputManager (*mOgre,
MWBase::Environment::get().getWorld()->getPlayer(),
*MWBase::Environment::get().getWindowManager(), *this, keybinderUser, keybinderUserExists));
mEnvironment.getWorld()->renderPlayer();
if (!mNewGame)
@ -597,4 +601,4 @@ void OMW::Engine::setStartupScript (const std::string& path)
void OMW::Engine::setActivationDistanceOverride (int distance)
{
mActivationDistanceOverride = distance;
}
}

@ -39,9 +39,11 @@ namespace MWBase
Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */
Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */
Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */
Play_NoTrack = 1<<2 /* (3D only) Play the sound at the given object's position
Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position
* but do not keep it updated (the sound will not move with
* the object and will not stop when the object is deleted. */
Play_LoopNoEnv = Play_Loop | Play_NoEnv
};
enum PlayType {
Play_TypeSfx = 1<<3, /* Normal SFX sound */

@ -9,6 +9,8 @@
#include <components/translation/translation.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include "../mwmechanics/stat.hpp"
#include "../mwgui/mode.hpp"
@ -253,9 +255,6 @@ namespace MWBase
virtual void executeInConsole (const std::string& path) = 0;
virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total) = 0;
virtual void loadingDone() = 0;
virtual void enableRest() = 0;
virtual bool getRestEnabled() = 0;
virtual bool getJournalAllowed() = 0;
@ -282,6 +281,8 @@ namespace MWBase
virtual const Translation::Storage& getTranslationDataStorage() const = 0;
virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0;
virtual Loading::Listener* getLoadingScreen() = 0;
};
}

@ -220,9 +220,10 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0;
///< Return pointer to the object the player is looking at, if it is within activation range
/// Returns a pointer to the object the provided object is facing (if within the
/// specified distance). This will attempt to use the "Bip01 Head" node as a basis.
virtual MWWorld::Ptr getFacedObject(const MWWorld::Ptr &ptr, float distance) = 0;
/// Returns a pointer to the object the provided object would hit (if within the
/// specified distance), and the point where the hit occurs. This will attempt to
/// use the "Head" node as a basis.
virtual std::pair<MWWorld::Ptr,Ogre::Vector3> getHitContact(const MWWorld::Ptr &ptr, float distance) = 0;
virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0;
///< Adjust position after load to be on ground. Must be called after model load.
@ -250,8 +251,9 @@ namespace MWBase
virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0;
///< Convert position to cell numbers
virtual void doPhysics (const MWWorld::PtrMovementList &actors, float duration) = 0;
///< Run physics simulation and modify \a world accordingly.
virtual void queueMovement(const MWWorld::Ptr &ptr, const Ogre::Vector3 &velocity) = 0;
///< Queues movement for \a ptr (in local space), to be applied in the next call to
/// doPhysics.
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
///< cast a Ray and return true if there is an object in the ray path.
@ -371,7 +373,7 @@ namespace MWBase
/// \todo this does not belong here
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual void stopVideo() = 0;
virtual void frameStarted (float dt) = 0;
virtual void frameStarted (float dt, bool paused) = 0;
/// Find default position inside exterior cell specified by name
/// \return false if exterior with given name not exists, true otherwise
@ -393,6 +395,8 @@ namespace MWBase
/// Sets the NPC's Acrobatics skill to match the fWerewolfAcrobatics GMST.
/// It only applies to the current form the NPC is in.
virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0;
virtual bool toggleGodMode() = 0;
};
}

@ -6,19 +6,26 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld//cellstore.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/action.hpp"
#include "../mwworld/failedaction.hpp"
#include "../mwworld/nullaction.hpp"
#include "../mwrender/actors.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwgui/tooltips.hpp"
#include "../mwmechanics/npcstats.hpp"
namespace MWClass
{
void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{
const std::string model = getModel(ptr);
if (!model.empty()) {
@ -94,7 +101,23 @@ namespace MWClass
return info;
}
boost::shared_ptr<MWWorld::Action> Activator::activate(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor) const
{
if(get(actor).isNpc() && get(actor).getNpcStats(actor).isWerewolf())
{
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfActivator");
boost::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
if(sound) action->setSound(sound->mId);
return action;
}
return boost::shared_ptr<MWWorld::Action>(new MWWorld::NullAction);
}
MWWorld::Ptr
Activator::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
{

@ -31,6 +31,9 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const;
///< Generate action for activation
static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const;

@ -332,14 +332,15 @@ namespace MWClass
float dist = 100.0f * (!weapon.isEmpty() ?
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
gmst.find("fHandToHandReach")->getFloat());
MWWorld::Ptr victim = world->getFacedObject(ptr, dist);
// TODO: Use second to work out the hit angle and where to spawn the blood effect
MWWorld::Ptr victim = world->getHitContact(ptr, dist).first;
if(victim.isEmpty()) // Didn't hit anything
return;
const MWWorld::Class &othercls = MWWorld::Class::get(victim);
if(!othercls.isActor()) // Can't hit non-actors
return;
MWMechanics::CreatureStats &otherstats = getCreatureStats(victim);
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
if(otherstats.isDead()) // Can't hit dead actors
return;
@ -1021,6 +1022,16 @@ namespace MWClass
}
return "";
}
if(name == "land")
{
MWBase::World *world = MWBase::Environment::get().getWorld();
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isUnderwater(ptr.getCell(), pos))
return "DefaultLandWater";
if(world->isOnGround(ptr))
return "Body Fall Medium";
return "";
}
if(name == "swimleft")
return "Swim Left";
if(name == "swimright")
@ -1034,8 +1045,6 @@ namespace MWClass
return "";
if(name == "scream")
return "";
if(name == "land")
return "";
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}

@ -88,8 +88,13 @@ namespace MWGui
mDragAndDropWidget->setVisible(false);
targetModel->copyItem(mItem, mDraggedCount);
mSourceModel->removeItem(mItem, mDraggedCount);
// If item is dropped where it was taken from, we don't need to do anything -
// otherwise, do the transfer
if (targetModel != mSourceModel)
{
targetModel->copyItem(mItem, mDraggedCount);
mSourceModel->removeItem(mItem, mDraggedCount);
}
mSourceModel->update();

@ -74,13 +74,12 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
void ContainerItemModel::copyItem (const ItemStack& item, size_t count)
{
const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1];
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
throw std::runtime_error("Item to copy needs to be from a different container!");
int origCount = item.mBase.getRefData().getCount();
item.mBase.getRefData().setCount(count);
MWWorld::ContainerStoreIterator it = MWWorld::Class::get(source).getContainerStore(source).add(item.mBase, source);
if (*it != item.mBase)
item.mBase.getRefData().setCount(origCount);
else
item.mBase.getRefData().setCount(origCount + count); // item copied onto itself
source.getClass().getContainerStore(source).add(item.mBase, source);
item.mBase.getRefData().setCount(origCount);
}
void ContainerItemModel::removeItem (const ItemStack& item, size_t count)

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

Loading…
Cancel
Save