1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-05-02 21:11:24 +00:00

Merge branch 'master' into pathgrid-edit

Conflicts:
	apps/opencs/view/render/cell.cpp
	apps/opencs/view/render/mousestate.cpp
This commit is contained in:
cc9cii 2015-05-31 11:12:39 +10:00
commit a02448f0ba
258 changed files with 4373 additions and 951 deletions

View file

@ -98,6 +98,7 @@ Programmers
Sebastian Wick (swick) Sebastian Wick (swick)
Sergey Shambir Sergey Shambir
sir_herrbatka sir_herrbatka
smbas
Stefan Galowicz (bogglez) Stefan Galowicz (bogglez)
Stanislav Bobrov (Jiub) Stanislav Bobrov (Jiub)
Sylvain Thesnieres (Garvek) Sylvain Thesnieres (Garvek)
@ -109,6 +110,13 @@ Programmers
Vincent Heuken Vincent Heuken
vocollapse vocollapse
Manual
------
Bodillium
Cramal
sir_herrbatka
Packagers Packagers
--------- ---------

View file

@ -1,3 +1,49 @@
0.36.0
------
Bug #923: Editor: Operations-Multithreading is broken
Bug #1317: Erene Llenim in Seyda Neen does not walk around
Bug #1405: Water rendering glitch near Seyda Neen lighthouse
Bug #1621: "Error Detecting Morrowind Installation" in the default directory
Bug #2216: Creating a clone of the player stops you moving.
Bug #2387: Casting bound weapon spell doesn't switch to "ready weapon" mode
Bug #2407: Default to (0, 0) when "unknown cell" is encountered.
Bug #2411: enchanted item charges don't update/refresh if spell list window is pinned open
Bug #2428: Editor: cloning / creating new container class results in invalid omwaddon file - openmw-0.35
Bug #2429: Editor - cloning omits some values or sets different values than the original has
Bug #2430: NPC with negative fatigue don't fall (LGNPC Vivec, Foreign Quarter v2.21)
Bug #2432: Error on startup with Uvirith's Legacy enabled
Bug #2435: Editor: changed entries in the objects window are not shown as such
Bug #2437: Editor: changing an entry of a container/NPC/clothing/ingredient/globals will not be saved in the omwaddon file
Bug #2447: Editor doesn't save terrain information
Bug #2451: Editor not listing files with accented characters
Bug #2453: Chargen: sex, race and hair sliders not initialized properly
Bug #2459: Minor terrain clipping through statics due to difference in triangle alignment
Bug #2461: Invisible sound mark has collision in Sandus Ancestral Tomb
Bug #2465: tainted gold stack
Bug #2475: cumulative stacks of 100 point fortify skill speechcraft boosts do not apply correctly
Bug #2498: Editor: crash when issuing undo command after the table subview is closed
Bug #2500: Editor: object table - can't undo delete record
Bug #2518: OpenMW detect spell returns false positives
Bug #2521: NPCs don't react to stealing when inventory menu is open.
Bug #2525: Can't click on red dialogue choice [rise of house telvanni][60fffec]
Bug #2530: GetSpellEffects not working as in vanilla
Bug #2557: Crash on first launch after choosing "Run installation wizard"
Feature #139: Editor: Global Search & Replace
Feature #1219: Editor: Add dialogue mode only columns
Feature #2024: Hotkey for hand to hand (i.e. unequip any weapon)
Feature #2119: "Always Sneak" key bind
Feature #2262: Editor: Handle moved instances
Feature #2425: Editor: Add start script table
Feature #2426: Editor: start script record verifier
Feature #2480: Launcher: Multiselect entries in the Data Files list
Feature #2505: Editor: optionally show a line number column in the script editor
Feature #2512: Editor: Offer use of monospace fonts in the script editor as an option
Feature #2514: Editor: focus on ID input field on clone/add
Feature #2519: it is not possible to change icons that appear on the map after casting the Detect <animal | enchantment | key> spells
Task #2460: OS X: Use Application Support directory as user data path
Task #2516: Editor: Change References / Referenceables terminology
0.35.1 0.35.1
------ ------

View file

@ -19,8 +19,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
message(STATUS "Configuring OpenMW...") message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 35) set(OPENMW_VERSION_MINOR 36)
set(OPENMW_VERSION_RELEASE 1) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
set(OPENMW_VERSION_TAGHASH "") set(OPENMW_VERSION_TAGHASH "")
@ -134,9 +134,11 @@ endif()
# Platform specific # Platform specific
if (WIN32) if (WIN32)
if(NOT MINGW)
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
set(PLATFORM_INCLUDE_DIR "platform") set(PLATFORM_INCLUDE_DIR "platform")
add_definitions(-DBOOST_ALL_NO_LIB) add_definitions(-DBOOST_ALL_NO_LIB)
endif(NOT MINGW)
# Suppress WinMain(), provided by SDL # Suppress WinMain(), provided by SDL
add_definitions(-DSDL_MAIN_HANDLED) add_definitions(-DSDL_MAIN_HANDLED)
@ -351,7 +353,7 @@ endif()
# CXX Compiler settings # CXX Compiler settings
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++98 -pedantic -Wno-long-long") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long")
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE) if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION) execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION)

View file

@ -1,12 +1,12 @@
OpenMW OpenMW
====== ======
[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg?style=plastic)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) [![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
OpenMW is an attempt at recreating the engine for the popular role-playing game OpenMW is an attempt at recreating the engine for the popular role-playing game
Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
* Version: 0.35.1 * Version: 0.36.0
* License: GPL (see docs/license/GPL3.txt for more information) * License: GPL (see docs/license/GPL3.txt for more information)
* Website: http://www.openmw.org * Website: http://www.openmw.org
* IRC: #openmw on irc.freenode.net * IRC: #openmw on irc.freenode.net

View file

@ -24,10 +24,10 @@
const char *Launcher::DataFilesPage::mDefaultContentListName = "Default"; const char *Launcher::DataFilesPage::mDefaultContentListName = "Default";
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent) Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent)
: mCfgMgr(cfg) : QWidget(parent)
, mCfgMgr(cfg)
, mGameSettings(gameSettings) , mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings) , mLauncherSettings(launcherSettings)
, QWidget(parent)
{ {
ui.setupUi (this); ui.setupUi (this);
setObjectName ("DataFilesPage"); setObjectName ("DataFilesPage");

View file

@ -36,13 +36,13 @@ QString getAspect(int x, int y)
} }
Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent) Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent)
: mOgre(NULL) : QWidget(parent)
, mOgre(NULL)
, mSelectedRenderSystem(NULL) , mSelectedRenderSystem(NULL)
, mOpenGLRenderSystem(NULL) , mOpenGLRenderSystem(NULL)
, mDirect3DRenderSystem(NULL) , mDirect3DRenderSystem(NULL)
, mCfgMgr(cfg) , mCfgMgr(cfg)
, mGraphicsSettings(graphicsSetting) , mGraphicsSettings(graphicsSetting)
, QWidget(parent)
{ {
setObjectName ("GraphicsPage"); setObjectName ("GraphicsPage");
setupUi(this); setupUi(this);

View file

@ -59,13 +59,15 @@ int main(int argc, char *argv[])
Launcher::MainDialog mainWin; Launcher::MainDialog mainWin;
if (!mainWin.showFirstRunDialog()) Launcher::FirstRunDialogResult result = mainWin.showFirstRunDialog();
if (result == Launcher::FirstRunDialogResultFailure)
return 0; return 0;
// if (!mainWin.setup()) { // if (!mainWin.setup()) {
// return 0; // return 0;
// } // }
if (result == Launcher::FirstRunDialogResultContinue)
mainWin.show(); mainWin.show();
int returnValue = app.exec(); int returnValue = app.exec();

View file

@ -25,7 +25,7 @@
using namespace Process; using namespace Process;
Launcher::MainDialog::MainDialog(QWidget *parent) Launcher::MainDialog::MainDialog(QWidget *parent)
: mGameSettings(mCfgMgr), QMainWindow (parent) : QMainWindow(parent), mGameSettings (mCfgMgr)
{ {
setupUi(this); setupUi(this);
@ -148,10 +148,10 @@ void Launcher::MainDialog::createPages()
} }
bool Launcher::MainDialog::showFirstRunDialog() Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
{ {
if (!setupLauncherSettings()) if (!setupLauncherSettings())
return false; return FirstRunDialogResultFailure;
if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true")) if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true"))
{ {
@ -176,14 +176,14 @@ bool Launcher::MainDialog::showFirstRunDialog()
if (msgBox.clickedButton() == wizardButton) if (msgBox.clickedButton() == wizardButton)
{ {
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) { if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) {
return false; return FirstRunDialogResultFailure;
} else { } else {
return true; return FirstRunDialogResultWizard;
} }
} }
} }
return setup(); return setup() ? FirstRunDialogResultContinue : FirstRunDialogResultFailure;
} }
bool Launcher::MainDialog::setup() bool Launcher::MainDialog::setup()

View file

@ -31,6 +31,13 @@ namespace Launcher
class UnshieldThread; class UnshieldThread;
class SettingsPage; class SettingsPage;
enum FirstRunDialogResult
{
FirstRunDialogResultFailure,
FirstRunDialogResultContinue,
FirstRunDialogResultWizard
};
#ifndef WIN32 #ifndef WIN32
bool expansions(Launcher::UnshieldThread& cd); bool expansions(Launcher::UnshieldThread& cd);
#endif #endif
@ -44,7 +51,7 @@ namespace Launcher
~MainDialog(); ~MainDialog();
bool setup(); bool setup();
bool showFirstRunDialog(); FirstRunDialogResult showFirstRunDialog();
bool reloadSettings(); bool reloadSettings();
bool writeSettings(); bool writeSettings();

View file

@ -18,10 +18,10 @@ using namespace Process;
Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg, Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,
Config::GameSettings &gameSettings, Config::GameSettings &gameSettings,
Config::LauncherSettings &launcherSettings, MainDialog *parent) Config::LauncherSettings &launcherSettings, MainDialog *parent)
: mCfgMgr(cfg) : QWidget(parent)
, mCfgMgr(cfg)
, mGameSettings(gameSettings) , mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings) , mLauncherSettings(launcherSettings)
, QWidget(parent)
, mMain(parent) , mMain(parent)
{ {
setupUi(this); setupUi(this);

View file

@ -18,6 +18,10 @@ target_link_libraries(openmw-iniimporter
components components
) )
if (MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")
endif()
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(openmw-iniimporter gcov) target_link_libraries(openmw-iniimporter gcov)

View file

@ -41,13 +41,13 @@ opencs_units (model/tools
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage startscriptcheck search searchoperation searchstage pathgridcheck
) )
opencs_units (view/doc opencs_units (view/doc
viewmanager view operations operation subview startup filedialog newgame viewmanager view operations operation subview startup filedialog newgame
filewidget adjusterwidget loader globaldebugprofilemenu runlogsubview filewidget adjusterwidget loader globaldebugprofilemenu runlogsubview sizehint
) )
@ -64,6 +64,7 @@ opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview cellcreator referenceablecreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world

View file

@ -23,8 +23,8 @@
CS::Editor::Editor (OgreInit::OgreInit& ogreInit) CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
: mUserSettings (mCfgMgr), mOverlaySystem (0), mDocumentManager (mCfgMgr), : mUserSettings (mCfgMgr), mOverlaySystem (0), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mViewManager (mDocumentManager), mPid(""),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL), mPid(""), mLock() mLock(), mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
{ {
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig(); std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
@ -67,9 +67,11 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)), connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)),
this, SLOT(createNewFile (const boost::filesystem::path&))); this, SLOT(createNewFile (const boost::filesystem::path&)));
connect (&mFileDialog, SIGNAL (rejected()), this, SLOT (cancelFileDialog ()));
connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)),
this, SLOT (createNewGame (const boost::filesystem::path&))); this, SLOT (createNewGame (const boost::filesystem::path&)));
connect (&mNewGame, SIGNAL (cancelCreateGame()), this, SLOT (cancelCreateGame ()));
} }
CS::Editor::~Editor () CS::Editor::~Editor ()
@ -176,12 +178,40 @@ void CS::Editor::createGame()
mNewGame.activateWindow(); mNewGame.activateWindow();
} }
void CS::Editor::cancelCreateGame()
{
if (!mDocumentManager.isEmpty())
return;
mNewGame.hide();
if (mStartup.isHidden())
mStartup.show();
mStartup.raise();
mStartup.activateWindow();
}
void CS::Editor::createAddon() void CS::Editor::createAddon()
{ {
mStartup.hide(); mStartup.hide();
mFileDialog.showDialog (CSVDoc::ContentAction_New); mFileDialog.showDialog (CSVDoc::ContentAction_New);
} }
void CS::Editor::cancelFileDialog()
{
if (!mDocumentManager.isEmpty())
return;
mFileDialog.hide();
if (mStartup.isHidden())
mStartup.show();
mStartup.raise();
mStartup.activateWindow();
}
void CS::Editor::loadDocument() void CS::Editor::loadDocument()
{ {
mStartup.hide(); mStartup.hide();

View file

@ -87,6 +87,8 @@ namespace CS
void createGame(); void createGame();
void createAddon(); void createAddon();
void cancelCreateGame();
void cancelFileDialog();
void loadDocument(); void loadDocument();
void openFiles (const boost::filesystem::path &path); void openFiles (const boost::filesystem::path &path);

View file

@ -2251,11 +2251,12 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
const std::vector<std::string>& blacklistedScripts) const std::vector<std::string>& blacklistedScripts)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager), : mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager),
mTools (*this), mResDir(resDir), mTools (*this),
mProjectPath ((configuration.getUserDataPath() / "projects") / mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")), (savePath.filename().string() + ".project")),
mSavingOperation (*this, mProjectPath, encoding), mSavingOperation (*this, mProjectPath, encoding),
mSaving (&mSavingOperation), mSaving (&mSavingOperation),
mResDir(resDir),
mRunner (mProjectPath), mPhysics(boost::shared_ptr<CSVWorld::PhysicsSystem>()) mRunner (mProjectPath), mPhysics(boost::shared_ptr<CSVWorld::PhysicsSystem>())
{ {
if (mContentFiles.empty()) if (mContentFiles.empty())

View file

@ -49,6 +49,11 @@ CSMDoc::DocumentManager::~DocumentManager()
delete *iter; delete *iter;
} }
bool CSMDoc::DocumentManager::isEmpty()
{
return mDocuments.empty();
}
void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath, void CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_) bool new_)
{ {

View file

@ -59,6 +59,8 @@ namespace CSMDoc
/// Ask OGRE for a list of available resources. /// Ask OGRE for a list of available resources.
void listResources(); void listResources();
bool isEmpty();
private: private:
boost::filesystem::path mResDir; boost::filesystem::path mResDir;

View file

@ -231,8 +231,29 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)
record.mState==CSMWorld::RecordBase::State_Modified || record.mState==CSMWorld::RecordBase::State_Modified ||
record.mState==CSMWorld::RecordBase::State_ModifiedOnly) record.mState==CSMWorld::RecordBase::State_ModifiedOnly)
{ {
mState.getSubRecords()[Misc::StringUtils::lowerCase (record.get().mCell)] std::string cellId = record.get().mOriginalCell.empty() ?
.push_back (i); record.get().mCell : record.get().mOriginalCell;
std::deque<int>& indices =
mState.getSubRecords()[Misc::StringUtils::lowerCase (cellId)];
// collect moved references at the end of the container
bool interior = cellId.substr (0, 1)!="#";
std::ostringstream stream;
if (!interior)
{
// recalculate the ref's cell location
std::pair<int, int> index = record.get().getCellIndex();
stream << "#" << index.first << " " << index.second;
}
// An empty mOriginalCell is meant to indicate that it is the same as
// the current cell. It is possible that a moved ref is moved again.
if ((record.get().mOriginalCell.empty() ?
record.get().mCell : record.get().mOriginalCell) != stream.str() && !interior)
indices.push_back (i);
else
indices.push_front (i);
} }
} }
} }
@ -253,7 +274,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
const CSMWorld::Record<CSMWorld::Cell>& cell = const CSMWorld::Record<CSMWorld::Cell>& cell =
mDocument.getData().getCells().getRecord (stage); mDocument.getData().getCells().getRecord (stage);
std::map<std::string, std::vector<int> >::const_iterator references = std::map<std::string, std::deque<int> >::const_iterator references =
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId)); mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
if (cell.mState==CSMWorld::RecordBase::State_Modified || if (cell.mState==CSMWorld::RecordBase::State_Modified ||
@ -284,7 +305,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
// write references // write references
if (references!=mState.getSubRecords().end()) if (references!=mState.getSubRecords().end())
{ {
for (std::vector<int>::const_iterator iter (references->second.begin()); for (std::deque<int>::const_iterator iter (references->second.begin());
iter!=references->second.end(); ++iter) iter!=references->second.end(); ++iter)
{ {
const CSMWorld::Record<CSMWorld::CellRef>& ref = const CSMWorld::Record<CSMWorld::CellRef>& ref =
@ -293,6 +314,32 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
if (ref.mState==CSMWorld::RecordBase::State_Modified || if (ref.mState==CSMWorld::RecordBase::State_Modified ||
ref.mState==CSMWorld::RecordBase::State_ModifiedOnly) ref.mState==CSMWorld::RecordBase::State_ModifiedOnly)
{ {
// recalculate the ref's cell location
std::ostringstream stream;
if (!interior)
{
std::pair<int, int> index = ref.get().getCellIndex();
stream << "#" << index.first << " " << index.second;
}
// An empty mOriginalCell is meant to indicate that it is the same as
// the current cell. It is possible that a moved ref is moved again.
if ((ref.get().mOriginalCell.empty() ? ref.get().mCell : ref.get().mOriginalCell)
!= stream.str() && !interior)
{
ESM::MovedCellRef moved;
moved.mRefNum = ref.get().mRefNum;
// Need to fill mTarget with the ref's new position.
std::istringstream istream (stream.str().c_str());
char ignore;
istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1];
ref.get().mRefNum.save (mState.getWriter(), false, "MVRF");
mState.getWriter().writeHNT ("CNDT", moved.mTarget, 8);
}
ref.get().save (mState.getWriter()); ref.get().save (mState.getWriter());
} }
else if (ref.mState==CSMWorld::RecordBase::State_Deleted) else if (ref.mState==CSMWorld::RecordBase::State_Deleted)

View file

@ -64,7 +64,7 @@ bool CSMDoc::SavingState::isProjectFile() const
return mProjectFile; return mProjectFile;
} }
std::map<std::string, std::vector<int> >& CSMDoc::SavingState::getSubRecords() std::map<std::string, std::deque<int> >& CSMDoc::SavingState::getSubRecords()
{ {
return mSubRecords; return mSubRecords;
} }

View file

@ -3,6 +3,7 @@
#include <fstream> #include <fstream>
#include <map> #include <map>
#include <deque>
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
@ -26,7 +27,7 @@ namespace CSMDoc
ESM::ESMWriter mWriter; ESM::ESMWriter mWriter;
boost::filesystem::path mProjectPath; boost::filesystem::path mProjectPath;
bool mProjectFile; bool mProjectFile;
std::map<std::string, std::vector<int> > mSubRecords; // record ID, list of subrecords std::map<std::string, std::deque<int> > mSubRecords; // record ID, list of subrecords
public: public:
@ -49,7 +50,7 @@ namespace CSMDoc
bool isProjectFile() const; bool isProjectFile() const;
///< Currently saving project file? (instead of content file) ///< Currently saving project file? (instead of content file)
std::map<std::string, std::vector<int> >& getSubRecords(); std::map<std::string, std::deque<int> >& getSubRecords();
}; };

View file

@ -9,7 +9,7 @@
CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType, CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType,
double lower, double upper) double lower, double upper)
: mColumnId (columnId), mLowerType (lowerType), mUpperType (upperType), mLower (lower), mUpper (upper){} : mColumnId (columnId), mLower (lower), mUpper (upper), mLowerType (lowerType), mUpperType (upperType){}
bool CSMFilter::ValueNode::test (const CSMWorld::IdTableBase& table, int row, bool CSMFilter::ValueNode::test (const CSMWorld::IdTableBase& table, int row,
const std::map<int, int>& columns) const const std::map<int, int>& columns) const
@ -27,7 +27,7 @@ bool CSMFilter::ValueNode::test (const CSMWorld::IdTableBase& table, int row,
QVariant data = table.data (index); QVariant data = table.data (index);
if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int && if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int &&
data.type()!=QVariant::UInt) data.type()!=QVariant::UInt && data.type()!=static_cast<QVariant::Type> (QMetaType::Float))
return false; return false;
double value = data.toDouble(); double value = data.toDouble();

View file

@ -4,7 +4,7 @@
CSMSettings::Connector::Connector(CSVSettings::View *master, CSMSettings::Connector::Connector(CSVSettings::View *master,
QObject *parent) QObject *parent)
: mMasterView (master), QObject(parent) : QObject(parent), mMasterView (master)
{} {}
void CSMSettings::Connector::addSlaveView (CSVSettings::View *view, void CSMSettings::Connector::addSlaveView (CSVSettings::View *view,

View file

@ -143,6 +143,24 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
minWidth->setDefaultValue (325); minWidth->setDefaultValue (325);
minWidth->setRange (50, 10000); minWidth->setRange (50, 10000);
minWidth->setToolTip ("Minimum width of subviews."); minWidth->setToolTip ("Minimum width of subviews.");
QString defaultScroll = "Scrollbar Only";
QStringList scrollValues = QStringList() << defaultScroll << "Grow Only" << "Grow then Scroll";
Setting *mainwinScroll = createSetting (Type_RadioButton, "mainwindow-scrollbar",
"Add a horizontal scrollbar to the main view window.");
mainwinScroll->setDefaultValue (defaultScroll);
mainwinScroll->setDeclaredValues (scrollValues);
mainwinScroll->setToolTip ("Scrollbar Only: Simple addition of scrollbars, the view window does not grow"
" automatically.\n"
"Grow Only: Original Editor behaviour. The view window grows as subviews are added. No scrollbars.\n"
"Grow then Scroll: The view window grows. The scrollbar appears once it cannot grow any further.");
Setting *grow = createSetting (Type_CheckBox, "grow-limit", "Grow Limit Screen");
grow->setDefaultValue ("false");
grow->setToolTip ("When \"Grow then Scroll\" option is selected, the window size grows to"
" the width of the virtual desktop. \nIf this option is selected the the window growth"
"is limited to the current screen.");
} }
declareSection ("records", "Records"); declareSection ("records", "Records");
@ -206,6 +224,14 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
shiftCtrlDoubleClick->setDeclaredValues (values); shiftCtrlDoubleClick->setDeclaredValues (values);
shiftCtrlDoubleClick->setDefaultValue (editRecordAndClose); shiftCtrlDoubleClick->setDefaultValue (editRecordAndClose);
shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in table:<p>" + toolTip); shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in table:<p>" + toolTip);
QString defaultValue = "Jump and Select";
QStringList jumpValues = QStringList() << defaultValue << "Jump Only" << "No Jump";
Setting *jumpToAdded = createSetting (Type_RadioButton, "jump-to-added",
"Jump to the added or cloned record.");
jumpToAdded->setDefaultValue (defaultValue);
jumpToAdded->setDeclaredValues (jumpValues);
} }
declareSection ("search", "Search & Replace"); declareSection ("search", "Search & Replace");
@ -221,6 +247,57 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
after->setDefaultValue (10); after->setDefaultValue (10);
after->setRange (0, 1000); after->setRange (0, 1000);
after->setToolTip ("Maximum number of character to display in search result after the searched text"); after->setToolTip ("Maximum number of character to display in search result after the searched text");
Setting *autoDelete = createSetting (Type_CheckBox, "auto-delete", "Delete row from result table after a successful replace");
autoDelete->setDefaultValue ("true");
}
declareSection ("script-editor", "Script Editor");
{
Setting *lineNum = createSetting (Type_CheckBox, "show-linenum", "Show Line Numbers");
lineNum->setDefaultValue ("true");
lineNum->setToolTip ("Show line numbers to the left of the script editor window."
"The current row and column numbers of the text cursor are shown at the bottom.");
Setting *monoFont = createSetting (Type_CheckBox, "mono-font", "Use monospace font");
monoFont->setDefaultValue ("true");
monoFont->setToolTip ("Whether to use monospaced fonts on script edit subview.");
QString tooltip =
"\n#RGB (each of R, G, and B is a single hex digit)"
"\n#RRGGBB"
"\n#RRRGGGBBB"
"\n#RRRRGGGGBBBB"
"\nA name from the list of colors defined in the list of SVG color keyword names."
"\nX11 color names may also work.";
Setting *formatInt = createSetting (Type_LineEdit, "colour-int", "Highlight Colour: Int");
formatInt->setDefaultValues (QStringList() << "Dark magenta");
formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip);
Setting *formatFloat = createSetting (Type_LineEdit, "colour-float", "Highlight Colour: Float");
formatFloat->setDefaultValues (QStringList() << "Magenta");
formatFloat->setToolTip ("(Default: Magenta) Use one of the following formats:" + tooltip);
Setting *formatName = createSetting (Type_LineEdit, "colour-name", "Highlight Colour: Name");
formatName->setDefaultValues (QStringList() << "Gray");
formatName->setToolTip ("(Default: Gray) Use one of the following formats:" + tooltip);
Setting *formatKeyword = createSetting (Type_LineEdit, "colour-keyword", "Highlight Colour: Keyword");
formatKeyword->setDefaultValues (QStringList() << "Red");
formatKeyword->setToolTip ("(Default: Red) Use one of the following formats:" + tooltip);
Setting *formatSpecial = createSetting (Type_LineEdit, "colour-special", "Highlight Colour: Special");
formatSpecial->setDefaultValues (QStringList() << "Dark yellow");
formatSpecial->setToolTip ("(Default: Dark yellow) Use one of the following formats:" + tooltip);
Setting *formatComment = createSetting (Type_LineEdit, "colour-comment", "Highlight Colour: Comment");
formatComment->setDefaultValues (QStringList() << "Green");
formatComment->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip);
Setting *formatId = createSetting (Type_LineEdit, "colour-id", "Highlight Colour: Id");
formatId->setDefaultValues (QStringList() << "Blue");
formatId->setToolTip ("(Default: Blue) Use one of the following formats:" + tooltip);
} }
{ {

View file

@ -0,0 +1,161 @@
#include "pathgridcheck.hpp"
#include <sstream>
#include <algorithm>
#include "../world/universalid.hpp"
#include "../world/idcollection.hpp"
#include "../world/subcellcollection.hpp"
#include "../world/pathgrid.hpp"
namespace
{
struct Point
{
unsigned char mConnectionNum;
std::vector<int> mOtherIndex;
Point() : mConnectionNum(0), mOtherIndex(0) {}
};
}
CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids)
: mPathgrids (pathgrids)
{}
int CSMTools::PathgridCheckStage::setup()
{
return mPathgrids.getSize();
}
void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::Pathgrid>& record = mPathgrids.getRecord (stage);
if (record.isDeleted())
return;
const CSMWorld::Pathgrid& pathgrid = record.get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Pathgrid, pathgrid.mId);
// check the number of pathgrid points
if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
messages.push_back (std::make_pair (id, pathgrid.mId + " has less points than expected"));
else if (pathgrid.mData.mS2 > static_cast<int>(pathgrid.mPoints.size()))
messages.push_back (std::make_pair (id, pathgrid.mId + " has more points than expected"));
std::vector<Point> pointList(pathgrid.mPoints.size());
std::vector<int> duplList;
for (unsigned int i = 0; i < pathgrid.mEdges.size(); ++i)
{
if (pathgrid.mEdges[i].mV0 < static_cast<int>(pathgrid.mPoints.size()) && pathgrid.mEdges[i].mV0 >= 0)
{
pointList[pathgrid.mEdges[i].mV0].mConnectionNum++;
// first check for duplicate edges
unsigned int j = 0;
for (; j < pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size(); ++j)
{
if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1)
{
std::ostringstream ss;
ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0
<< " and " << pathgrid.mEdges[i].mV1;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
break;
}
}
// only add if not a duplicate
if (j == pointList[pathgrid.mEdges[i].mV0].mOtherIndex.size())
pointList[pathgrid.mEdges[i].mV0].mOtherIndex.push_back(pathgrid.mEdges[i].mV1);
}
else
{
std::ostringstream ss;
ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
}
}
for (unsigned int i = 0; i < pathgrid.mPoints.size(); ++i)
{
// check the connection number for each point matches the edge connections
if (pathgrid.mPoints[i].mConnectionNum > pointList[i].mConnectionNum)
{
std::ostringstream ss;
ss << " has has less edges than expected for point " << i;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
}
else if (pathgrid.mPoints[i].mConnectionNum < pointList[i].mConnectionNum)
{
std::ostringstream ss;
ss << " has has more edges than expected for point " << i;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
}
// check that edges are bidirectional
bool foundReverse = false;
for (unsigned int j = 0; j < pointList[i].mOtherIndex.size(); ++j)
{
for (unsigned int k = 0; k < pointList[pointList[i].mOtherIndex[j]].mOtherIndex.size(); ++k)
{
if (pointList[pointList[i].mOtherIndex[j]].mOtherIndex[k] == static_cast<int>(i))
{
foundReverse = true;
break;
}
}
if (!foundReverse)
{
std::ostringstream ss;
ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j];
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
}
}
// check duplicate points
// FIXME: how to do this efficiently?
for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j)
{
if (j == i)
continue;
if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX &&
pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY &&
pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ)
{
std::vector<int>::const_iterator it = find(duplList.begin(), duplList.end(), i);
if (it == duplList.end())
{
std::ostringstream ss;
ss << " has a duplicated point (" << i
<< ") x=" << pathgrid.mPoints[i].mX
<< ", y=" << pathgrid.mPoints[i].mY
<< ", z=" << pathgrid.mPoints[i].mZ;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
duplList.push_back(i);
break;
}
}
}
}
// check pathgrid points that are not connected to anything
for (unsigned int i = 0; i < pointList.size(); ++i)
{
if (pointList[i].mConnectionNum == 0)
{
std::ostringstream ss;
ss << " has an orphaned point (" << i
<< ") x=" << pathgrid.mPoints[i].mX
<< ", y=" << pathgrid.mPoints[i].mY
<< ", z=" << pathgrid.mPoints[i].mZ;
messages.push_back (std::make_pair (id, pathgrid.mId + ss.str()));
}
}
// TODO: check whether there are disconnected graphs
}

View file

@ -0,0 +1,31 @@
#ifndef CSM_TOOLS_PATHGRIDCHECK_H
#define CSM_TOOLS_PATHGRIDCHECK_H
#include "../world/collection.hpp"
#include "../doc/stage.hpp"
namespace CSMWorld
{
struct Pathgrid;
template<typename T, typename AT>
class SubCellCollection;
}
namespace CSMTools
{
class PathgridCheckStage : public CSMDoc::Stage
{
const CSMWorld::SubCellCollection<CSMWorld::Pathgrid, CSMWorld::IdAccessor<CSMWorld::Pathgrid> >& mPathgrids;
public:
PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid, CSMWorld::IdAccessor<CSMWorld::Pathgrid> >& pathgrids);
virtual int setup();
virtual void perform (int stage, CSMDoc::Messages& messages);
};
}
#endif // CSM_TOOLS_PATHGRIDCHECK_H

View file

@ -8,12 +8,14 @@
CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races, const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races,
const CSMWorld::IdCollection<ESM::Class>& classes, const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& faction) const CSMWorld::IdCollection<ESM::Faction>& faction,
const CSMWorld::IdCollection<ESM::Script>& scripts)
: :
mReferencables(referenceable), mReferencables(referenceable),
mClasses(classes),
mRaces(races), mRaces(races),
mClasses(classes),
mFactions(faction), mFactions(faction),
mScripts(scripts),
mPlayerPresent(false) mPlayerPresent(false)
{ {
} }
@ -245,6 +247,9 @@ void CSMTools::ReferenceableCheckStage::bookCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId);
inventoryItemCheck<ESM::Book>(book, messages, id.toString(), true); inventoryItemCheck<ESM::Book>(book, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Book>(book, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::activatorCheck( void CSMTools::ReferenceableCheckStage::activatorCheck(
@ -265,6 +270,9 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
//Checking for model, IIRC all activators should have a model //Checking for model, IIRC all activators should have a model
if (activator.mModel.empty()) if (activator.mModel.empty())
messages.push_back (std::make_pair (id, activator.mId + " has no model")); messages.push_back (std::make_pair (id, activator.mId + " has no model"));
// Check that mentioned scripts exist
scriptCheck<ESM::Activator>(activator, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::potionCheck( void CSMTools::ReferenceableCheckStage::potionCheck(
@ -284,6 +292,9 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
inventoryItemCheck<ESM::Potion>(potion, messages, id.toString()); inventoryItemCheck<ESM::Potion>(potion, messages, id.toString());
//IIRC potion can have empty effects list just fine. //IIRC potion can have empty effects list just fine.
// Check that mentioned scripts exist
scriptCheck<ESM::Potion>(potion, messages, id.toString());
} }
@ -305,6 +316,9 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(
inventoryItemCheck<ESM::Apparatus>(apparatus, messages, id.toString()); inventoryItemCheck<ESM::Apparatus>(apparatus, messages, id.toString());
toolCheck<ESM::Apparatus>(apparatus, messages, id.toString()); toolCheck<ESM::Apparatus>(apparatus, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Apparatus>(apparatus, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::armorCheck( void CSMTools::ReferenceableCheckStage::armorCheck(
@ -331,6 +345,9 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
//checking for health. Only positive numbers are allowed, or 0 is illegal //checking for health. Only positive numbers are allowed, or 0 is illegal
if (armor.mData.mHealth <= 0) if (armor.mData.mHealth <= 0)
messages.push_back (std::make_pair (id, armor.mId + " has non positive health")); messages.push_back (std::make_pair (id, armor.mId + " has non positive health"));
// Check that mentioned scripts exist
scriptCheck<ESM::Armor>(armor, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::clothingCheck( void CSMTools::ReferenceableCheckStage::clothingCheck(
@ -348,6 +365,9 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(
const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get(); const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId);
inventoryItemCheck<ESM::Clothing>(clothing, messages, id.toString(), true); inventoryItemCheck<ESM::Clothing>(clothing, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Clothing>(clothing, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::containerCheck( void CSMTools::ReferenceableCheckStage::containerCheck(
@ -377,6 +397,9 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
//checking for name //checking for name
if (container.mName.empty()) if (container.mName.empty())
messages.push_back (std::make_pair (id, container.mId + " has an empty name")); messages.push_back (std::make_pair (id, container.mId + " has an empty name"));
// Check that mentioned scripts exist
scriptCheck<ESM::Container>(container, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::creatureCheck ( void CSMTools::ReferenceableCheckStage::creatureCheck (
@ -444,6 +467,9 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
//TODO, find meaning of other values //TODO, find meaning of other values
if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures
messages.push_back (std::make_pair (id, creature.mId + " has negative gold ")); messages.push_back (std::make_pair (id, creature.mId + " has negative gold "));
// Check that mentioned scripts exist
scriptCheck<ESM::Creature>(creature, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::doorCheck( void CSMTools::ReferenceableCheckStage::doorCheck(
@ -455,15 +481,18 @@ void CSMTools::ReferenceableCheckStage::doorCheck(
if (baseRecord.isDeleted()) if (baseRecord.isDeleted())
return; return;
const ESM::Door& Door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get(); const ESM::Door& door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, door.mId);
//usual, name or model //usual, name or model
if (Door.mName.empty()) if (door.mName.empty())
messages.push_back (std::make_pair (id, Door.mId + " has an empty name")); messages.push_back (std::make_pair (id, door.mId + " has an empty name"));
if (Door.mModel.empty()) if (door.mModel.empty())
messages.push_back (std::make_pair (id, Door.mId + " has no model")); messages.push_back (std::make_pair (id, door.mId + " has no model"));
// Check that mentioned scripts exist
scriptCheck<ESM::Door>(door, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::ingredientCheck( void CSMTools::ReferenceableCheckStage::ingredientCheck(
@ -478,10 +507,13 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(
return; return;
} }
const ESM::Ingredient& Ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get(); const ESM::Ingredient& ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, ingredient.mId);
inventoryItemCheck<ESM::Ingredient>(Ingredient, messages, id.toString()); inventoryItemCheck<ESM::Ingredient>(ingredient, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Ingredient>(ingredient, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::creaturesLevListCheck( void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(
@ -542,6 +574,9 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
if (light.mData.mTime == 0) if (light.mData.mTime == 0)
messages.push_back (std::make_pair (id, light.mId + " has zero duration")); messages.push_back (std::make_pair (id, light.mId + " has zero duration"));
} }
// Check that mentioned scripts exist
scriptCheck<ESM::Light>(light, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::lockpickCheck( void CSMTools::ReferenceableCheckStage::lockpickCheck(
@ -562,6 +597,9 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck(
inventoryItemCheck<ESM::Lockpick>(lockpick, messages, id.toString()); inventoryItemCheck<ESM::Lockpick>(lockpick, messages, id.toString());
toolCheck<ESM::Lockpick>(lockpick, messages, id.toString(), true); toolCheck<ESM::Lockpick>(lockpick, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Lockpick>(lockpick, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::miscCheck( void CSMTools::ReferenceableCheckStage::miscCheck(
@ -580,6 +618,9 @@ void CSMTools::ReferenceableCheckStage::miscCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId);
inventoryItemCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString()); inventoryItemCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::npcCheck ( void CSMTools::ReferenceableCheckStage::npcCheck (
@ -697,6 +738,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
messages.push_back (std::make_pair (id, npc.mId + " has no hair")); messages.push_back (std::make_pair (id, npc.mId + " has no hair"));
//TODO: reputation, Disposition, rank, everything else //TODO: reputation, Disposition, rank, everything else
// Check that mentioned scripts exist
scriptCheck<ESM::NPC>(npc, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::weaponCheck( void CSMTools::ReferenceableCheckStage::weaponCheck(
@ -773,6 +817,9 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(
messages.push_back (std::make_pair (id, weapon.mId + " has negative reach")); messages.push_back (std::make_pair (id, weapon.mId + " has negative reach"));
} }
} }
// Check that mentioned scripts exist
scriptCheck<ESM::Weapon>(weapon, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::probeCheck( void CSMTools::ReferenceableCheckStage::probeCheck(
@ -792,6 +839,9 @@ void CSMTools::ReferenceableCheckStage::probeCheck(
inventoryItemCheck<ESM::Probe>(probe, messages, id.toString()); inventoryItemCheck<ESM::Probe>(probe, messages, id.toString());
toolCheck<ESM::Probe>(probe, messages, id.toString(), true); toolCheck<ESM::Probe>(probe, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Probe>(probe, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::repairCheck ( void CSMTools::ReferenceableCheckStage::repairCheck (
@ -808,6 +858,9 @@ void CSMTools::ReferenceableCheckStage::repairCheck (
inventoryItemCheck<ESM::Repair> (repair, messages, id.toString()); inventoryItemCheck<ESM::Repair> (repair, messages, id.toString());
toolCheck<ESM::Repair> (repair, messages, id.toString(), true); toolCheck<ESM::Repair> (repair, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Repair>(repair, messages, id.toString());
} }
void CSMTools::ReferenceableCheckStage::staticCheck ( void CSMTools::ReferenceableCheckStage::staticCheck (
@ -919,3 +972,13 @@ template<typename List> void CSMTools::ReferenceableCheckStage::listCheck (
someList.mId + " contains item with non-positive level")); someList.mId + " contains item with non-positive level"));
} }
} }
template<typename Tool> void CSMTools::ReferenceableCheckStage::scriptCheck (
const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID)
{
if (!someTool.mScript.empty())
{
if (mScripts.searchId(someTool.mScript) == -1)
messages.push_back (std::make_pair (someID, someTool.mId + " refers to an unknown script \""+someTool.mScript+"\""));
}
}

View file

@ -15,7 +15,8 @@ namespace CSMTools
ReferenceableCheckStage (const CSMWorld::RefIdData& referenceable, ReferenceableCheckStage (const CSMWorld::RefIdData& referenceable,
const CSMWorld::IdCollection<ESM::Race>& races, const CSMWorld::IdCollection<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Class>& classes, const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions); const CSMWorld::IdCollection<ESM::Faction>& factions,
const CSMWorld::IdCollection<ESM::Script>& scripts);
virtual void perform(int stage, CSMDoc::Messages& messages); virtual void perform(int stage, CSMDoc::Messages& messages);
virtual int setup(); virtual int setup();
@ -69,10 +70,15 @@ namespace CSMTools
CSMDoc::Messages& messages, CSMDoc::Messages& messages,
const std::string& someID); const std::string& someID);
template<typename TOOL> void scriptCheck(const TOOL& someTool,
CSMDoc::Messages& messages,
const std::string& someID);
const CSMWorld::RefIdData& mReferencables; const CSMWorld::RefIdData& mReferencables;
const CSMWorld::IdCollection<ESM::Race>& mRaces; const CSMWorld::IdCollection<ESM::Race>& mRaces;
const CSMWorld::IdCollection<ESM::Class>& mClasses; const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::Script>& mScripts;
bool mPlayerPresent; bool mPlayerPresent;
}; };
} }

View file

@ -28,7 +28,7 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message
// Check for empty reference id // Check for empty reference id
if (cellRef.mRefID.empty()) { if (cellRef.mRefID.empty()) {
messages.push_back(std::make_pair(id, " is an empty reference")); messages.push_back(std::make_pair(id, " is an empty instance (not based on an object)"));
} else { } else {
// Check for non existing referenced object // Check for non existing referenced object
if (mReferencables.searchId(cellRef.mRefID) == -1) { if (mReferencables.searchId(cellRef.mRefID) == -1) {

View file

@ -120,8 +120,15 @@ bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& p
if (parent.isValid()) if (parent.isValid())
return false; return false;
if (count>0)
{
beginRemoveRows (parent, row, row+count-1);
mRows.erase (mRows.begin()+row, mRows.begin()+row+count); mRows.erase (mRows.begin()+row, mRows.begin()+row+count);
endRemoveRows();
}
return true; return true;
} }

View file

@ -277,3 +277,20 @@ void CSMTools::Search::replace (CSMDoc::Document& document, CSMWorld::IdTableBas
} }
} }
bool CSMTools::Search::verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model,
const CSMWorld::UniversalId& id, const std::string& messageHint) const
{
CSMDoc::Messages messages;
int row = model->getModelIndex (id.getId(),
model->findColumnIndex (CSMWorld::Columns::ColumnId_Id)).row();
searchRow (model, row, messages);
for (CSMDoc::Messages::Iterator iter (messages.begin()); iter!=messages.end(); ++iter)
if (iter->mHint==messageHint)
return true;
return false;
}

View file

@ -88,6 +88,10 @@ namespace CSMTools
void replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model, void replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model,
const CSMWorld::UniversalId& id, const std::string& messageHint, const CSMWorld::UniversalId& id, const std::string& messageHint,
const std::string& replaceText) const; const std::string& replaceText) const;
// Check if model still matches search results.
bool verify (CSMDoc::Document& document, CSMWorld::IdTableBase *model,
const CSMWorld::UniversalId& id, const std::string& messageHint) const;
}; };
} }

View file

@ -26,6 +26,7 @@
#include "referencecheck.hpp" #include "referencecheck.hpp"
#include "startscriptcheck.hpp" #include "startscriptcheck.hpp"
#include "searchoperation.hpp" #include "searchoperation.hpp"
#include "pathgridcheck.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type) CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{ {
@ -81,7 +82,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells()));
mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts()));
mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));
@ -96,6 +97,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )), CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )),
mData.getRaces() )); mData.getRaces() ));
mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids()));
mVerifier.setOperation (mVerifierOperation); mVerifier.setOperation (mVerifierOperation);
} }
@ -103,8 +106,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
} }
CSMTools::Tools::Tools (CSMDoc::Document& document) CSMTools::Tools::Tools (CSMDoc::Document& document)
: mDocument (document), mData (document.getData()), mVerifierOperation (0), mNextReportNumber (0), : mDocument (document), mData (document.getData()), mVerifierOperation (0),
mSearchOperation (0) mSearchOperation (0), mNextReportNumber (0)
{ {
// index 0: load error log // index 0: load error log
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel)); mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));

View file

@ -3,7 +3,7 @@
#include "columns.hpp" #include "columns.hpp"
CSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags) CSMWorld::ColumnBase::ColumnBase (int columnId, Display displayType, int flags)
: mColumnId (columnId), mDisplayType (displayType), mFlags (flags) : mColumnId (columnId), mFlags (flags), mDisplayType (displayType)
{} {}
CSMWorld::ColumnBase::~ColumnBase() {} CSMWorld::ColumnBase::~ColumnBase() {}
@ -81,6 +81,10 @@ bool CSMWorld::ColumnBase::isId (Display display)
Display_PartRefType, Display_PartRefType,
Display_AiPackageType, Display_AiPackageType,
Display_YesNo, Display_YesNo,
Display_InfoCondFunc,
Display_InfoCondVar,
Display_InfoCondComp,
Display_RaceSkill,
Display_None Display_None
}; };
@ -134,8 +138,8 @@ bool CSMWorld::NestableColumn::hasChildren() const
} }
CSMWorld::NestedChildColumn::NestedChildColumn (int id, CSMWorld::NestedChildColumn::NestedChildColumn (int id,
CSMWorld::ColumnBase::Display display, bool isEditable) CSMWorld::ColumnBase::Display display, int flags, bool isEditable)
: NestableColumn (id, display, CSMWorld::ColumnBase::Flag_Dialogue) , mIsEditable(isEditable) : NestableColumn (id, display, flags) , mIsEditable(isEditable)
{} {}
bool CSMWorld::NestedChildColumn::isEditable () const bool CSMWorld::NestedChildColumn::isEditable () const

View file

@ -17,14 +17,16 @@ namespace CSMWorld
enum Roles enum Roles
{ {
Role_Flags = Qt::UserRole, Role_Flags = Qt::UserRole,
Role_Display = Qt::UserRole+1 Role_Display = Qt::UserRole+1,
Role_ColumnId = Qt::UserRole+2
}; };
enum Flags enum Flags
{ {
Flag_Table = 1, // column should be displayed in table view Flag_Table = 1, // column should be displayed in table view
Flag_Dialogue = 2, // column should be displayed in dialogue view Flag_Dialogue = 2, // column should be displayed in dialogue view
Flag_Dialogue_List = 4 // column should be diaplyed in dialogue view Flag_Dialogue_List = 4, // column should be diaplyed in dialogue view
Flag_Dialogue_Refresh = 8 // refresh dialogue view if this column is modified
}; };
enum Display enum Display
@ -115,6 +117,10 @@ namespace CSMWorld
Display_PartRefType, Display_PartRefType,
Display_AiPackageType, Display_AiPackageType,
Display_YesNo, Display_YesNo,
Display_InfoCondFunc,
Display_InfoCondVar,
Display_InfoCondComp,
Display_RaceSkill,
//top level columns that nest other columns //top level columns that nest other columns
Display_NestedHeader Display_NestedHeader
@ -195,7 +201,8 @@ namespace CSMWorld
struct NestedChildColumn : public NestableColumn struct NestedChildColumn : public NestableColumn
{ {
NestedChildColumn (int id, Display display, bool isEditable = true); NestedChildColumn (int id,
Display display, int flags = ColumnBase::Flag_Dialogue, bool isEditable = true);
virtual bool isEditable() const; virtual bool isEditable() const;

View file

@ -467,8 +467,9 @@ namespace CSMWorld
int mMask; int mMask;
bool mInverted; bool mInverted;
FlagColumn (int columnId, int mask, bool inverted = false) FlagColumn (int columnId, int mask,
: Column<ESXRecordT> (columnId, ColumnBase::Display_Boolean), mMask (mask), int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, bool inverted = false)
: Column<ESXRecordT> (columnId, ColumnBase::Display_Boolean, flags), mMask (mask),
mInverted (inverted) mInverted (inverted)
{} {}
@ -870,7 +871,13 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
struct CellColumn : public Column<ESXRecordT> struct CellColumn : public Column<ESXRecordT>
{ {
CellColumn() : Column<ESXRecordT> (Columns::ColumnId_Cell, ColumnBase::Display_Cell) {} bool mBlocked;
/// \param blocked Do not allow user-modification
CellColumn (bool blocked = false)
: Column<ESXRecordT> (Columns::ColumnId_Cell, ColumnBase::Display_Cell),
mBlocked (blocked)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
{ {
@ -892,9 +899,41 @@ namespace CSMWorld
} }
virtual bool isUserEditable() const virtual bool isUserEditable() const
{
return !mBlocked;
}
};
template<typename ESXRecordT>
struct OriginalCellColumn : public Column<ESXRecordT>
{
OriginalCellColumn()
: Column<ESXRecordT> (Columns::ColumnId_OriginalCell, ColumnBase::Display_Cell)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mOriginalCell.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mOriginalCell = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{ {
return true; return true;
} }
virtual bool isUserEditable() const
{
return false;
}
}; };
template<typename ESXRecordT> template<typename ESXRecordT>

View file

@ -172,11 +172,11 @@ namespace CSMWorld
{ ColumnId_Rank, "Rank" }, { ColumnId_Rank, "Rank" },
{ ColumnId_Gender, "Gender" }, { ColumnId_Gender, "Gender" },
{ ColumnId_PcRank, "PC Rank" }, { ColumnId_PcRank, "PC Rank" },
{ ColumnId_ReferenceableId, "Referenceable ID" }, { ColumnId_ReferenceableId, "Object ID" },
{ ColumnId_ContainerContent, "Content" }, { ColumnId_ContainerContent, "Content" },
{ ColumnId_ItemCount, "Count" }, { ColumnId_ItemCount, "Count" },
{ ColumnId_InventoryItemId, "ID"}, { ColumnId_InventoryItemId, "Item ID"},
{ ColumnId_CombatState, "Combat" }, { ColumnId_CombatState, "Combat" },
{ ColumnId_MagicState, "Magic" }, { ColumnId_MagicState, "Magic" },
@ -188,10 +188,10 @@ namespace CSMWorld
{ ColumnId_ActorInventory, "Inventory" }, { ColumnId_ActorInventory, "Inventory" },
{ ColumnId_SpellList, "Spells" }, { ColumnId_SpellList, "Spells" },
{ ColumnId_SpellId, "ID"}, { ColumnId_SpellId, "Spell ID"},
{ ColumnId_NpcDestinations, "Destinations" }, { ColumnId_NpcDestinations, "Destinations" },
{ ColumnId_DestinationCell, "Cell"}, { ColumnId_DestinationCell, "Dest Cell"},
{ ColumnId_PosX, "Dest X"}, { ColumnId_PosX, "Dest X"},
{ ColumnId_PosY, "Dest Y"}, { ColumnId_PosY, "Dest Y"},
{ ColumnId_PosZ, "Dest Z"}, { ColumnId_PosZ, "Dest Z"},
@ -224,17 +224,17 @@ namespace CSMWorld
{ ColumnId_BoltSound, "Bolt Sound" }, { ColumnId_BoltSound, "Bolt Sound" },
{ ColumnId_PathgridPoints, "Points" }, { ColumnId_PathgridPoints, "Points" },
{ ColumnId_PathgridIndex, "Index" }, { ColumnId_PathgridIndex, "pIndex" },
{ ColumnId_PathgridPosX, "X" }, { ColumnId_PathgridPosX, "X" },
{ ColumnId_PathgridPosY, "Y" }, { ColumnId_PathgridPosY, "Y" },
{ ColumnId_PathgridPosZ, "Z" }, { ColumnId_PathgridPosZ, "Z" },
{ ColumnId_PathgridEdges, "Edges" }, { ColumnId_PathgridEdges, "Edges" },
{ ColumnId_PathgridEdgeIndex, "Index" }, { ColumnId_PathgridEdgeIndex, "eIndex" },
{ ColumnId_PathgridEdge0, "Point 0" }, { ColumnId_PathgridEdge0, "Point 0" },
{ ColumnId_PathgridEdge1, "Point 1" }, { ColumnId_PathgridEdge1, "Point 1" },
{ ColumnId_RegionSounds, "Sounds" }, { ColumnId_RegionSounds, "Sounds" },
{ ColumnId_SoundName, "Name" }, { ColumnId_SoundName, "Sound Name" },
{ ColumnId_SoundChance, "Chance" }, { ColumnId_SoundChance, "Chance" },
{ ColumnId_FactionReactions, "Reactions" }, { ColumnId_FactionReactions, "Reactions" },
@ -250,7 +250,7 @@ namespace CSMWorld
{ ColumnId_AiPackageList, "Ai Packages" }, { ColumnId_AiPackageList, "Ai Packages" },
{ ColumnId_AiPackageType, "Package" }, { ColumnId_AiPackageType, "Package" },
{ ColumnId_AiWanderDist, "Wander Dist" }, { ColumnId_AiWanderDist, "Wander Dist" },
{ ColumnId_AiDuration, "Duration" }, { ColumnId_AiDuration, "Ai Duration" },
{ ColumnId_AiWanderToD, "Wander ToD" }, { ColumnId_AiWanderToD, "Wander ToD" },
{ ColumnId_AiWanderIdle, "Wander Idle" }, { ColumnId_AiWanderIdle, "Wander Idle" },
{ ColumnId_AiWanderRepeat, "Wander Repeat" }, { ColumnId_AiWanderRepeat, "Wander Repeat" },
@ -260,19 +260,56 @@ namespace CSMWorld
{ ColumnId_PartRefList, "Part Reference" }, { ColumnId_PartRefList, "Part Reference" },
{ ColumnId_PartRefType, "Type" }, { ColumnId_PartRefType, "Type" },
{ ColumnId_PartRefMale, "Male" }, { ColumnId_PartRefMale, "Male Part" },
{ ColumnId_PartRefFemale, "Female" }, { ColumnId_PartRefFemale, "Female Part" },
{ ColumnId_LevelledList,"Levelled List" }, { ColumnId_LevelledList,"Levelled List" },
{ ColumnId_LevelledItemId,"Item ID" }, { ColumnId_LevelledItemId,"Levelled Item" },
{ ColumnId_LevelledItemLevel,"Level" }, { ColumnId_LevelledItemLevel,"Level" },
{ ColumnId_LevelledItemType, "Type" }, { ColumnId_LevelledItemType, "Calculate all levels <= player" },
{ ColumnId_LevelledItemTypeEach, "Select a new item each instance" },
{ ColumnId_LevelledItemChanceNone, "Chance None" }, { ColumnId_LevelledItemChanceNone, "Chance None" },
{ ColumnId_PowerList, "Powers" }, { ColumnId_PowerList, "Powers" },
{ ColumnId_SkillImpact, "Skills" }, { ColumnId_SkillImpact, "Skills" },
{ ColumnId_InfoList, "Info List" }, { ColumnId_InfoList, "Info List" },
{ ColumnId_InfoCondition, "Info Conditions" },
{ ColumnId_InfoCondFunc, "Function" },
{ ColumnId_InfoCondVar, "Func/Variable" },
{ ColumnId_InfoCondComp, "Comp" },
{ ColumnId_InfoCondValue, "Values" },
{ ColumnId_OriginalCell, "Original Cell" },
{ ColumnId_NpcAttributes, "Attributes" },
{ ColumnId_NpcSkills, "Skills" },
{ ColumnId_UChar, "Value [0..255]" },
{ ColumnId_NpcMisc, "Misc" },
{ ColumnId_NpcLevel, "Level" },
{ ColumnId_NpcFactionID, "Faction ID" },
{ ColumnId_NpcHealth, "Health" },
{ ColumnId_NpcMana, "Mana" },
{ ColumnId_NpcFatigue, "Fatigue" },
{ ColumnId_NpcDisposition, "Disposition" },
{ ColumnId_NpcReputation, "Reputation" },
{ ColumnId_NpcRank, "Rank" },
{ ColumnId_NpcGold, "Gold" },
{ ColumnId_NpcPersistence, "Persistent" },
{ ColumnId_RaceAttributes, "Attributes" },
{ ColumnId_RaceMaleValue, "Male" },
{ ColumnId_RaceFemaleValue, "Female" },
{ ColumnId_RaceSkillBonus, "Skill Bonus" },
{ ColumnId_RaceSkill, "Skills" },
{ ColumnId_RaceBonus, "Bonus" },
{ ColumnId_Interior, "Interior" },
{ ColumnId_Ambient, "Ambient" },
{ ColumnId_Sunlight, "Sunlight" },
{ ColumnId_Fog, "Fog" },
{ ColumnId_FogDensity, "Fog Density" },
{ ColumnId_WaterLevel, "Water Level" },
{ ColumnId_MapColor, "Map Color" },
{ ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },
@ -500,6 +537,18 @@ namespace
"No", "Yes", 0 "No", "Yes", 0
}; };
static const char *sInfoCondFunc[] =
{
" ", "Function", "Global", "Local", "Journal",
"Item", "Dead", "Not ID", "Not Faction", "Not Class",
"Not Race", "Not Cell", "Not Local", 0
};
static const char *sInfoCondComp[] =
{
"!=", "<", "<=", "=", ">", ">=", 0
};
const char **getEnumNames (CSMWorld::Columns::ColumnId column) const char **getEnumNames (CSMWorld::Columns::ColumnId column)
{ {
switch (column) switch (column)
@ -528,6 +577,11 @@ namespace
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType; case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;
case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType; case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;
case CSMWorld::Columns::ColumnId_AiWanderRepeat: return sAiWanderRepeat; case CSMWorld::Columns::ColumnId_AiWanderRepeat: return sAiWanderRepeat;
case CSMWorld::Columns::ColumnId_InfoCondFunc: return sInfoCondFunc;
// FIXME: don't have dynamic value enum delegate, use Display_String for now
//case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond;
case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp;
case CSMWorld::Columns::ColumnId_RaceSkill: return sSkills;
default: return 0; default: return 0;
} }

View file

@ -257,12 +257,50 @@ namespace CSMWorld
ColumnId_LevelledItemId = 234, ColumnId_LevelledItemId = 234,
ColumnId_LevelledItemLevel = 235, ColumnId_LevelledItemLevel = 235,
ColumnId_LevelledItemType = 236, ColumnId_LevelledItemType = 236,
ColumnId_LevelledItemChanceNone = 237, ColumnId_LevelledItemTypeEach = 237,
ColumnId_LevelledItemChanceNone = 238,
ColumnId_PowerList = 238, ColumnId_PowerList = 239,
ColumnId_SkillImpact = 239, // impact from magic effects ColumnId_SkillImpact = 240, // impact from magic effects
ColumnId_InfoList = 240, ColumnId_InfoList = 241,
ColumnId_InfoCondition = 242,
ColumnId_InfoCondFunc = 243,
ColumnId_InfoCondVar = 244,
ColumnId_InfoCondComp = 245,
ColumnId_InfoCondValue = 246,
ColumnId_OriginalCell = 247,
ColumnId_NpcAttributes = 248,
ColumnId_NpcSkills = 249,
ColumnId_UChar = 250,
ColumnId_NpcMisc = 251,
ColumnId_NpcLevel = 252,
ColumnId_NpcFactionID = 253,
ColumnId_NpcHealth = 254,
ColumnId_NpcMana = 255,
ColumnId_NpcFatigue = 256,
ColumnId_NpcDisposition = 257,
ColumnId_NpcReputation = 258,
ColumnId_NpcRank = 259,
ColumnId_NpcGold = 260,
ColumnId_NpcPersistence = 261,
ColumnId_RaceAttributes = 262,
ColumnId_RaceMaleValue = 263,
ColumnId_RaceFemaleValue = 264,
ColumnId_RaceSkillBonus = 265,
ColumnId_RaceSkill = 266,
ColumnId_RaceBonus = 267,
ColumnId_Interior = 268,
ColumnId_Ambient = 269,
ColumnId_Sunlight = 270,
ColumnId_Fog = 271,
ColumnId_FogDensity = 272,
ColumnId_WaterLevel = 273,
ColumnId_MapColor = 274,
// Allocated to a separate value range, so we don't get a collision should we ever need // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // to extend the number of use values.

View file

@ -2,6 +2,7 @@
#include "commanddispatcher.hpp" #include "commanddispatcher.hpp"
#include <algorithm> #include <algorithm>
#include <memory>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
@ -10,6 +11,7 @@
#include "idtable.hpp" #include "idtable.hpp"
#include "record.hpp" #include "record.hpp"
#include "commands.hpp" #include "commands.hpp"
#include "idtableproxymodel.hpp"
std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const
{ {
@ -81,7 +83,7 @@ std::vector<std::string> CSMWorld::CommandDispatcher::getRevertableRecords() con
CSMWorld::CommandDispatcher::CommandDispatcher (CSMDoc::Document& document, CSMWorld::CommandDispatcher::CommandDispatcher (CSMDoc::Document& document,
const CSMWorld::UniversalId& id, QObject *parent) const CSMWorld::UniversalId& id, QObject *parent)
: QObject (parent), mDocument (document), mId (id), mLocked (false) : QObject (parent), mLocked (false), mDocument (document), mId (id)
{} {}
void CSMWorld::CommandDispatcher::setEditLock (bool locked) void CSMWorld::CommandDispatcher::setEditLock (bool locked)
@ -131,6 +133,54 @@ std::vector<CSMWorld::UniversalId> CSMWorld::CommandDispatcher::getExtendedTypes
return tables; return tables;
} }
void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_)
{
if (mLocked)
return;
std::auto_ptr<CSMWorld::UpdateCellCommand> modifyCell;
int columnId = model->data (index, ColumnBase::Role_ColumnId).toInt();
if (columnId==Columns::ColumnId_PositionXPos || columnId==Columns::ColumnId_PositionYPos)
{
IdTableProxyModel *proxy = dynamic_cast<IdTableProxyModel *> (model);
int row = proxy ? proxy->mapToSource (index).row() : index.row();
// This is not guaranteed to be the same as \a model, since a proxy could be used.
IdTable& model2 = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
int cellColumn = model2.searchColumnIndex (Columns::ColumnId_Cell);
if (cellColumn!=-1)
{
QModelIndex cellIndex = model2.index (row, cellColumn);
std::string cellId = model2.data (cellIndex).toString().toUtf8().data();
if (cellId.find ('#')!=std::string::npos)
{
// Need to recalculate the cell
modifyCell.reset (new UpdateCellCommand (model2, row));
}
}
}
std::auto_ptr<CSMWorld::ModifyCommand> modifyData (
new CSMWorld::ModifyCommand (*model, index, new_));
if (modifyCell.get())
{
mDocument.getUndoStack().beginMacro (modifyData->text());
mDocument.getUndoStack().push (modifyData.release());
mDocument.getUndoStack().push (modifyCell.release());
mDocument.getUndoStack().endMacro();
}
else
mDocument.getUndoStack().push (modifyData.release());
}
void CSMWorld::CommandDispatcher::executeDelete() void CSMWorld::CommandDispatcher::executeDelete()
{ {
if (mLocked) if (mLocked)
@ -153,6 +203,14 @@ void CSMWorld::CommandDispatcher::executeDelete()
std::string id = model.data (model.getModelIndex (*iter, columnIndex)). std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
toString().toUtf8().constData(); toString().toUtf8().constData();
if (mId.getType() == UniversalId::Type_Referenceables)
{
mDocument.getUndoStack().push ( new CSMWorld::DeleteCommand (model, id,
static_cast<CSMWorld::UniversalId::Type>(model.data (model.index (
model.getModelIndex (id, columnIndex).row(),
model.findColumnIndex (CSMWorld::Columns::ColumnId_RecordType))).toInt())));
}
else
mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id)); mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id));
} }

View file

@ -7,6 +7,9 @@
#include "universalid.hpp" #include "universalid.hpp"
class QModelIndex;
class QAbstractItemModel;
namespace CSMDoc namespace CSMDoc
{ {
class Document; class Document;
@ -53,6 +56,12 @@ namespace CSMWorld
/// the extended mode, the returned vector will be empty instead. /// the extended mode, the returned vector will be empty instead.
std::vector<UniversalId> getExtendedTypes() const; std::vector<UniversalId> getExtendedTypes() const;
/// Add a modify command to the undo stack.
///
/// \attention model must either be a model for the table operated on by this
/// dispatcher or a proxy of it.
void executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_);
public slots: public slots:
void executeDelete(); void executeDelete();

View file

@ -1,5 +1,8 @@
#include "commands.hpp" #include "commands.hpp"
#include <cmath>
#include <sstream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <QAbstractItemModel> #include <QAbstractItemModel>
@ -11,16 +14,28 @@
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand* parent) const QVariant& new_, QUndoCommand* parent)
: QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_) : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false)
{ {
if (QAbstractProxyModel *proxy = dynamic_cast<QAbstractProxyModel *> (&model)) if (QAbstractProxyModel *proxy = dynamic_cast<QAbstractProxyModel *> (&model))
{ {
// Replace proxy with actual model // Replace proxy with actual model
mIndex = proxy->mapToSource (index); mIndex = proxy->mapToSource (index);
mModel = proxy->sourceModel(); mModel = proxy->sourceModel();
}
setText ("Modify " + dynamic_cast<CSMWorld::IdTree*>(mModel)->nestedHeaderData (
mIndex.parent().column(), mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString());
}
else
setText ("Modify " + mModel->headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); setText ("Modify " + mModel->headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString());
// Remember record state before the modification
if (CSMWorld::IdTable *table = dynamic_cast<IdTable *>(mModel))
{
mHasRecordState = true;
int stateColumnIndex = table->findColumnIndex(Columns::ColumnId_Modification);
mRecordStateIndex = table->index(mIndex.row(), stateColumnIndex);
mOldRecordState = static_cast<CSMWorld::RecordBase::State>(table->data(mRecordStateIndex).toInt());
}
} }
void CSMWorld::ModifyCommand::redo() void CSMWorld::ModifyCommand::redo()
@ -32,6 +47,10 @@ void CSMWorld::ModifyCommand::redo()
void CSMWorld::ModifyCommand::undo() void CSMWorld::ModifyCommand::undo()
{ {
mModel->setData (mIndex, mOld); mModel->setData (mIndex, mOld);
if (mHasRecordState)
{
mModel->setData(mRecordStateIndex, mOldRecordState);
}
} }
@ -103,8 +122,9 @@ void CSMWorld::RevertCommand::undo()
mModel.setRecord (mId, *mOld); mModel.setRecord (mId, *mOld);
} }
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent) CSMWorld::DeleteCommand::DeleteCommand (IdTable& model,
: QUndoCommand (parent), mModel (model), mId (id), mOld (0) const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0), mType(type)
{ {
setText (("Delete record " + id).c_str()); setText (("Delete record " + id).c_str());
@ -135,7 +155,7 @@ void CSMWorld::DeleteCommand::redo()
void CSMWorld::DeleteCommand::undo() void CSMWorld::DeleteCommand::undo()
{ {
mModel.setRecord (mId, *mOld); mModel.setRecord (mId, *mOld, mType);
} }
@ -182,17 +202,58 @@ void CSMWorld::CloneCommand::undo()
mModel.removeRow (mModel.getModelIndex (mId, 0).row()); mModel.removeRow (mModel.getModelIndex (mId, 0).row());
} }
CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mRow (row)
{
setText ("Update cell ID");
}
void CSMWorld::UpdateCellCommand::redo()
{
if (!mNew.isValid())
{
int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell);
mIndex = mModel.index (mRow, cellColumn);
const int cellSize = 8192;
QModelIndex xIndex = mModel.index (
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos));
QModelIndex yIndex = mModel.index (
mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos));
int x = std::floor (mModel.data (xIndex).toFloat() / cellSize);
int y = std::floor (mModel.data (yIndex).toFloat() / cellSize);
std::ostringstream stream;
stream << "#" << x << " " << y;
mNew = QString::fromUtf8 (stream.str().c_str());
}
mModel.setData (mIndex, mNew);
}
void CSMWorld::UpdateCellCommand::undo()
{
mModel.setData (mIndex, mOld);
}
CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model, CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model,
const std::string& id, const std::string& id,
int nestedRow, int nestedRow,
int parentColumn, int parentColumn,
QUndoCommand* parent) : QUndoCommand* parent) :
mId(id),
mModel(model),
mParentColumn(parentColumn),
QUndoCommand(parent), QUndoCommand(parent),
mNestedRow(nestedRow), NestedTableStoring(model, id, parentColumn),
NestedTableStoring(model, id, parentColumn) mModel(model),
mId(id),
mParentColumn(parentColumn),
mNestedRow(nestedRow)
{ {
std::string title = std::string title =
model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData();
@ -215,12 +276,12 @@ void CSMWorld::DeleteNestedCommand::undo()
} }
CSMWorld::AddNestedCommand::AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent) CSMWorld::AddNestedCommand::AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent)
: mModel(model), : QUndoCommand(parent),
NestedTableStoring(model, id, parentColumn),
mModel(model),
mId(id), mId(id),
mNewRow(nestedRow), mNewRow(nestedRow),
mParentColumn(parentColumn), mParentColumn(parentColumn)
QUndoCommand(parent),
NestedTableStoring(model, id, parentColumn)
{ {
std::string title = std::string title =
model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData(); model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData();

View file

@ -30,6 +30,10 @@ namespace CSMWorld
QVariant mNew; QVariant mNew;
QVariant mOld; QVariant mOld;
bool mHasRecordState;
QModelIndex mRecordStateIndex;
CSMWorld::RecordBase::State mOldRecordState;
public: public:
ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_,
@ -110,6 +114,7 @@ namespace CSMWorld
IdTable& mModel; IdTable& mModel;
std::string mId; std::string mId;
RecordBase *mOld; RecordBase *mOld;
UniversalId::Type mType;
// not implemented // not implemented
DeleteCommand (const DeleteCommand&); DeleteCommand (const DeleteCommand&);
@ -117,7 +122,8 @@ namespace CSMWorld
public: public:
DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); DeleteCommand (IdTable& model, const std::string& id,
UniversalId::Type type = UniversalId::Type_None, QUndoCommand *parent = 0);
virtual ~DeleteCommand(); virtual ~DeleteCommand();
@ -141,6 +147,29 @@ namespace CSMWorld
virtual void undo(); virtual void undo();
}; };
/// \brief Update cell ID according to x/y-coordinates
///
/// \note The new value will be calculated in the first call to redo instead of the
/// constructor to accommodate multiple coordinate-affecting commands being executed
/// in a macro.
class UpdateCellCommand : public QUndoCommand
{
IdTable& mModel;
int mRow;
QModelIndex mIndex;
QVariant mNew; // invalid, if new cell ID has not been calculated yet
QVariant mOld;
public:
UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent = 0);
virtual void redo();
virtual void undo();
};
class NestedTableStoring class NestedTableStoring
{ {
NestedTableWrapperBase* mOld; NestedTableWrapperBase* mOld;

View file

@ -136,6 +136,25 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter<ESM::Race> ())); mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter<ESM::Race> ()));
mRaces.getNestableColumn(index)->addColumn( mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String)); new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String));
// Race attributes
mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_RaceAttributes));
index = mRaces.getColumns()-1;
mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceAttributeAdapter()));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_RaceAttributes, ColumnBase::Display_String,
ColumnBase::Flag_Dialogue, false));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_RaceMaleValue, ColumnBase::Display_Integer));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_RaceFemaleValue, ColumnBase::Display_Integer));
// Race skill bonus
mRaces.addColumn (new NestedParentColumn<ESM::Race> (Columns::ColumnId_RaceSkillBonus));
index = mRaces.getColumns()-1;
mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new RaceSkillsBonusAdapter()));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_RaceSkill, ColumnBase::Display_RaceSkill));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_RaceBonus, ColumnBase::Display_Integer));
mSounds.addColumn (new StringIdColumn<ESM::Sound>); mSounds.addColumn (new StringIdColumn<ESM::Sound>);
mSounds.addColumn (new RecordStateColumn<ESM::Sound>); mSounds.addColumn (new RecordStateColumn<ESM::Sound>);
@ -242,6 +261,19 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoListAdapter ())); mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoListAdapter ()));
mTopicInfos.getNestableColumn(index)->addColumn( mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_ScriptText, ColumnBase::Display_ScriptLines)); new NestedChildColumn (Columns::ColumnId_ScriptText, ColumnBase::Display_ScriptLines));
// Special conditions
mTopicInfos.addColumn (new NestedParentColumn<Info> (Columns::ColumnId_InfoCondition));
index = mTopicInfos.getColumns()-1;
mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoConditionAdapter ()));
mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc));
// FIXME: don't have dynamic value enum delegate, use Display_String for now
mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_String));
mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp));
mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Value, ColumnBase::Display_Var));
mJournalInfos.addColumn (new StringIdColumn<Info> (true)); mJournalInfos.addColumn (new StringIdColumn<Info> (true));
mJournalInfos.addColumn (new RecordStateColumn<Info>); mJournalInfos.addColumn (new RecordStateColumn<Info>);
@ -256,10 +288,32 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell)); mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
mCells.addColumn (new NameColumn<Cell>); mCells.addColumn (new NameColumn<Cell>);
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep)); mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater)); mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater,
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx)); ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));
mCells.addColumn (new RegionColumn<Cell>); mCells.addColumn (new RegionColumn<Cell>);
mCells.addColumn (new RefNumCounterColumn<Cell>); mCells.addColumn (new RefNumCounterColumn<Cell>);
// Misc Cell data
mCells.addColumn (new NestedParentColumn<Cell> (Columns::ColumnId_Cell,
ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List));
index = mCells.getColumns()-1;
mCells.addAdapter (std::make_pair(&mCells.getColumn(index), new CellListAdapter ()));
mCells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Interior, ColumnBase::Display_Boolean,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));
mCells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Ambient, ColumnBase::Display_Integer));
mCells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Sunlight, ColumnBase::Display_Integer));
mCells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Fog, ColumnBase::Display_Integer));
mCells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_FogDensity, ColumnBase::Display_Float));
mCells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_WaterLevel, ColumnBase::Display_Float));
mCells.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_MapColor, ColumnBase::Display_Integer));
mEnchantments.addColumn (new StringIdColumn<ESM::Enchantment>); mEnchantments.addColumn (new StringIdColumn<ESM::Enchantment>);
mEnchantments.addColumn (new RecordStateColumn<ESM::Enchantment>); mEnchantments.addColumn (new RecordStateColumn<ESM::Enchantment>);
@ -296,7 +350,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mBodyParts.addColumn (new BodyPartTypeColumn<ESM::BodyPart>); mBodyParts.addColumn (new BodyPartTypeColumn<ESM::BodyPart>);
mBodyParts.addColumn (new VampireColumn<ESM::BodyPart>); mBodyParts.addColumn (new VampireColumn<ESM::BodyPart>);
mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Female, ESM::BodyPart::BPF_Female)); mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Female, ESM::BodyPart::BPF_Female));
mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Playable, ESM::BodyPart::BPF_NotPlayable, true)); mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Playable,
ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true));
mBodyParts.addColumn (new MeshTypeColumn<ESM::BodyPart>); mBodyParts.addColumn (new MeshTypeColumn<ESM::BodyPart>);
mBodyParts.addColumn (new ModelColumn<ESM::BodyPart>); mBodyParts.addColumn (new ModelColumn<ESM::BodyPart>);
mBodyParts.addColumn (new RaceColumn<ESM::BodyPart>); mBodyParts.addColumn (new RaceColumn<ESM::BodyPart>);
@ -343,7 +398,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
// new objects deleted in dtor of NestableColumn // new objects deleted in dtor of NestableColumn
// WARNING: The order of the columns below are assumed in PathgridPointListAdapter // WARNING: The order of the columns below are assumed in PathgridPointListAdapter
mPathgrids.getNestableColumn(index)->addColumn( mPathgrids.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_PathgridIndex, ColumnBase::Display_Integer, false)); new NestedChildColumn (Columns::ColumnId_PathgridIndex, ColumnBase::Display_Integer,
ColumnBase::Flag_Dialogue, false));
mPathgrids.getNestableColumn(index)->addColumn( mPathgrids.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer)); new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer));
mPathgrids.getNestableColumn(index)->addColumn( mPathgrids.getNestableColumn(index)->addColumn(
@ -355,7 +411,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
index = mPathgrids.getColumns()-1; index = mPathgrids.getColumns()-1;
mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ())); mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ()));
mPathgrids.getNestableColumn(index)->addColumn( mPathgrids.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_PathgridEdgeIndex, ColumnBase::Display_Integer, false)); new NestedChildColumn (Columns::ColumnId_PathgridEdgeIndex, ColumnBase::Display_Integer,
ColumnBase::Flag_Dialogue, false));
mPathgrids.getNestableColumn(index)->addColumn( mPathgrids.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer)); new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer));
mPathgrids.getNestableColumn(index)->addColumn( mPathgrids.getNestableColumn(index)->addColumn(
@ -368,7 +425,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mRefs.addColumn (new StringIdColumn<CellRef> (true)); mRefs.addColumn (new StringIdColumn<CellRef> (true));
mRefs.addColumn (new RecordStateColumn<CellRef>); mRefs.addColumn (new RecordStateColumn<CellRef>);
mRefs.addColumn (new FixedRecordTypeColumn<CellRef> (UniversalId::Type_Reference)); mRefs.addColumn (new FixedRecordTypeColumn<CellRef> (UniversalId::Type_Reference));
mRefs.addColumn (new CellColumn<CellRef>); mRefs.addColumn (new CellColumn<CellRef> (true));
mRefs.addColumn (new OriginalCellColumn<CellRef>);
mRefs.addColumn (new IdColumn<CellRef>); mRefs.addColumn (new IdColumn<CellRef>);
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false)); mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false));
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 1, false)); mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 1, false));
@ -433,7 +491,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
addModel (new IdTree (&mTopicInfos, &mTopicInfos, IdTable::Feature_ReorderWithinTopic), addModel (new IdTree (&mTopicInfos, &mTopicInfos, IdTable::Feature_ReorderWithinTopic),
UniversalId::Type_TopicInfo); UniversalId::Type_TopicInfo);
addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo); addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo);
addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell); addModel (new IdTree (&mCells, &mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell);
addModel (new IdTree (&mEnchantments, &mEnchantments), UniversalId::Type_Enchantment); addModel (new IdTree (&mEnchantments, &mEnchantments), UniversalId::Type_Enchantment);
addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart);
addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);
@ -457,6 +515,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
UniversalId::Type_Texture); UniversalId::Type_Texture);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)), addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)),
UniversalId::Type_Video); UniversalId::Type_Video);
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
} }
CSMWorld::Data::~Data() CSMWorld::Data::~Data()
@ -778,7 +838,6 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mReader = 0; mReader = 0;
mDialogue = 0; mDialogue = 0;
mRefLoadCache.clear();
mReader = new ESM::ESMReader; mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder); mReader->setEncoder (&mEncoder);
@ -815,7 +874,6 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
mReader = 0; mReader = 0;
mDialogue = 0; mDialogue = 0;
mRefLoadCache.clear();
return true; return true;
} }
@ -860,9 +918,16 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
case ESM::REC_CELL: case ESM::REC_CELL:
{ {
mCells.load (*mReader, mBase); int index = mCells.load (*mReader, mBase);
std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (mCells.getSize()-1)); if (index < 0 || index >= mCells.getSize())
mRefs.load (*mReader, mCells.getSize()-1, mBase, mRefLoadCache[cellId], messages); {
// log an error and continue loading the refs to the last loaded cell
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_None);
messages.add (id, "Logic error: cell index out of bounds");
index = mCells.getSize()-1;
}
std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (index));
mRefs.load (*mReader, index, mBase, mRefLoadCache[cellId], messages);
break; break;
} }

View file

@ -88,7 +88,7 @@ namespace CSMWorld
IdCollection<ESM::StartScript> mStartScripts; IdCollection<ESM::StartScript> mStartScripts;
NestedInfoCollection mTopicInfos; NestedInfoCollection mTopicInfos;
InfoCollection mJournalInfos; InfoCollection mJournalInfos;
IdCollection<Cell> mCells; NestedIdCollection<Cell> mCells;
IdCollection<LandTexture> mLandTextures; IdCollection<LandTexture> mLandTextures;
IdCollection<Land> mLand; IdCollection<Land> mLand;
RefIdCollection mReferenceables; RefIdCollection mReferenceables;

View file

@ -74,6 +74,15 @@ namespace CSMWorld
{ {
ESXRecordT record; ESXRecordT record;
// Sometimes id (i.e. NAME of the cell) may be different to the id we stored
// earlier. e.g. NAME == "Vivec, Arena" but id == "#-4 11". Sometime NAME is
// missing altogether for scripts or cells.
//
// In such cases the returned index will be -1. We then try updating the
// IdAccessor's id manually (e.g. set mId of the record to "Vivec, Arena")
// and try getting the index once more after loading the record. The mId of the
// record would have changed to "#-4 11" after the load, and searchId() should find
// it (if this is a modify)
int index = this->searchId (id); int index = this->searchId (id);
if (index==-1) if (index==-1)

View file

@ -30,7 +30,13 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const
QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const
{ {
if ((role!=Qt::DisplayRole && role!=Qt::EditRole) || index.row() < 0 || index.column() < 0) if (index.row() < 0 || index.column() < 0)
return QVariant();
if (role==ColumnBase::Role_ColumnId)
return QVariant (getColumnId (index.column()));
if ((role!=Qt::DisplayRole && role!=Qt::EditRole))
return QVariant(); return QVariant();
if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable()) if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable())
@ -56,6 +62,9 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation
if (role==ColumnBase::Role_Display) if (role==ColumnBase::Role_Display)
return mIdCollection->getColumn (section).mDisplayType; return mIdCollection->getColumn (section).mDisplayType;
if (role==ColumnBase::Role_ColumnId)
return getColumnId (section);
return QVariant(); return QVariant();
} }
@ -65,8 +74,7 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
{ {
mIdCollection->setData (index.row(), index.column(), value); mIdCollection->setData (index.row(), index.column(), value);
emit dataChanged (CSMWorld::IdTable::index (index.row(), 0), emit dataChanged (index, index);
CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1));
return true; return true;
} }
@ -145,17 +153,17 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column)
return index(mIdCollection->getIndex (id), column); return index(mIdCollection->getIndex (id), column);
} }
void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record) void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record, CSMWorld::UniversalId::Type type)
{ {
int index = mIdCollection->searchId (id); int index = mIdCollection->searchId (id);
if (index==-1) if (index==-1)
{ {
int index = mIdCollection->getAppendIndex (id); int index = mIdCollection->getAppendIndex (id, type);
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendRecord (record); mIdCollection->appendRecord (record, type);
endInsertRows(); endInsertRows();
} }

View file

@ -59,7 +59,8 @@ namespace CSMWorld
virtual QModelIndex getModelIndex (const std::string& id, int column) const; virtual QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const std::string& id, const RecordBase& record); void setRecord (const std::string& id, const RecordBase& record,
UniversalId::Type type = UniversalId::Type_None);
///< Add record or overwrite existing recrod. ///< Add record or overwrite existing recrod.
const RecordBase& getRecord (const std::string& id) const; const RecordBase& getRecord (const std::string& id) const;

View file

@ -46,10 +46,16 @@ void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::
{ {
mFilter = filter; mFilter = filter;
updateColumnMap(); updateColumnMap();
invalidateFilter(); reset();
} }
bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{ {
return QSortFilterProxyModel::lessThan(left, right); return QSortFilterProxyModel::lessThan(left, right);
} }
void CSMWorld::IdTableProxyModel::refreshFilter()
{
updateColumnMap();
invalidateFilter();
}

View file

@ -34,6 +34,8 @@ namespace CSMWorld
void setFilter (const boost::shared_ptr<CSMFilter::Node>& filter); void setFilter (const boost::shared_ptr<CSMFilter::Node>& filter);
void refreshFilter();
protected: protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const; bool lessThan(const QModelIndex &left, const QModelIndex &right) const;

View file

@ -74,7 +74,7 @@ QVariant CSMWorld::IdTree::nestedHeaderData(int section, int subSection, Qt::Ori
return tr(parentColumn->nestedColumn(subSection).getTitle().c_str()); return tr(parentColumn->nestedColumn(subSection).getTitle().c_str());
if (role==ColumnBase::Role_Flags) if (role==ColumnBase::Role_Flags)
return idCollection()->getColumn (section).mFlags; return parentColumn->nestedColumn(subSection).mFlags;
if (role==ColumnBase::Role_Display) if (role==ColumnBase::Role_Display)
return parentColumn->nestedColumn(subSection).mDisplayType; return parentColumn->nestedColumn(subSection).mDisplayType;
@ -92,8 +92,8 @@ bool CSMWorld::IdTree::setData (const QModelIndex &index, const QVariant &value,
mNestedCollection->setNestedData(parentAddress.first, parentAddress.second, value, index.row(), index.column()); mNestedCollection->setNestedData(parentAddress.first, parentAddress.second, value, index.row(), index.column());
emit dataChanged (CSMWorld::IdTree::index (parentAddress.first, 0), emit dataChanged (index, index);
CSMWorld::IdTree::index (parentAddress.first, idCollection()->getColumns()-1));
return true; return true;
} }
else else

View file

@ -173,6 +173,17 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s
RecordConstIterator begin = getRecords().begin()+iter->second; RecordConstIterator begin = getRecords().begin()+iter->second;
while (begin != getRecords().begin())
{
if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2))
{
// we've gone one too far, go back
++begin;
break;
}
--begin;
}
// Find end // Find end
RecordConstIterator end = begin; RecordConstIterator end = begin;

View file

@ -487,7 +487,7 @@ namespace CSMWorld
void InfoListAdapter::removeRow(Record<Info>& record, int rowToRemove) const void InfoListAdapter::removeRow(Record<Info>& record, int rowToRemove) const
{ {
throw std::logic_error ("cannot add a row to a fixed table"); throw std::logic_error ("cannot remove a row to a fixed table");
} }
void InfoListAdapter::setTable(Record<Info>& record, void InfoListAdapter::setTable(Record<Info>& record,
@ -534,4 +534,667 @@ namespace CSMWorld
{ {
return 1; // fixed at size 1 return 1; // fixed at size 1
} }
// ESM::DialInfo::SelectStruct.mSelectRule
// 012345...
// ^^^ ^^
// ||| ||
// ||| |+------------- condition variable string
// ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc
// ||+---------------- function index (encoded, where function == '1')
// |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc
// +------------------ unknown
//
InfoConditionAdapter::InfoConditionAdapter () {}
void InfoConditionAdapter::addRow(Record<Info>& record, int position) const
{
Info info = record.get();
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
// blank row
ESM::DialInfo::SelectStruct condStruct;
condStruct.mSelectRule = "00000";
condStruct.mValue = ESM::Variant();
condStruct.mValue.setType(ESM::VT_Int); // default to ints
conditions.insert(conditions.begin()+position, condStruct);
record.setModified (info);
}
void InfoConditionAdapter::removeRow(Record<Info>& record, int rowToRemove) const
{
Info info = record.get();
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
if (rowToRemove < 0 || rowToRemove >= static_cast<int> (conditions.size()))
throw std::runtime_error ("index out of range");
conditions.erase(conditions.begin()+rowToRemove);
record.setModified (info);
}
void InfoConditionAdapter::setTable(Record<Info>& record,
const NestedTableWrapperBase& nestedTable) const
{
Info info = record.get();
info.mSelects =
static_cast<const NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct> >&>(nestedTable).mNestedTable;
record.setModified (info);
}
NestedTableWrapperBase* InfoConditionAdapter::table(const Record<Info>& record) const
{
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct> >(record.get().mSelects);
}
// See the mappings in MWDialogue::SelectWrapper::getArgument
// from ESM::Attribute, ESM::Skill and MWMechanics::CreatureStats (for AI)
static std::map<const std::string, std::string> populateEncToInfoFunc()
{
std::map<const std::string, std::string> funcMap;
funcMap["00"] = "Rank Low";
funcMap["01"] = "Rank High";
funcMap["02"] = "Rank Requirement";
funcMap["03"] = "Reputation";
funcMap["04"] = "Health Percent";
funcMap["05"] = "PC Reputation";
funcMap["06"] = "PC Level";
funcMap["07"] = "PC Health Percent";
funcMap["08"] = "PC Magicka";
funcMap["09"] = "PC Fatigue";
funcMap["10"] = "PC Strength";
funcMap["11"] = "PC Block";
funcMap["12"] = "PC Armoror";
funcMap["13"] = "PC Medium Armor";
funcMap["14"] = "PC Heavy Armor";
funcMap["15"] = "PC Blunt Weapon";
funcMap["16"] = "PC Long Blade";
funcMap["17"] = "PC Axe";
funcMap["18"] = "PC Spear";
funcMap["19"] = "PC Athletics";
funcMap["20"] = "PC Enchant";
funcMap["21"] = "PC Destruction";
funcMap["22"] = "PC Alteration";
funcMap["23"] = "PC Illusion";
funcMap["24"] = "PC Conjuration";
funcMap["25"] = "PC Mysticism";
funcMap["26"] = "PC Restoration";
funcMap["27"] = "PC Alchemy";
funcMap["28"] = "PC Unarmored";
funcMap["29"] = "PC Security";
funcMap["30"] = "PC Sneak";
funcMap["31"] = "PC Acrobatics";
funcMap["32"] = "PC Light Armor";
funcMap["33"] = "PC Short Blade";
funcMap["34"] = "PC Marksman";
funcMap["35"] = "PC Merchantile";
funcMap["36"] = "PC Speechcraft";
funcMap["37"] = "PC Hand To Hand";
funcMap["38"] = "PC Sex";
funcMap["39"] = "PC Expelled";
funcMap["40"] = "PC Common Disease";
funcMap["41"] = "PC Blight Disease";
funcMap["42"] = "PC Clothing Modifier";
funcMap["43"] = "PC Crime Level";
funcMap["44"] = "Same Sex";
funcMap["45"] = "Same Race";
funcMap["46"] = "Same Faction";
funcMap["47"] = "Faction Rank Difference";
funcMap["48"] = "Detected";
funcMap["49"] = "Alarmed";
funcMap["50"] = "Choice";
funcMap["51"] = "PC Intelligence";
funcMap["52"] = "PC Willpower";
funcMap["53"] = "PC Agility";
funcMap["54"] = "PC Speed";
funcMap["55"] = "PC Endurance";
funcMap["56"] = "PC Personality";
funcMap["57"] = "PC Luck";
funcMap["58"] = "PC Corpus";
funcMap["59"] = "Weather";
funcMap["60"] = "PC Vampire";
funcMap["61"] = "Level";
funcMap["62"] = "Attacked";
funcMap["63"] = "Talked To PC";
funcMap["64"] = "PC Health";
funcMap["65"] = "Creature Target";
funcMap["66"] = "Friend Hit";
funcMap["67"] = "Fight";
funcMap["68"] = "Hello";
funcMap["69"] = "Alarm";
funcMap["70"] = "Flee";
funcMap["71"] = "Should Attack";
funcMap["72"] = "Werewolf";
funcMap["73"] = "PC Werewolf Kills";
return funcMap;
}
static const std::map<const std::string, std::string> sEncToInfoFunc = populateEncToInfoFunc();
QVariant InfoConditionAdapter::getData(const Record<Info>& record,
int subRowIndex, int subColIndex) const
{
Info info = record.get();
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
throw std::runtime_error ("index out of range");
switch (subColIndex)
{
case 0:
{
char condType = conditions[subRowIndex].mSelectRule[1];
switch (condType)
{
case '0': return 0; // blank space
case '1': return 1; // Function
case '2': return 2; // Global
case '3': return 3; // Local
case '4': return 4; // Journal
case '5': return 5; // Item
case '6': return 6; // Dead
case '7': return 7; // Not ID
case '8': return 8; // Not Factio
case '9': return 9; // Not Class
case 'A': return 10; // Not Race
case 'B': return 11; // Not Cell
case 'C': return 12; // Not Local
default: return QVariant(); // TODO: log an error?
}
}
case 1:
{
if (conditions[subRowIndex].mSelectRule[1] == '1')
{
// throws an exception if the encoding is not found
return sEncToInfoFunc.at(conditions[subRowIndex].mSelectRule.substr(2, 2)).c_str();
}
else
return QString(conditions[subRowIndex].mSelectRule.substr(5).c_str());
}
case 2:
{
char compType = conditions[subRowIndex].mSelectRule[4];
switch (compType)
{
case '0': return 3; // =
case '1': return 0; // !=
case '2': return 4; // >
case '3': return 5; // >=
case '4': return 1; // <
case '5': return 2; // <=
default: return QVariant(); // TODO: log an error?
}
}
case 3:
{
switch (conditions[subRowIndex].mValue.getType())
{
case ESM::VT_String:
{
return QString::fromUtf8 (conditions[subRowIndex].mValue.getString().c_str());
}
case ESM::VT_Int:
case ESM::VT_Short:
case ESM::VT_Long:
{
return conditions[subRowIndex].mValue.getInteger();
}
case ESM::VT_Float:
{
return conditions[subRowIndex].mValue.getFloat();
}
default: return QVariant();
}
}
default: throw std::runtime_error("Info condition subcolumn index out of range");
}
}
void InfoConditionAdapter::setData(Record<Info>& record,
const QVariant& value, int subRowIndex, int subColIndex) const
{
Info info = record.get();
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
throw std::runtime_error ("index out of range");
switch (subColIndex)
{
case 0:
{
// See sInfoCondFunc in columns.cpp for the enum values
switch (value.toInt())
{
// FIXME: when these change the values of the other columns need to change
// correspondingly (and automatically)
case 1:
{
conditions[subRowIndex].mSelectRule[1] = '1'; // Function
// default to "Rank Low"
conditions[subRowIndex].mSelectRule[2] = '0';
conditions[subRowIndex].mSelectRule[3] = '0';
break;
}
case 2: conditions[subRowIndex].mSelectRule[1] = '2'; break; // Global
case 3: conditions[subRowIndex].mSelectRule[1] = '3'; break; // Local
case 4: conditions[subRowIndex].mSelectRule[1] = '4'; break; // Journal
case 5: conditions[subRowIndex].mSelectRule[1] = '5'; break; // Item
case 6: conditions[subRowIndex].mSelectRule[1] = '6'; break; // Dead
case 7: conditions[subRowIndex].mSelectRule[1] = '7'; break; // Not ID
case 8: conditions[subRowIndex].mSelectRule[1] = '8'; break; // Not Faction
case 9: conditions[subRowIndex].mSelectRule[1] = '9'; break; // Not Class
case 10: conditions[subRowIndex].mSelectRule[1] = 'A'; break; // Not Race
case 11: conditions[subRowIndex].mSelectRule[1] = 'B'; break; // Not Cell
case 12: conditions[subRowIndex].mSelectRule[1] = 'C'; break; // Not Local
default: return; // return without saving
}
break;
}
case 1:
{
if (conditions[subRowIndex].mSelectRule[1] == '1')
{
std::map<const std::string, std::string>::const_iterator it = sEncToInfoFunc.begin();
for (;it != sEncToInfoFunc.end(); ++it)
{
if (it->second == value.toString().toUtf8().constData())
{
std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 2);
rule.append(it->first);
// leave old values for undo (NOTE: may not be vanilla's behaviour)
rule.append(conditions[subRowIndex].mSelectRule.substr(4));
conditions[subRowIndex].mSelectRule = rule;
break;
}
}
if (it == sEncToInfoFunc.end())
return; // return without saving; TODO: maybe log an error here
}
else
{
// FIXME: validate the string values before saving, based on the current function
std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 5);
conditions[subRowIndex].mSelectRule = rule.append(value.toString().toUtf8().constData());
}
break;
}
case 2:
{
// See sInfoCondComp in columns.cpp for the enum values
switch (value.toInt())
{
case 0: conditions[subRowIndex].mSelectRule[4] = '1'; break; // !=
case 1: conditions[subRowIndex].mSelectRule[4] = '4'; break; // <
case 2: conditions[subRowIndex].mSelectRule[4] = '5'; break; // <=
case 3: conditions[subRowIndex].mSelectRule[4] = '0'; break; // =
case 4: conditions[subRowIndex].mSelectRule[4] = '2'; break; // >
case 5: conditions[subRowIndex].mSelectRule[4] = '3'; break; // >=
default: return; // return without saving
}
break;
}
case 3:
{
switch (conditions[subRowIndex].mValue.getType())
{
case ESM::VT_String:
{
conditions[subRowIndex].mValue.setString (value.toString().toUtf8().constData());
break;
}
case ESM::VT_Int:
case ESM::VT_Short:
case ESM::VT_Long:
{
conditions[subRowIndex].mValue.setInteger (value.toInt());
break;
}
case ESM::VT_Float:
{
conditions[subRowIndex].mValue.setFloat (value.toFloat());
break;
}
default: break;
}
break;
}
default: throw std::runtime_error("Info condition subcolumn index out of range");
}
record.setModified (info);
}
int InfoConditionAdapter::getColumnsCount(const Record<Info>& record) const
{
return 4;
}
int InfoConditionAdapter::getRowsCount(const Record<Info>& record) const
{
return static_cast<int>(record.get().mSelects.size());
}
RaceAttributeAdapter::RaceAttributeAdapter () {}
void RaceAttributeAdapter::addRow(Record<ESM::Race>& record, int position) const
{
// Do nothing, this table cannot be changed by the user
}
void RaceAttributeAdapter::removeRow(Record<ESM::Race>& record, int rowToRemove) const
{
// Do nothing, this table cannot be changed by the user
}
void RaceAttributeAdapter::setTable(Record<ESM::Race>& record,
const NestedTableWrapperBase& nestedTable) const
{
ESM::Race race = record.get();
race.mData =
static_cast<const NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >&>(nestedTable).mNestedTable.at(0);
record.setModified (race);
}
NestedTableWrapperBase* RaceAttributeAdapter::table(const Record<ESM::Race>& record) const
{
std::vector<typename ESM::Race::RADTstruct> wrap;
wrap.push_back(record.get().mData);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >(wrap);
}
QVariant RaceAttributeAdapter::getData(const Record<ESM::Race>& record,
int subRowIndex, int subColIndex) const
{
ESM::Race race = record.get();
if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length)
throw std::runtime_error ("index out of range");
switch (subColIndex)
{
case 0: return QString(ESM::Attribute::sAttributeNames[subRowIndex].c_str());
case 1: return race.mData.mAttributeValues[subRowIndex].mMale;
case 2: return race.mData.mAttributeValues[subRowIndex].mFemale;
default: throw std::runtime_error("Race Attribute subcolumn index out of range");
}
}
void RaceAttributeAdapter::setData(Record<ESM::Race>& record,
const QVariant& value, int subRowIndex, int subColIndex) const
{
ESM::Race race = record.get();
if (subRowIndex < 0 || subRowIndex >= ESM::Attribute::Length)
throw std::runtime_error ("index out of range");
switch (subColIndex)
{
case 0: return; // throw an exception here?
case 1: race.mData.mAttributeValues[subRowIndex].mMale = value.toInt(); break;
case 2: race.mData.mAttributeValues[subRowIndex].mFemale = value.toInt(); break;
default: throw std::runtime_error("Race Attribute subcolumn index out of range");
}
record.setModified (race);
}
int RaceAttributeAdapter::getColumnsCount(const Record<ESM::Race>& record) const
{
return 3; // attrib, male, female
}
int RaceAttributeAdapter::getRowsCount(const Record<ESM::Race>& record) const
{
return ESM::Attribute::Length; // there are 8 attributes
}
RaceSkillsBonusAdapter::RaceSkillsBonusAdapter () {}
void RaceSkillsBonusAdapter::addRow(Record<ESM::Race>& record, int position) const
{
// Do nothing, this table cannot be changed by the user
}
void RaceSkillsBonusAdapter::removeRow(Record<ESM::Race>& record, int rowToRemove) const
{
// Do nothing, this table cannot be changed by the user
}
void RaceSkillsBonusAdapter::setTable(Record<ESM::Race>& record,
const NestedTableWrapperBase& nestedTable) const
{
ESM::Race race = record.get();
race.mData =
static_cast<const NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >&>(nestedTable).mNestedTable.at(0);
record.setModified (race);
}
NestedTableWrapperBase* RaceSkillsBonusAdapter::table(const Record<ESM::Race>& record) const
{
std::vector<typename ESM::Race::RADTstruct> wrap;
wrap.push_back(record.get().mData);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::Race::RADTstruct> >(wrap);
}
QVariant RaceSkillsBonusAdapter::getData(const Record<ESM::Race>& record,
int subRowIndex, int subColIndex) const
{
ESM::Race race = record.get();
if (subRowIndex < 0 || subRowIndex >= static_cast<int>(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0])))
throw std::runtime_error ("index out of range");
switch (subColIndex)
{
case 0: return race.mData.mBonus[subRowIndex].mSkill; // can be -1
case 1: return race.mData.mBonus[subRowIndex].mBonus;
default: throw std::runtime_error("Race skill bonus subcolumn index out of range");
}
}
void RaceSkillsBonusAdapter::setData(Record<ESM::Race>& record,
const QVariant& value, int subRowIndex, int subColIndex) const
{
ESM::Race race = record.get();
if (subRowIndex < 0 || subRowIndex >= static_cast<int>(sizeof(race.mData.mBonus)/sizeof(race.mData.mBonus[0])))
throw std::runtime_error ("index out of range");
switch (subColIndex)
{
case 0: race.mData.mBonus[subRowIndex].mSkill = value.toInt(); break; // can be -1
case 1: race.mData.mBonus[subRowIndex].mBonus = value.toInt(); break;
default: throw std::runtime_error("Race skill bonus subcolumn index out of range");
}
record.setModified (race);
}
int RaceSkillsBonusAdapter::getColumnsCount(const Record<ESM::Race>& record) const
{
return 2; // skill, bonus
}
int RaceSkillsBonusAdapter::getRowsCount(const Record<ESM::Race>& record) const
{
// there are 7 skill bonuses
return static_cast<int>(sizeof(record.get().mData.mBonus)/sizeof(record.get().mData.mBonus[0]));
}
CellListAdapter::CellListAdapter () {}
void CellListAdapter::addRow(Record<CSMWorld::Cell>& record, int position) const
{
throw std::logic_error ("cannot add a row to a fixed table");
}
void CellListAdapter::removeRow(Record<CSMWorld::Cell>& record, int rowToRemove) const
{
throw std::logic_error ("cannot remove a row to a fixed table");
}
void CellListAdapter::setTable(Record<CSMWorld::Cell>& record,
const NestedTableWrapperBase& nestedTable) const
{
throw std::logic_error ("table operation not supported");
}
NestedTableWrapperBase* CellListAdapter::table(const Record<CSMWorld::Cell>& record) const
{
throw std::logic_error ("table operation not supported");
}
QVariant CellListAdapter::getData(const Record<CSMWorld::Cell>& record,
int subRowIndex, int subColIndex) const
{
CSMWorld::Cell cell = record.get();
bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0;
bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0;
bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0;
switch (subColIndex)
{
case 0: return isInterior;
case 1: return (isInterior && !behaveLikeExterior) ?
cell.mAmbi.mAmbient : QVariant(QVariant::UserType);
case 2: return (isInterior && !behaveLikeExterior) ?
cell.mAmbi.mSunlight : QVariant(QVariant::UserType);
case 3: return (isInterior && !behaveLikeExterior) ?
cell.mAmbi.mFog : QVariant(QVariant::UserType);
case 4: return (isInterior && !behaveLikeExterior) ?
cell.mAmbi.mFogDensity : QVariant(QVariant::UserType);
case 5:
{
if (isInterior && !behaveLikeExterior && interiorWater)
return cell.mWater;
else
return QVariant(QVariant::UserType);
}
case 6: return isInterior ?
QVariant(QVariant::UserType) : cell.mMapColor; // TODO: how to select?
//case 7: return isInterior ?
//behaveLikeExterior : QVariant(QVariant::UserType);
default: throw std::runtime_error("Cell subcolumn index out of range");
}
}
void CellListAdapter::setData(Record<CSMWorld::Cell>& record,
const QVariant& value, int subRowIndex, int subColIndex) const
{
CSMWorld::Cell cell = record.get();
bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0;
bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0;
bool interiorWater = (cell.mData.mFlags & ESM::Cell::HasWater) != 0;
switch (subColIndex)
{
case 0:
{
if (value.toBool())
cell.mData.mFlags |= ESM::Cell::Interior;
else
cell.mData.mFlags &= ~ESM::Cell::Interior;
break;
}
case 1:
{
if (isInterior && !behaveLikeExterior)
cell.mAmbi.mAmbient = static_cast<int32_t>(value.toInt());
else
return; // return without saving
break;
}
case 2:
{
if (isInterior && !behaveLikeExterior)
cell.mAmbi.mSunlight = static_cast<int32_t>(value.toInt());
else
return; // return without saving
break;
}
case 3:
{
if (isInterior && !behaveLikeExterior)
cell.mAmbi.mFog = static_cast<int32_t>(value.toInt());
else
return; // return without saving
break;
}
case 4:
{
if (isInterior && !behaveLikeExterior)
cell.mAmbi.mFogDensity = value.toFloat();
else
return; // return without saving
break;
}
case 5:
{
if (isInterior && !behaveLikeExterior && interiorWater)
cell.mWater = value.toFloat();
else
return; // return without saving
break;
}
case 6:
{
if (!isInterior)
cell.mMapColor = value.toInt();
else
return; // return without saving
break;
}
#if 0
// redundant since this flag is shown in the main table as "Interior Sky"
// keep here for documenting the logic based on vanilla
case 7:
{
if (isInterior)
{
if (value.toBool())
cell.mData.mFlags |= ESM::Cell::QuasiEx;
else
cell.mData.mFlags &= ~ESM::Cell::QuasiEx;
}
else
return; // return without saving
break;
}
#endif
default: throw std::runtime_error("Cell subcolumn index out of range");
}
record.setModified (cell);
}
int CellListAdapter::getColumnsCount(const Record<CSMWorld::Cell>& record) const
{
return 7;
}
int CellListAdapter::getRowsCount(const Record<CSMWorld::Cell>& record) const
{
return 1; // fixed at size 1
}
} }

View file

@ -7,9 +7,11 @@
#include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back #include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back
#include <components/esm/loadskil.hpp> // for converting skill names #include <components/esm/loadskil.hpp> // for converting skill names
#include <components/esm/attr.hpp> // for converting attributes #include <components/esm/attr.hpp> // for converting attributes
#include <components/esm/loadrace.hpp>
#include "nestedcolumnadapter.hpp" #include "nestedcolumnadapter.hpp"
#include "nestedtablewrapper.hpp" #include "nestedtablewrapper.hpp"
#include "cell.hpp"
namespace ESM namespace ESM
{ {
@ -295,7 +297,7 @@ namespace CSMWorld
case 0: case 0:
{ {
if (effect.mEffectID >=0 && effect.mEffectID < ESM::MagicEffect::Length) if (effect.mEffectID >=0 && effect.mEffectID < ESM::MagicEffect::Length)
return effect.mRange; return effect.mEffectID;
else else
throw std::runtime_error("Magic effects ID unexpected value"); throw std::runtime_error("Magic effects ID unexpected value");
} }
@ -396,6 +398,106 @@ namespace CSMWorld
virtual int getRowsCount(const Record<Info>& record) const; virtual int getRowsCount(const Record<Info>& record) const;
}; };
class InfoConditionAdapter : public NestedColumnAdapter<Info>
{
public:
InfoConditionAdapter ();
virtual void addRow(Record<Info>& record, int position) const;
virtual void removeRow(Record<Info>& record, int rowToRemove) const;
virtual void setTable(Record<Info>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<Info>& record) const;
virtual QVariant getData(const Record<Info>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<Info>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<Info>& record) const;
virtual int getRowsCount(const Record<Info>& record) const;
};
class RaceAttributeAdapter : public NestedColumnAdapter<ESM::Race>
{
public:
RaceAttributeAdapter ();
virtual void addRow(Record<ESM::Race>& record, int position) const;
virtual void removeRow(Record<ESM::Race>& record, int rowToRemove) const;
virtual void setTable(Record<ESM::Race>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<ESM::Race>& record) const;
virtual QVariant getData(const Record<ESM::Race>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<ESM::Race>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<ESM::Race>& record) const;
virtual int getRowsCount(const Record<ESM::Race>& record) const;
};
class RaceSkillsBonusAdapter : public NestedColumnAdapter<ESM::Race>
{
public:
RaceSkillsBonusAdapter ();
virtual void addRow(Record<ESM::Race>& record, int position) const;
virtual void removeRow(Record<ESM::Race>& record, int rowToRemove) const;
virtual void setTable(Record<ESM::Race>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<ESM::Race>& record) const;
virtual QVariant getData(const Record<ESM::Race>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<ESM::Race>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<ESM::Race>& record) const;
virtual int getRowsCount(const Record<ESM::Race>& record) const;
};
class CellListAdapter : public NestedColumnAdapter<CSMWorld::Cell>
{
public:
CellListAdapter ();
virtual void addRow(Record<CSMWorld::Cell>& record, int position) const;
virtual void removeRow(Record<CSMWorld::Cell>& record, int rowToRemove) const;
virtual void setTable(Record<CSMWorld::Cell>& record,
const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* table(const Record<CSMWorld::Cell>& record) const;
virtual QVariant getData(const Record<CSMWorld::Cell>& record,
int subRowIndex, int subColIndex) const;
virtual void setData(Record<CSMWorld::Cell>& record,
const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getColumnsCount(const Record<CSMWorld::Cell>& record) const;
virtual int getRowsCount(const Record<CSMWorld::Cell>& record) const;
};
} }
#endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H #endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H

View file

@ -1,8 +1,18 @@
#include "ref.hpp" #include "ref.hpp"
#include <cmath>
CSMWorld::CellRef::CellRef() CSMWorld::CellRef::CellRef()
{ {
mRefNum.mIndex = 0; mRefNum.mIndex = 0;
mRefNum.mContentFile = 0; mRefNum.mContentFile = 0;
} }
std::pair<int, int> CSMWorld::CellRef::getCellIndex() const
{
const int cellSize = 8192;
return std::make_pair (
std::floor (mPos.pos[0]/cellSize), std::floor (mPos.pos[1]/cellSize));
}

View file

@ -1,6 +1,8 @@
#ifndef CSM_WOLRD_REF_H #ifndef CSM_WOLRD_REF_H
#define CSM_WOLRD_REF_H #define CSM_WOLRD_REF_H
#include <utility>
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
namespace CSMWorld namespace CSMWorld
@ -10,8 +12,12 @@ namespace CSMWorld
{ {
std::string mId; std::string mId;
std::string mCell; std::string mCell;
std::string mOriginalCell;
CellRef(); CellRef();
/// Calculate cell index based on coordinates (x and y)
std::pair<int, int> getCellIndex() const;
}; };
} }

View file

@ -2,8 +2,10 @@
#include "refcollection.hpp" #include "refcollection.hpp"
#include <sstream> #include <sstream>
#include <iostream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/loadcell.hpp>
#include "ref.hpp" #include "ref.hpp"
#include "cell.hpp" #include "cell.hpp"
@ -20,14 +22,75 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CellRef ref; CellRef ref;
bool deleted = false; bool deleted = false;
ESM::MovedCellRef mref;
while (ESM::Cell::getNextRef (reader, ref, deleted)) // hack to initialise mindex
while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, deleted, true, &mref))
{ {
// Keep mOriginalCell empty when in modified (as an indicator that the
// original cell will always be equal the current cell).
ref.mOriginalCell = base ? cell2.mId : "";
if (cell.get().isExterior())
{
// ignoring moved references sub-record; instead calculate cell from coordinates
std::pair<int, int> index = ref.getCellIndex();
std::ostringstream stream;
stream << "#" << index.first << " " << index.second;
ref.mCell = stream.str();
if (!base && // don't try to update base records
mref.mRefNum.mIndex != 0) // MVRF tag found
{
// there is a requirement for a placeholder where the original object was
//
// see the forum discussions here for more details:
// https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30
ref.mOriginalCell = cell2.mId;
if (deleted)
{
// FIXME: how to mark the record deleted?
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
mCells.getId (cellIndex));
messages.add (id, "Moved reference "+ref.mRefID+" is in DELE state");
continue;
}
// It is not always possibe to ignore moved references sub-record and
// calculate from coordinates. Some mods may place the ref in positions
// outside normal bounds, resulting in non sensical cell id's. This often
// happens if the moved ref was deleted.
//
// Use the target cell from the MVRF tag but if different output an error
// message
if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1])
{
std::cerr << "The Position of moved ref "
<< ref.mRefID << " does not match the target cell" << std::endl;
std::cerr << "Position: #" << index.first << " " << index.second
<<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl;
std::ostringstream stream;
stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1];
ref.mCell = stream.str(); // overwrite
}
}
}
else
ref.mCell = cell2.mId; ref.mCell = cell2.mId;
/// \todo handle moved references // ignore content file number
std::map<ESM::RefNum, std::string>::iterator iter = cache.begin();
std::map<ESM::RefNum, std::string>::iterator iter = cache.find (ref.mRefNum); for (; iter != cache.end(); ++iter)
{
if (ref.mRefNum.mIndex == iter->first.mIndex)
break;
}
if (deleted) if (deleted)
{ {

View file

@ -27,8 +27,7 @@ namespace CSMWorld
{} {}
void load (ESM::ESMReader& reader, int cellIndex, bool base, void load (ESM::ESMReader& reader, int cellIndex, bool base,
std::map<ESM::RefNum, std::string>& cache, std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages);
CSMDoc::Messages& messages);
///< Load a sequence of references. ///< Load a sequence of references.
std::string getNewId(); std::string getNewId();

View file

@ -13,7 +13,7 @@ CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns)
CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const PotionColumns& columns, CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const PotionColumns& columns,
const RefIdColumn *autoCalc) const RefIdColumn *autoCalc)
: InventoryRefIdAdapter<ESM::Potion> (UniversalId::Type_Potion, columns), : InventoryRefIdAdapter<ESM::Potion> (UniversalId::Type_Potion, columns),
mAutoCalc (autoCalc), mColumns(columns) mColumns(columns), mAutoCalc (autoCalc)
{} {}
QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data,
@ -468,7 +468,10 @@ CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns)
mClass(NULL), mClass(NULL),
mFaction(NULL), mFaction(NULL),
mHair(NULL), mHair(NULL),
mHead(NULL) mHead(NULL),
mAttributes(NULL),
mSkills(NULL),
mMisc(NULL)
{} {}
CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns)
@ -496,6 +499,17 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re
if (column==mColumns.mHead) if (column==mColumns.mHead)
return QString::fromUtf8 (record.get().mHead.c_str()); return QString::fromUtf8 (record.get().mHead.c_str());
if (column==mColumns.mAttributes || column==mColumns.mSkills)
{
if ((record.get().mFlags & ESM::NPC::Autocalc) != 0)
return QVariant(QVariant::UserType);
else
return true;
}
if (column==mColumns.mMisc)
return true;
std::map<const RefIdColumn *, unsigned int>::const_iterator iter = std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column); mColumns.mFlags.find (column);
@ -538,6 +552,338 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
} }
} }
CSMWorld::NpcAttributesRefIdAdapter::NpcAttributesRefIdAdapter ()
{}
void CSMWorld::NpcAttributesRefIdAdapter::addNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int position) const
{
// Do nothing, this table cannot be changed by the user
}
void CSMWorld::NpcAttributesRefIdAdapter::removeNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int rowToRemove) const
{
// Do nothing, this table cannot be changed by the user
}
void CSMWorld::NpcAttributesRefIdAdapter::setNestedTable (const RefIdColumn* column,
RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const
{
Record<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
ESM::NPC npc = record.get();
// store the whole struct
npc.mNpdt52 =
static_cast<const NestedTableWrapper<std::vector<typename ESM::NPC::NPDTstruct52> > &>(nestedTable).mNestedTable.at(0);
record.setModified (npc);
}
CSMWorld::NestedTableWrapperBase* CSMWorld::NpcAttributesRefIdAdapter::nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const
{
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
// return the whole struct
std::vector<typename ESM::NPC::NPDTstruct52> wrap;
wrap.push_back(record.get().mNpdt52);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<typename ESM::NPC::NPDTstruct52> >(wrap);
}
QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const
{
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52;
if (subColIndex == 0)
switch (subRowIndex)
{
case 0: return QString("Strength");
case 1: return QString("Intelligence");
case 2: return QString("Willpower");
case 3: return QString("Agility");
case 4: return QString("Speed");
case 5: return QString("Endurance");
case 6: return QString("Personality");
case 7: return QString("Luck");
default: return QVariant(); // throw an exception here?
}
else if (subColIndex == 1)
switch (subRowIndex)
{
case 0: return static_cast<int>(npcStruct.mStrength);
case 1: return static_cast<int>(npcStruct.mIntelligence);
case 2: return static_cast<int>(npcStruct.mWillpower);
case 3: return static_cast<int>(npcStruct.mAgility);
case 4: return static_cast<int>(npcStruct.mSpeed);
case 5: return static_cast<int>(npcStruct.mEndurance);
case 6: return static_cast<int>(npcStruct.mPersonality);
case 7: return static_cast<int>(npcStruct.mLuck);
default: return QVariant(); // throw an exception here?
}
else
return QVariant(); // throw an exception here?
}
void CSMWorld::NpcAttributesRefIdAdapter::setNestedData (const RefIdColumn *column,
RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const
{
Record<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc)));
ESM::NPC npc = record.get();
ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52;
if (subColIndex == 1)
switch(subRowIndex)
{
case 0: npcStruct.mStrength = static_cast<unsigned char>(value.toInt()); break;
case 1: npcStruct.mIntelligence = static_cast<unsigned char>(value.toInt()); break;
case 2: npcStruct.mWillpower = static_cast<unsigned char>(value.toInt()); break;
case 3: npcStruct.mAgility = static_cast<unsigned char>(value.toInt()); break;
case 4: npcStruct.mSpeed = static_cast<unsigned char>(value.toInt()); break;
case 5: npcStruct.mEndurance = static_cast<unsigned char>(value.toInt()); break;
case 6: npcStruct.mPersonality = static_cast<unsigned char>(value.toInt()); break;
case 7: npcStruct.mLuck = static_cast<unsigned char>(value.toInt()); break;
default: return; // throw an exception here?
}
else
return; // throw an exception here?
record.setModified (npc);
}
int CSMWorld::NpcAttributesRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const
{
return 2;
}
int CSMWorld::NpcAttributesRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const
{
// There are 8 attributes
return 8;
}
CSMWorld::NpcSkillsRefIdAdapter::NpcSkillsRefIdAdapter ()
{}
void CSMWorld::NpcSkillsRefIdAdapter::addNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int position) const
{
// Do nothing, this table cannot be changed by the user
}
void CSMWorld::NpcSkillsRefIdAdapter::removeNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int rowToRemove) const
{
// Do nothing, this table cannot be changed by the user
}
void CSMWorld::NpcSkillsRefIdAdapter::setNestedTable (const RefIdColumn* column,
RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const
{
Record<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
ESM::NPC npc = record.get();
// store the whole struct
npc.mNpdt52 =
static_cast<const NestedTableWrapper<std::vector<typename ESM::NPC::NPDTstruct52> > &>(nestedTable).mNestedTable.at(0);
record.setModified (npc);
}
CSMWorld::NestedTableWrapperBase* CSMWorld::NpcSkillsRefIdAdapter::nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const
{
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
// return the whole struct
std::vector<typename ESM::NPC::NPDTstruct52> wrap;
wrap.push_back(record.get().mNpdt52);
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<typename ESM::NPC::NPDTstruct52> >(wrap);
}
QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const
{
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
const ESM::NPC::NPDTstruct52& npcStruct = record.get().mNpdt52;
if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length)
throw std::runtime_error ("index out of range");
if (subColIndex == 0)
return QString(ESM::Skill::sSkillNames[subRowIndex].c_str());
else if (subColIndex == 1)
return static_cast<int>(npcStruct.mSkills[subRowIndex]);
else
return QVariant(); // throw an exception here?
}
void CSMWorld::NpcSkillsRefIdAdapter::setNestedData (const RefIdColumn *column,
RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const
{
Record<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc)));
ESM::NPC npc = record.get();
ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52;
if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length)
throw std::runtime_error ("index out of range");
if (subColIndex == 1)
npcStruct.mSkills[subRowIndex] = static_cast<unsigned char>(value.toInt());
else
return; // throw an exception here?
record.setModified (npc);
}
int CSMWorld::NpcSkillsRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const
{
return 2;
}
int CSMWorld::NpcSkillsRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const
{
// There are 27 skills
return ESM::Skill::Length;
}
CSMWorld::NpcMiscRefIdAdapter::NpcMiscRefIdAdapter ()
{}
CSMWorld::NpcMiscRefIdAdapter::~NpcMiscRefIdAdapter()
{}
void CSMWorld::NpcMiscRefIdAdapter::addNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int position) const
{
throw std::logic_error ("cannot add a row to a fixed table");
}
void CSMWorld::NpcMiscRefIdAdapter::removeNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int rowToRemove) const
{
throw std::logic_error ("cannot remove a row to a fixed table");
}
void CSMWorld::NpcMiscRefIdAdapter::setNestedTable (const RefIdColumn* column,
RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const
{
throw std::logic_error ("table operation not supported");
}
CSMWorld::NestedTableWrapperBase* CSMWorld::NpcMiscRefIdAdapter::nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const
{
throw std::logic_error ("table operation not supported");
}
QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const
{
const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc)));
bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0;
if (autoCalc)
switch (subColIndex)
{
case 0: return static_cast<int>(record.get().mNpdt12.mLevel);
case 1: return QVariant(QVariant::UserType);
case 2: return QVariant(QVariant::UserType);
case 3: return QVariant(QVariant::UserType);
case 4: return QVariant(QVariant::UserType);
case 5: return static_cast<int>(record.get().mNpdt12.mDisposition);
case 6: return static_cast<int>(record.get().mNpdt12.mReputation);
case 7: return static_cast<int>(record.get().mNpdt12.mRank);
case 8: return record.get().mNpdt12.mGold;
case 9: return record.get().mPersistent == true;
default: return QVariant(); // throw an exception here?
}
else
switch (subColIndex)
{
case 0: return static_cast<int>(record.get().mNpdt52.mLevel);
case 1: return static_cast<int>(record.get().mNpdt52.mFactionID);
case 2: return static_cast<int>(record.get().mNpdt52.mHealth);
case 3: return static_cast<int>(record.get().mNpdt52.mMana);
case 4: return static_cast<int>(record.get().mNpdt52.mFatigue);
case 5: return static_cast<int>(record.get().mNpdt52.mDisposition);
case 6: return static_cast<int>(record.get().mNpdt52.mReputation);
case 7: return static_cast<int>(record.get().mNpdt52.mRank);
case 8: return record.get().mNpdt52.mGold;
case 9: return record.get().mPersistent == true;
default: return QVariant(); // throw an exception here?
}
}
void CSMWorld::NpcMiscRefIdAdapter::setNestedData (const RefIdColumn *column,
RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const
{
Record<ESM::NPC>& record =
static_cast<Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc)));
ESM::NPC npc = record.get();
bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0;
if (autoCalc)
switch(subColIndex)
{
case 0: npc.mNpdt12.mLevel = static_cast<short>(value.toInt()); break;
case 1: return;
case 2: return;
case 3: return;
case 4: return;
case 5: npc.mNpdt12.mDisposition = static_cast<signed char>(value.toInt()); break;
case 6: npc.mNpdt12.mReputation = static_cast<signed char>(value.toInt()); break;
case 7: npc.mNpdt12.mRank = static_cast<signed char>(value.toInt()); break;
case 8: npc.mNpdt12.mGold = value.toInt(); break;
case 9: npc.mPersistent = value.toBool(); break;
default: return; // throw an exception here?
}
else
switch(subColIndex)
{
case 0: npc.mNpdt52.mLevel = static_cast<short>(value.toInt()); break;
case 1: npc.mNpdt52.mFactionID = static_cast<char>(value.toInt()); break;
case 2: npc.mNpdt52.mHealth = static_cast<unsigned short>(value.toInt()); break;
case 3: npc.mNpdt52.mMana = static_cast<unsigned short>(value.toInt()); break;
case 4: npc.mNpdt52.mFatigue = static_cast<unsigned short>(value.toInt()); break;
case 5: npc.mNpdt52.mDisposition = static_cast<signed char>(value.toInt()); break;
case 6: npc.mNpdt52.mReputation = static_cast<signed char>(value.toInt()); break;
case 7: npc.mNpdt52.mRank = static_cast<signed char>(value.toInt()); break;
case 8: npc.mNpdt52.mGold = value.toInt(); break;
case 9: npc.mPersistent = value.toBool(); break;
default: return; // throw an exception here?
}
record.setModified (npc);
}
int CSMWorld::NpcMiscRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const
{
return 10; // Level, FactionID, Health, Mana, Fatigue, Disposition, Reputation, Rank, Gold, Persist
}
int CSMWorld::NpcMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const
{
return 1; // fixed at size 1
}
CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns) CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns)
: EnchantableColumns (columns) {} : EnchantableColumns (columns) {}

View file

@ -2,6 +2,7 @@
#define CSM_WOLRD_REFIDADAPTERIMP_H #define CSM_WOLRD_REFIDADAPTERIMP_H
#include <map> #include <map>
#include <iostream>
#include <QVariant> #include <QVariant>
@ -791,6 +792,9 @@ namespace CSMWorld
const RefIdColumn *mFaction; const RefIdColumn *mFaction;
const RefIdColumn *mHair; const RefIdColumn *mHair;
const RefIdColumn *mHead; const RefIdColumn *mHead;
const RefIdColumn *mAttributes; // depends on npc type
const RefIdColumn *mSkills; // depends on npc type
const RefIdColumn *mMisc; // may depend on npc type, e.g. FactionID
NpcColumns (const ActorColumns& actorColumns); NpcColumns (const ActorColumns& actorColumns);
}; };
@ -841,8 +845,100 @@ namespace CSMWorld
///< If the data type does not match an exception is thrown. ///< If the data type does not match an exception is thrown.
}; };
class NestedRefIdAdapterBase; class NestedRefIdAdapterBase;
class NpcAttributesRefIdAdapter : public NestedRefIdAdapterBase
{
public:
NpcAttributesRefIdAdapter ();
virtual void addNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int position) const;
virtual void removeNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int rowToRemove) const;
virtual void setNestedTable (const RefIdColumn* column,
RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const;
virtual QVariant getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const;
virtual void setNestedData (const RefIdColumn *column,
RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const;
virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const;
};
class NpcSkillsRefIdAdapter : public NestedRefIdAdapterBase
{
public:
NpcSkillsRefIdAdapter ();
virtual void addNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int position) const;
virtual void removeNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int rowToRemove) const;
virtual void setNestedTable (const RefIdColumn* column,
RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const;
virtual QVariant getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const;
virtual void setNestedData (const RefIdColumn *column,
RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const;
virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const;
};
class NpcMiscRefIdAdapter : public NestedRefIdAdapterBase
{
NpcMiscRefIdAdapter (const NpcMiscRefIdAdapter&);
NpcMiscRefIdAdapter& operator= (const NpcMiscRefIdAdapter&);
public:
NpcMiscRefIdAdapter ();
virtual ~NpcMiscRefIdAdapter();
virtual void addNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int position) const;
virtual void removeNestedRow (const RefIdColumn *column,
RefIdData& data, int index, int rowToRemove) const;
virtual void setNestedTable (const RefIdColumn* column,
RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const;
virtual NestedTableWrapperBase* nestedTable (const RefIdColumn* column,
const RefIdData& data, int index) const;
virtual QVariant getNestedData (const RefIdColumn *column,
const RefIdData& data, int index, int subRowIndex, int subColIndex) const;
virtual void setNestedData (const RefIdColumn *column,
RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const;
virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const;
virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const;
};
template<typename ESXRecordT> template<typename ESXRecordT>
class EffectsListAdapter; class EffectsListAdapter;
@ -1829,10 +1925,10 @@ namespace CSMWorld
} }
// for non-tables
template <typename ESXRecordT> template <typename ESXRecordT>
class NestedListLevListRefIdAdapter : public NestedRefIdAdapterBase class NestedListLevListRefIdAdapter : public NestedRefIdAdapterBase
{ {
UniversalId::Type mType; UniversalId::Type mType;
// not implemented // not implemented
@ -1876,31 +1972,27 @@ namespace CSMWorld
const Record<ESXRecordT>& record = const Record<ESXRecordT>& record =
static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType))); static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
if (mType == UniversalId::Type_CreatureLevelledList)
{
switch (subColIndex) switch (subColIndex)
{ {
case 0: case 0: return QVariant(); // disable the checkbox editor
{ case 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels;
if (mType == CSMWorld::UniversalId::Type_CreatureLevelledList && case 2: return static_cast<int> (record.get().mChanceNone);
record.get().mFlags == 0x01) default:
{ throw std::runtime_error("Trying to access non-existing column in levelled creatues!");
return QString("All Levels");
} }
else if(mType == CSMWorld::UniversalId::Type_ItemLevelledList &&
record.get().mFlags == 0x01)
{
return QString("Each");
}
else if(mType == CSMWorld::UniversalId::Type_ItemLevelledList &&
record.get().mFlags == 0x02)
{
return QString("All Levels");
} }
else else
throw std::runtime_error("unknown leveled list type"); {
} switch (subColIndex)
case 1: return static_cast<int> (record.get().mChanceNone); {
case 0: return record.get().mFlags & ESM::ItemLevList::Each;
case 1: return record.get().mFlags & ESM::ItemLevList::AllLevels;
case 2: return static_cast<int> (record.get().mChanceNone);
default: default:
throw std::runtime_error("Trying to access non-existing column in the nested table!"); throw std::runtime_error("Trying to access non-existing column in levelled items!");
}
} }
} }
@ -1911,34 +2003,63 @@ namespace CSMWorld
static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType))); static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));
ESXRecordT leveled = record.get(); ESXRecordT leveled = record.get();
if (mType == UniversalId::Type_CreatureLevelledList)
{
switch(subColIndex)
{
case 0: return; // return without saving
case 1:
{
if(value.toBool())
{
leveled.mFlags |= ESM::CreatureLevList::AllLevels;
break;
}
else
{
leveled.mFlags &= ~ESM::CreatureLevList::AllLevels;
break;
}
}
case 2: leveled.mChanceNone = static_cast<unsigned char>(value.toInt()); break;
default:
throw std::runtime_error("Trying to set non-existing column in levelled creatures!");
}
}
else
{
switch(subColIndex) switch(subColIndex)
{ {
case 0: case 0:
{ {
if (mType == CSMWorld::UniversalId::Type_CreatureLevelledList && if(value.toBool())
value.toString().toStdString() == "All Levels")
{ {
leveled.mFlags = 0x01; leveled.mFlags |= ESM::ItemLevList::Each;
break;
}
else if(mType == CSMWorld::UniversalId::Type_ItemLevelledList &&
value.toString().toStdString() == "Each")
{
leveled.mFlags = 0x01;
break;
}
else if(mType == CSMWorld::UniversalId::Type_ItemLevelledList &&
value.toString().toStdString() == "All Levels")
{
leveled.mFlags = 0x02;
break; break;
} }
else else
return; // return without saving {
leveled.mFlags &= ~ESM::ItemLevList::Each;
break;
} }
case 1: leveled.mChanceNone = static_cast<unsigned char>(value.toInt()); break; }
case 1:
{
if(value.toBool())
{
leveled.mFlags |= ESM::ItemLevList::AllLevels;
break;
}
else
{
leveled.mFlags &= ~ESM::ItemLevList::AllLevels;
break;
}
}
case 2: leveled.mChanceNone = static_cast<unsigned char>(value.toInt()); break;
default: default:
throw std::runtime_error("Trying to access non-existing column in the nested table!"); throw std::runtime_error("Trying to set non-existing column in levelled items!");
}
} }
record.setModified (leveled); record.setModified (leveled);
@ -1946,7 +2067,7 @@ namespace CSMWorld
virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const virtual int getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const
{ {
return 2; return 3;
} }
virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const virtual int getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const
@ -1955,6 +2076,7 @@ namespace CSMWorld
} }
}; };
// for tables
template <typename ESXRecordT> template <typename ESXRecordT>
class NestedLevListRefIdAdapter : public NestedRefIdAdapterBase class NestedLevListRefIdAdapter : public NestedRefIdAdapterBase
{ {

View file

@ -245,7 +245,8 @@ CSMWorld::RefIdCollection::RefIdCollection()
actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag)); actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag));
} }
mColumns.push_back (RefIdColumn (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean)); mColumns.push_back (RefIdColumn (Columns::ColumnId_AutoCalc, ColumnBase::Display_Boolean,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));
const RefIdColumn *autoCalc = &mColumns.back(); const RefIdColumn *autoCalc = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType, mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType,
@ -427,6 +428,62 @@ CSMWorld::RefIdCollection::RefIdCollection()
npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal)); npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal));
// Need a way to add a table of stats and values (rather than adding a long list of
// entries in the dialogue subview) E.g. attributes+stats(health, mana, fatigue), skills
// These needs to be driven from the autocalculated setting.
// Nested table
mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcAttributes,
ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue));
npcColumns.mAttributes = &mColumns.back();
std::map<UniversalId::Type, NestedRefIdAdapterBase*> attrMap;
attrMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcAttributesRefIdAdapter()));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), attrMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcAttributes, CSMWorld::ColumnBase::Display_String, false, false));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer));
// Nested table
mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcSkills,
ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue));
npcColumns.mSkills = &mColumns.back();
std::map<UniversalId::Type, NestedRefIdAdapterBase*> skillsMap;
skillsMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcSkillsRefIdAdapter()));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), skillsMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcSkills, CSMWorld::ColumnBase::Display_String, false, false));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer));
// Nested list
mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcMisc,
ColumnBase::Display_NestedHeader, ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List));
npcColumns.mMisc = &mColumns.back();
std::map<UniversalId::Type, NestedRefIdAdapterBase*> miscMap;
miscMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcMiscRefIdAdapter()));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), miscMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcLevel, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcFactionID, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcHealth, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcMana, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcFatigue, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcDisposition, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcReputation, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcRank, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcGold, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcPersistence, CSMWorld::ColumnBase::Display_Boolean));
WeaponColumns weaponColumns (enchantableColumns); WeaponColumns weaponColumns (enchantableColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType)); mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType));
@ -514,7 +571,9 @@ CSMWorld::RefIdCollection::RefIdCollection()
new NestedListLevListRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList))); new NestedListLevListRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList)));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), nestedListLevListMap)); mNestedAdapters.push_back (std::make_pair(&mColumns.back(), nestedListLevListMap));
mColumns.back().addColumn( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_LevelledItemType, CSMWorld::ColumnBase::Display_String)); new RefIdColumn (Columns::ColumnId_LevelledItemTypeEach, CSMWorld::ColumnBase::Display_Boolean));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_LevelledItemType, CSMWorld::ColumnBase::Display_Boolean));
mColumns.back().addColumn( mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_LevelledItemChanceNone, CSMWorld::ColumnBase::Display_Integer)); new RefIdColumn (Columns::ColumnId_LevelledItemChanceNone, CSMWorld::ColumnBase::Display_Integer));

View file

@ -176,7 +176,6 @@ void CSMWorld::RefIdData::erase (const LocalIndex& index, int count)
{ {
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter = std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (index.second); mRecordContainers.find (index.second);
if (iter==mRecordContainers.end()) if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type"); throw std::logic_error ("invalid local index type");
@ -189,6 +188,20 @@ void CSMWorld::RefIdData::erase (const LocalIndex& index, int count)
mIndex.erase (result); mIndex.erase (result);
} }
// Adjust the local indexes to avoid gaps between them after removal of records
int recordIndex = index.first + count;
int recordCount = iter->second->getSize();
while (recordIndex < recordCount)
{
std::map<std::string, LocalIndex>::iterator recordIndexFound =
mIndex.find(Misc::StringUtils::lowerCase(iter->second->getId(recordIndex)));
if (recordIndexFound != mIndex.end())
{
recordIndexFound->second.first -= count;
}
++recordIndex;
}
iter->second->erase (index.first, count); iter->second->erase (index.first, count);
} }

View file

@ -130,7 +130,7 @@ namespace CSMWorld
template<typename RecordT> template<typename RecordT>
void RefIdDataContainer<RecordT>::erase (int index, int count) void RefIdDataContainer<RecordT>::erase (int index, int count)
{ {
if (index<0 || index+count>=getSize()) if (index<0 || index+count>getSize())
throw std::runtime_error ("invalid RefIdDataContainer index"); throw std::runtime_error ("invalid RefIdDataContainer index");
mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count); mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count);

View file

@ -38,9 +38,9 @@ namespace
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
"Referenceables", 0 }, "Objects", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References,
"References", 0 }, "Instances", 0 },
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap,
"Region Map", 0 }, "Region Map", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 },
@ -79,7 +79,7 @@ namespace
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", 0 },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" },
@ -103,7 +103,7 @@ namespace
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, { 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_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_Reference, "Instance", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 },

View file

@ -18,7 +18,7 @@
#include "adjusterwidget.hpp" #include "adjusterwidget.hpp"
CSVDoc::FileDialog::FileDialog(QWidget *parent) : CSVDoc::FileDialog::FileDialog(QWidget *parent) :
QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0), mDialogBuilt(false), mAction(ContentAction_Undefined) QDialog(parent), mSelector (0), mAction(ContentAction_Undefined), mFileWidget (0), mAdjusterWidget (0), mDialogBuilt(false)
{ {
ui.setupUi (this); ui.setupUi (this);
resize(400, 400); resize(400, 400);

View file

@ -66,3 +66,9 @@ void CSVDoc::NewGameDialogue::create()
{ {
emit createRequest (mAdjusterWidget->getPath()); emit createRequest (mAdjusterWidget->getPath());
} }
void CSVDoc::NewGameDialogue::reject()
{
emit cancelCreateGame ();
QDialog::reject();
}

View file

@ -36,11 +36,15 @@ namespace CSVDoc
void createRequest (const boost::filesystem::path& file); void createRequest (const boost::filesystem::path& file);
void cancelCreateGame ();
private slots: private slots:
void stateChanged (bool valid); void stateChanged (bool valid);
void create(); void create();
void reject();
}; };
} }

View file

@ -0,0 +1,17 @@
#include "sizehint.hpp"
CSVDoc::SizeHintWidget::SizeHintWidget(QWidget *parent) : QWidget(parent)
{}
CSVDoc::SizeHintWidget::~SizeHintWidget()
{}
QSize CSVDoc::SizeHintWidget::sizeHint() const
{
return mSize;
}
void CSVDoc::SizeHintWidget::setSizeHint(const QSize &size)
{
mSize = size;
}

View file

@ -0,0 +1,22 @@
#ifndef CSV_DOC_SIZEHINT_H
#define CSV_DOC_SIZEHINT_H
#include <QWidget>
#include <QSize>
namespace CSVDoc
{
class SizeHintWidget : public QWidget
{
QSize mSize;
public:
SizeHintWidget(QWidget *parent = 0);
~SizeHintWidget();
virtual QSize sizeHint() const;
void setSizeHint(const QSize &size);
};
}
#endif // CSV_DOC_SIZEHINT_H

View file

@ -2,6 +2,24 @@
#include "view.hpp" #include "view.hpp"
#include <QShortcut>
#include <QEvent>
#include <QKeyEvent>
bool CSVDoc::SubView::event (QEvent *event)
{
if (event->type()==QEvent::ShortcutOverride)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *> (event);
if (keyEvent->key()==Qt::Key_W && keyEvent->modifiers()==(Qt::ShiftModifier | Qt::ControlModifier))
emit closeRequest();
return true;
}
return QDockWidget::event (event);
}
CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id)
: mUniversalId (id) : mUniversalId (id)
{ {

View file

@ -34,6 +34,8 @@ namespace CSVDoc
void setUniversalId(const CSMWorld::UniversalId& id); void setUniversalId(const CSMWorld::UniversalId& id);
bool event (QEvent *event);
public: public:
SubView (const CSMWorld::UniversalId& id); SubView (const CSMWorld::UniversalId& id);

View file

@ -9,6 +9,10 @@
#include <QDockWidget> #include <QDockWidget>
#include <QtGui/QApplication> #include <QtGui/QApplication>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QScrollArea>
#include <QHBoxLayout>
#include <QDesktopWidget>
#include <QScrollBar>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/settings/usersettings.hpp" #include "../../model/settings/usersettings.hpp"
@ -16,6 +20,7 @@
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../world/subviews.hpp" #include "../world/subviews.hpp"
#include "../world/tablesubview.hpp"
#include "../tools/subviews.hpp" #include "../tools/subviews.hpp"
@ -131,11 +136,11 @@ void CSVDoc::View::setupWorldMenu()
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
world->addAction (cells); world->addAction (cells);
QAction *referenceables = new QAction (tr ("Referenceables"), this); QAction *referenceables = new QAction (tr ("Objects"), this);
connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView())); connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView()));
world->addAction (referenceables); world->addAction (referenceables);
QAction *references = new QAction (tr ("References"), this); QAction *references = new QAction (tr ("Instances"), this);
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
world->addAction (references); world->addAction (references);
@ -334,8 +339,15 @@ void CSVDoc::View::updateTitle()
void CSVDoc::View::updateSubViewIndicies(SubView *view) void CSVDoc::View::updateSubViewIndicies(SubView *view)
{ {
if(view && mSubViews.contains(view)) if(view && mSubViews.contains(view))
{
mSubViews.removeOne(view); mSubViews.removeOne(view);
// adjust (reduce) the scroll area (even floating), except when it is "Scrollbar Only"
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
if(settings.settingValue ("window/mainwindow-scrollbar") == "Grow then Scroll")
updateScrollbar();
}
CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" && bool hideTitle = userSettings.setting ("window/hide-subview", QString ("false"))=="true" &&
@ -381,7 +393,7 @@ void CSVDoc::View::updateActions()
CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews)
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),
mViewTotal (totalViews) mViewTotal (totalViews), mScroll(0), mScrollbarOnly(false)
{ {
int width = CSMSettings::UserSettings::instance().settingValue int width = CSMSettings::UserSettings::instance().settingValue
("window/default-width").toInt(); ("window/default-width").toInt();
@ -392,15 +404,22 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
width = std::max(width, 300); width = std::max(width, 300);
height = std::max(height, 300); height = std::max(height, 300);
// trick to get the window decorations and their sizes resize (width, height);
show();
hide();
resize (width - (frameGeometry().width() - geometry().width()),
height - (frameGeometry().height() - geometry().height()));
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
if(settings.settingValue ("window/mainwindow-scrollbar") == "Grow Only")
{
setCentralWidget (&mSubViewWindow); setCentralWidget (&mSubViewWindow);
}
else
{
mScroll = new QScrollArea(this);
mScroll->setWidgetResizable(true);
mScroll->setWidget(&mSubViewWindow);
setCentralWidget(mScroll);
}
mOperations = new Operations; mOperations = new Operations;
addDockWidget (Qt::BottomDockWidgetArea, mOperations); addDockWidget (Qt::BottomDockWidgetArea, mOperations);
@ -527,6 +546,54 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
view->setStatusBar (mShowStatusBar->isChecked()); view->setStatusBar (mShowStatusBar->isChecked());
// Work out how to deal with additional subviews
//
// Policy for "Grow then Scroll":
//
// - Increase the horizontal width of the mainwindow until it becomes greater than or equal
// to the screen (monitor) width.
// - Move the mainwindow position sideways if necessary to fit within the screen.
// - Any more additions increases the size of the mSubViewWindow (horizontal scrollbar
// should become visible)
// - Move the scroll bar to the newly added subview
//
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
QString mainwinScroll = settings.settingValue ("window/mainwindow-scrollbar");
mScrollbarOnly = mainwinScroll.isEmpty() || mainwinScroll == "Scrollbar Only";
QDesktopWidget *dw = QApplication::desktop();
QRect rect;
if(settings.settingValue ("window/grow-limit") == "true")
rect = dw->screenGeometry(this);
else
rect = dw->screenGeometry(dw->screen(dw->screenNumber(this)));
if (!mScrollbarOnly && mScroll && mSubViews.size() > 1)
{
int newWidth = width()+minWidth;
int frameWidth = frameGeometry().width() - width();
if (newWidth+frameWidth <= rect.width())
{
resize(newWidth, height());
// WARNING: below code assumes that new subviews are added to the right
if (x() > rect.width()-(newWidth+frameWidth))
move(rect.width()-(newWidth+frameWidth), y()); // shift left to stay within the screen
}
else
{
// full width
resize(rect.width()-frameWidth, height());
mSubViewWindow.setMinimumWidth(mSubViewWindow.width()+minWidth);
move(0, y());
}
// Make the new subview visible, setFocus() or raise() don't seem to work
// On Ubuntu the scrollbar does not go right to the end, even if using
// mScroll->horizontalScrollBar()->setValue(mScroll->horizontalScrollBar()->maximum());
if (mSubViewWindow.width() > rect.width())
mScroll->horizontalScrollBar()->setValue(mSubViewWindow.width());
}
mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);
updateSubViewIndicies(); updateSubViewIndicies();
@ -774,6 +841,48 @@ void CSVDoc::View::updateUserSetting (const QString &name, const QStringList &li
{ {
subView->updateUserSetting (name, list); subView->updateUserSetting (name, list);
} }
if (name=="window/mainwindow-scrollbar")
{
if(list.at(0) != "Grow Only")
{
if (mScroll)
{
if (list.at(0).isEmpty() || list.at(0) == "Scrollbar Only")
{
mScrollbarOnly = true;
mSubViewWindow.setMinimumWidth(0);
}
else
{
if(!mScrollbarOnly)
return;
mScrollbarOnly = false;
updateScrollbar();
}
}
else
{
mScroll = new QScrollArea(this);
mScroll->setWidgetResizable(true);
mScroll->setWidget(&mSubViewWindow);
setCentralWidget(mScroll);
}
}
else
{
if (mScroll)
{
mScroll->takeWidget();
setCentralWidget (&mSubViewWindow);
mScroll->deleteLater();
mScroll = 0;
}
else
return;
}
}
} }
void CSVDoc::View::toggleShowStatusBar (bool show) void CSVDoc::View::toggleShowStatusBar (bool show)
@ -811,7 +920,33 @@ void CSVDoc::View::closeRequest (SubView *subView)
if (mSubViews.size()>1 || mViewTotal<=1 || if (mSubViews.size()>1 || mViewTotal<=1 ||
userSettings.setting ("window/hide-subview", QString ("false"))!="true") userSettings.setting ("window/hide-subview", QString ("false"))!="true")
{
subView->deleteLater(); subView->deleteLater();
mSubViews.removeOne (subView);
}
else if (mViewManager.closeRequest (this)) else if (mViewManager.closeRequest (this))
mViewManager.removeDocAndView (mDocument); mViewManager.removeDocAndView (mDocument);
} }
void CSVDoc::View::updateScrollbar()
{
QRect rect;
QWidget *topLevel = QApplication::topLevelAt(pos());
if (topLevel)
rect = topLevel->rect();
else
rect = this->rect();
int newWidth = 0;
for (int i = 0; i < mSubViews.size(); ++i)
{
newWidth += mSubViews[i]->width();
}
int frameWidth = frameGeometry().width() - width();
if ((newWidth+frameWidth) >= rect.width())
mSubViewWindow.setMinimumWidth(newWidth);
else
mSubViewWindow.setMinimumWidth(0);
}

View file

@ -10,6 +10,7 @@
class QAction; class QAction;
class QDockWidget; class QDockWidget;
class QScrollArea;
namespace CSMDoc namespace CSMDoc
{ {
@ -47,6 +48,8 @@ namespace CSVDoc
SubViewFactoryManager mSubViewFactory; SubViewFactoryManager mSubViewFactory;
QMainWindow mSubViewWindow; QMainWindow mSubViewWindow;
GlobalDebugProfileMenu *mGlobalDebugProfileMenu; GlobalDebugProfileMenu *mGlobalDebugProfileMenu;
QScrollArea *mScroll;
bool mScrollbarOnly;
// not implemented // not implemented
@ -87,6 +90,8 @@ namespace CSVDoc
/// User preference function /// User preference function
void resizeViewHeight (int height); void resizeViewHeight (int height);
void updateScrollbar();
public: public:
View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews);

View file

@ -90,7 +90,10 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false }, { CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false },
{ CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, false }, { CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, false },
{ CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false }, { CSMWorld::ColumnBase::Display_AiPackageType, CSMWorld::Columns::ColumnId_AiPackageType, false },
{ CSMWorld::ColumnBase::Display_YesNo, CSMWorld::Columns::ColumnId_AiWanderRepeat, false } { CSMWorld::ColumnBase::Display_YesNo, CSMWorld::Columns::ColumnId_AiWanderRepeat, false },
{ CSMWorld::ColumnBase::Display_InfoCondFunc, CSMWorld::Columns::ColumnId_InfoCondFunc, false },
{ CSMWorld::ColumnBase::Display_InfoCondComp, CSMWorld::Columns::ColumnId_InfoCondComp, false },
{ CSMWorld::ColumnBase::Display_RaceSkill, CSMWorld::Columns::ColumnId_RaceSkill, true },
}; };
for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i) for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)

View file

@ -120,10 +120,9 @@ bool CSVRender::Cell::addObjects (int start, int end)
CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager, CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager,
const std::string& id, boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin) const std::string& id, boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin)
: mDocument (document), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager) : mDocument (document), mId (Misc::StringUtils::lowerCase (id))
, mPhysics(physics), mX(0), mY(0), mPgIndex(-1), mModel(0), mProxyModel(0) , mProxyModel(0), mModel(0), mPgIndex(-1), mHandler(new CSMWorld::SignalHandler(this))
, mHandler(new CSMWorld::SignalHandler(this)) , mPhysics(physics), mSceneMgr(sceneManager), mX(0), mY(0){
{
mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode(); mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
mCellNode->setPosition (origin); mCellNode->setPosition (origin);

View file

@ -56,12 +56,11 @@ namespace CSVRender
// //
MouseState::MouseState(WorldspaceWidget *parent) MouseState::MouseState(WorldspaceWidget *parent)
: mParent(parent), mPhysics(parent->mDocument.getPhysics()), mSceneManager(parent->getSceneManager()) : mMouseState(Mouse_Default), mParent(parent), mPhysics(parent->mDocument.getPhysics())
, mCurrentObj(""), mMouseState(Mouse_Default), mOldCursorPos(0,0), mMouseEventTimer(0) , mSceneManager(parent->getSceneManager()), mOldCursorPos(0,0), mCurrentObj(""), mGrabbedSceneNode(""), mGrabbedRefId("")
, mGrabbedSceneNode(""), mGrabbedRefId(""), mOrigObjPos(Ogre::Vector3()) , mMouseEventTimer(0), mPlane(0), mOrigObjPos(Ogre::Vector3()), mOrigMousePos(Ogre::Vector3())
, mOrigMousePos(Ogre::Vector3()), mOldMousePos(Ogre::Vector3()), mPlane(0) , mOldMousePos(Ogre::Vector3()), mIdTableModel(0), mColIndexPosX(0)
, mColIndexPosX(0), mColIndexPosY(0), mColIndexPosZ(0), mIdTableModel(0) , mColIndexPosY(0), mColIndexPosZ(0) {
{
const CSMWorld::RefCollection& references = mParent->mDocument.getData().getReferences(); const CSMWorld::RefCollection& references = mParent->mDocument.getData().getReferences();
mColIndexPosX = references.findColumnIndex(CSMWorld::Columns::ColumnId_PositionXPos); mColIndexPosX = references.findColumnIndex(CSMWorld::Columns::ColumnId_PositionXPos);

View file

@ -27,8 +27,8 @@ namespace CSVRender
// http://www.ogre3d.org/tikiwiki/Creating+dynamic+textures // http://www.ogre3d.org/tikiwiki/Creating+dynamic+textures
// http://www.ogre3d.org/tikiwiki/ManualObject // http://www.ogre3d.org/tikiwiki/ManualObject
TextOverlay::TextOverlay(const Ogre::MovableObject* obj, const Ogre::Camera* camera, const Ogre::String& id) TextOverlay::TextOverlay(const Ogre::MovableObject* obj, const Ogre::Camera* camera, const Ogre::String& id)
: mOverlay(0), mCaption(""), mDesc(""), mEnabled(true), mCamera(camera), mObj(obj), mId(id) : mOverlay(0), mCaption(""), mDesc(""), mObj(obj), mCamera(camera), mFontHeight(16), mId(id)
, mOnScreen(false) , mInstance(0), mFontHeight(16) // FIXME: make font height configurable , mEnabled(true), mOnScreen(false), mInstance(0) // FIXME: make font height configurable
{ {
if(id == "" || !camera || !obj) if(id == "" || !camera || !obj)
throw std::runtime_error("TextOverlay could not be created."); throw std::runtime_error("TextOverlay could not be created.");

View file

@ -23,7 +23,7 @@
#include "editmode.hpp" #include "editmode.hpp"
CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent) CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent)
: SceneWidget (parent), mDocument(document), mSceneElements(0), mRun(0), mPhysics(boost::shared_ptr<CSVWorld::PhysicsSystem>()), mMouse(0), : SceneWidget (parent), mSceneElements(0), mRun(0), mDocument(document), mPhysics(boost::shared_ptr<CSVWorld::PhysicsSystem>()), mMouse(0),
mInteractionMask (0) mInteractionMask (0)
{ {
setAcceptDrops(true); setAcceptDrops(true);
@ -265,7 +265,7 @@ unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const
void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons ( void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons (
CSVWidget::SceneToolToggle2 *tool) CSVWidget::SceneToolToggle2 *tool)
{ {
tool->addButton (Element_Reference, "References"); tool->addButton (Element_Reference, "Instances");
tool->addButton (Element_Water, "Water"); tool->addButton (Element_Water, "Water");
tool->addButton (Element_Pathgrid, "Pathgrid"); tool->addButton (Element_Pathgrid, "Pathgrid");
} }
@ -274,7 +274,7 @@ void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneTo
{ {
/// \todo replace EditMode with suitable subclasses /// \todo replace EditMode with suitable subclasses
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Element_Reference, "Reference editing"), new EditMode (this, QIcon (":placeholder"), Element_Reference, "Instance editing"),
"object"); "object");
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"), new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"),

View file

@ -12,7 +12,7 @@
CSVSettings::BooleanView::BooleanView (CSMSettings::Setting *setting, CSVSettings::BooleanView::BooleanView (CSMSettings::Setting *setting,
Page *parent) Page *parent)
: mType(setting->type()), View (setting, parent) : View (setting, parent), mType(setting->type())
{ {
foreach (const QString &value, setting->declaredValues()) foreach (const QString &value, setting->declaredValues())
{ {

View file

@ -23,7 +23,7 @@
#include <QStandardItem> #include <QStandardItem>
CSVSettings::Dialog::Dialog(QMainWindow *parent) CSVSettings::Dialog::Dialog(QMainWindow *parent)
: mStackedWidget (0), mDebugMode (false), SettingWindow (parent) : SettingWindow (parent), mStackedWidget (0), mDebugMode (false)
{ {
setWindowTitle(QString::fromUtf8 ("User Settings")); setWindowTitle(QString::fromUtf8 ("User Settings"));

View file

@ -7,8 +7,8 @@ const QString CSVSettings::Frame::sInvisibleBoxStyle =
CSVSettings::Frame::Frame (bool isVisible, const QString &title, CSVSettings::Frame::Frame (bool isVisible, const QString &title,
QWidget *parent) QWidget *parent)
: mIsHorizontal (true), mLayout (new SettingLayout()), : QGroupBox (title, parent), mIsHorizontal (true),
QGroupBox (title, parent) mLayout (new SettingLayout())
{ {
setFlat (true); setFlat (true);
mVisibleBoxStyle = styleSheet(); mVisibleBoxStyle = styleSheet();

View file

@ -7,7 +7,7 @@
CSVSettings::ListView::ListView(CSMSettings::Setting *setting, CSVSettings::ListView::ListView(CSMSettings::Setting *setting,
Page *parent) Page *parent)
: mComboBox (0), mAbstractItemView (0), View(setting, parent) : View(setting, parent), mAbstractItemView (0), mComboBox (0)
{ {
QWidget *widget = QWidget *widget =
buildWidget(setting->isMultiLine(), setting->widgetWidth()); buildWidget(setting->isMultiLine(), setting->widgetWidth());

View file

@ -19,7 +19,7 @@ QMap <CSVSettings::ViewType, CSVSettings::IViewFactory *>
CSVSettings::Page::Page (const QString &pageName, QList <CSMSettings::Setting *> settingList, CSVSettings::Page::Page (const QString &pageName, QList <CSMSettings::Setting *> settingList,
SettingWindow *parent, const QString& label) SettingWindow *parent, const QString& label)
: mParent(parent), mIsEditorPage (false), Frame(false, "", parent), mLabel (label) : Frame(false, "", parent), mParent(parent), mIsEditorPage (false), mLabel (label)
{ {
setObjectName (pageName); setObjectName (pageName);

View file

@ -12,7 +12,7 @@
CSVSettings::RangeView::RangeView (CSMSettings::Setting *setting, CSVSettings::RangeView::RangeView (CSMSettings::Setting *setting,
Page *parent) Page *parent)
: mRangeWidget (0), mRangeType (setting->type()), View (setting, parent) : View (setting, parent), mRangeWidget (0), mRangeType (setting->type())
{ {
mRangeWidget = 0; mRangeWidget = 0;

View file

@ -3,7 +3,7 @@
#include <QLineEdit> #include <QLineEdit>
CSVSettings::SpinBox::SpinBox(QWidget *parent) CSVSettings::SpinBox::SpinBox(QWidget *parent)
: mValueList(QStringList()), QSpinBox(parent) : QSpinBox(parent), mValueList(QStringList())
{ {
setRange (0, 0); setRange (0, 0);
} }

View file

@ -5,7 +5,7 @@
#include "../../model/settings/setting.hpp" #include "../../model/settings/setting.hpp"
CSVSettings::TextView::TextView(CSMSettings::Setting *setting, Page *parent) CSVSettings::TextView::TextView(CSMSettings::Setting *setting, Page *parent)
: mDelimiter (setting->delimiter()), View (setting, parent) : View (setting, parent), mDelimiter (setting->delimiter())
{ {
if (setting->isMultiLine()) if (setting->isMultiLine())

View file

@ -12,12 +12,12 @@
CSVSettings::View::View(CSMSettings::Setting *setting, CSVSettings::View::View(CSMSettings::Setting *setting,
Page *parent) Page *parent)
: mDataModel(0), mParentPage (parent), : Frame(true, setting->getLabel(), parent),
mParentPage (parent), mDataModel(0),
mHasFixedValues (!setting->declaredValues().isEmpty()), mHasFixedValues (!setting->declaredValues().isEmpty()),
mIsMultiValue (setting->isMultiValue()), mIsMultiValue (setting->isMultiValue()),
mViewKey (setting->page() + '/' + setting->name()), mViewKey (setting->page() + '/' + setting->name()),
mSerializable (setting->serializable()), mSerializable (setting->serializable())
Frame(true, setting->getLabel(), parent)
{ {
if (!setting->getToolTip().isEmpty()) if (!setting->getToolTip().isEmpty())
setToolTip (setting->getToolTip()); setToolTip (setting->getToolTip());

View file

@ -131,8 +131,8 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
setModel (mModel); setModel (mModel);
setColumnHidden (2, true); setColumnHidden (2, true);
mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate ( mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0,
document, this); mDocument, this);
setItemDelegateForColumn (0, mIdTypeDelegate); setItemDelegateForColumn (0, mIdTypeDelegate);

View file

@ -153,6 +153,11 @@ void CSVTools::SearchBox::setEditLock (bool locked)
mReplace.setEnabled (!locked); mReplace.setEnabled (!locked);
} }
void CSVTools::SearchBox::focus()
{
mInput.currentWidget()->setFocus();
}
void CSVTools::SearchBox::modeSelected (int index) void CSVTools::SearchBox::modeSelected (int index)
{ {
switch (index) switch (index)
@ -172,6 +177,8 @@ void CSVTools::SearchBox::modeSelected (int index)
break; break;
} }
mInput.currentWidget()->setFocus();
updateSearchButton(); updateSearchButton();
} }

View file

@ -49,6 +49,8 @@ namespace CSVTools
void setEditLock (bool locked); void setEditLock (bool locked);
void focus();
private slots: private slots:
void modeSelected (int index); void modeSelected (int index);

View file

@ -7,6 +7,7 @@
#include "../../model/tools/search.hpp" #include "../../model/tools/search.hpp"
#include "../../model/tools/reportmodel.hpp" #include "../../model/tools/reportmodel.hpp"
#include "../../model/world/idtablebase.hpp" #include "../../model/world/idtablebase.hpp"
#include "../../model/settings/usersettings.hpp"
#include "reporttable.hpp" #include "reporttable.hpp"
#include "searchbox.hpp" #include "searchbox.hpp"
@ -23,6 +24,12 @@ void CSVTools::SearchSubView::replace (bool selection)
const CSMTools::ReportModel& model = const CSMTools::ReportModel& model =
dynamic_cast<const CSMTools::ReportModel&> (*mTable->model()); dynamic_cast<const CSMTools::ReportModel&> (*mTable->model());
bool autoDelete = CSMSettings::UserSettings::instance().setting (
"search/auto-delete", QString ("true"))=="true";
CSMTools::Search search (mSearch);
CSMWorld::IdTableBase *currentTable = 0;
// We are running through the indices in reverse order to avoid messing up multiple results // We are running through the indices in reverse order to avoid messing up multiple results
// in a single string. // in a single string.
for (std::vector<int>::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter) for (std::vector<int>::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter)
@ -34,16 +41,33 @@ void CSVTools::SearchSubView::replace (bool selection)
CSMWorld::IdTableBase *table = &dynamic_cast<CSMWorld::IdTableBase&> ( CSMWorld::IdTableBase *table = &dynamic_cast<CSMWorld::IdTableBase&> (
*mDocument.getData().getTableModel (type)); *mDocument.getData().getTableModel (type));
if (table!=currentTable)
{
search.configure (table);
currentTable = table;
}
std::string hint = model.getHint (*iter); std::string hint = model.getHint (*iter);
mSearch.replace (mDocument, table, id, hint, replace); if (search.verify (mDocument, table, id, hint))
{
search.replace (mDocument, table, id, hint, replace);
mTable->flagAsReplaced (*iter); mTable->flagAsReplaced (*iter);
if (autoDelete)
mTable->model()->removeRows (*iter, 1);
} }
} }
}
void CSVTools::SearchSubView::showEvent (QShowEvent *event)
{
CSVDoc::SubView::showEvent (event);
mSearchBox.focus();
}
CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: CSVDoc::SubView (id), mDocument (document), mPaddingBefore (10), mPaddingAfter (10), : CSVDoc::SubView (id), mDocument (document), mLocked (false)
mLocked (false)
{ {
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
@ -84,14 +108,6 @@ void CSVTools::SearchSubView::setEditLock (bool locked)
void CSVTools::SearchSubView::updateUserSetting (const QString &name, const QStringList &list) void CSVTools::SearchSubView::updateUserSetting (const QString &name, const QStringList &list)
{ {
mTable->updateUserSetting (name, list); mTable->updateUserSetting (name, list);
if (!list.empty())
{
if (name=="search/char-before")
mPaddingBefore = list.at (0).toInt();
else if (name=="search/char-after")
mPaddingAfter = list.at (0).toInt();
}
} }
void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *document) void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *document)
@ -101,8 +117,13 @@ void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *documen
void CSVTools::SearchSubView::startSearch (const CSMTools::Search& search) void CSVTools::SearchSubView::startSearch (const CSMTools::Search& search)
{ {
CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance();
int paddingBefore = userSettings.setting ("search/char-before", QString ("5")).toInt();
int paddingAfter = userSettings.setting ("search/char-after", QString ("5")).toInt();
mSearch = search; mSearch = search;
mSearch.setPadding (mPaddingBefore, mPaddingAfter); mSearch.setPadding (paddingBefore, paddingAfter);
mTable->clear(); mTable->clear();
mDocument.runSearch (getUniversalId(), mSearch); mDocument.runSearch (getUniversalId(), mSearch);

View file

@ -26,8 +26,6 @@ namespace CSVTools
ReportTable *mTable; ReportTable *mTable;
SearchBox mSearchBox; SearchBox mSearchBox;
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
int mPaddingBefore;
int mPaddingAfter;
CSMTools::Search mSearch; CSMTools::Search mSearch;
bool mLocked; bool mLocked;
@ -35,6 +33,10 @@ namespace CSVTools
void replace (bool selection); void replace (bool selection);
protected:
void showEvent (QShowEvent *event);
public: public:
SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);

View file

@ -40,6 +40,9 @@ namespace CSVWorld
/// Default implementation: Throw an exception if scope!=Scope_Content. /// Default implementation: Throw an exception if scope!=Scope_Content.
virtual void setScope (unsigned int scope); virtual void setScope (unsigned int scope);
/// Focus main input widget
virtual void focus() = 0;
signals: signals:
void done(); void done();

View file

@ -6,11 +6,12 @@
CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values, CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values,
const IconList &icons, const IconList &icons,
CSMWorld::CommandDispatcher *dispatcher,
CSMDoc::Document& document, CSMDoc::Document& document,
const QString &pageName, const QString &pageName,
const QString &settingName, const QString &settingName,
QObject *parent) QObject *parent)
: EnumDelegate (values, document, parent), mDisplayMode (Mode_TextOnly), : EnumDelegate (values, dispatcher, document, parent), mDisplayMode (Mode_TextOnly),
mIcons (icons), mIconSize (QSize(16, 16)), mIconLeftOffset(3), mIcons (icons), mIconSize (QSize(16, 16)), mIconLeftOffset(3),
mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName) mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName)
{ {
@ -136,9 +137,9 @@ void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, QString enumName,
} }
CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate ( CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate (
CSMDoc::Document& document, QObject *parent) const CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const
{ {
return new DataDisplayDelegate (mValues, mIcons, document, "", "", parent); return new DataDisplayDelegate (mValues, mIcons, dispatcher, document, "", "", parent);
} }

View file

@ -38,12 +38,9 @@ namespace CSVWorld
QString mSettingKey; QString mSettingKey;
public: public:
explicit DataDisplayDelegate (const ValueList & values, DataDisplayDelegate (const ValueList & values, const IconList & icons,
const IconList & icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document,
CSMDoc::Document& document, const QString &pageName, const QString &settingName, QObject *parent);
const QString &pageName,
const QString &settingName,
QObject *parent);
~DataDisplayDelegate(); ~DataDisplayDelegate();
@ -82,7 +79,7 @@ namespace CSVWorld
public: public:
virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const;
///< The ownership of the returned CommandDelegate is transferred to the caller. ///< The ownership of the returned CommandDelegate is transferred to the caller.
protected: protected:

View file

@ -0,0 +1,53 @@
#include "dialoguespinbox.hpp"
#include <QWheelEvent>
CSVWorld::DialogueSpinBox::DialogueSpinBox(QWidget *parent) : QSpinBox(parent)
{
setFocusPolicy(Qt::StrongFocus);
}
void CSVWorld::DialogueSpinBox::focusInEvent(QFocusEvent *event)
{
setFocusPolicy(Qt::WheelFocus);
QSpinBox::focusInEvent(event);
}
void CSVWorld::DialogueSpinBox::focusOutEvent(QFocusEvent *event)
{
setFocusPolicy(Qt::StrongFocus);
QSpinBox::focusOutEvent(event);
}
void CSVWorld::DialogueSpinBox::wheelEvent(QWheelEvent *event)
{
if (!hasFocus())
event->ignore();
else
QSpinBox::wheelEvent(event);
}
CSVWorld::DialogueDoubleSpinBox::DialogueDoubleSpinBox(QWidget *parent) : QDoubleSpinBox(parent)
{
setFocusPolicy(Qt::StrongFocus);
}
void CSVWorld::DialogueDoubleSpinBox::focusInEvent(QFocusEvent *event)
{
setFocusPolicy(Qt::WheelFocus);
QDoubleSpinBox::focusInEvent(event);
}
void CSVWorld::DialogueDoubleSpinBox::focusOutEvent(QFocusEvent *event)
{
setFocusPolicy(Qt::StrongFocus);
QDoubleSpinBox::focusOutEvent(event);
}
void CSVWorld::DialogueDoubleSpinBox::wheelEvent(QWheelEvent *event)
{
if (!hasFocus())
event->ignore();
else
QDoubleSpinBox::wheelEvent(event);
}

View file

@ -0,0 +1,40 @@
#ifndef CSV_WORLD_DIALOGUESPINBOX_H
#define CSV_WORLD_DIALOGUESPINBOX_H
#include <QSpinBox>
#include <QDoubleSpinBox>
namespace CSVWorld
{
class DialogueSpinBox : public QSpinBox
{
Q_OBJECT
public:
DialogueSpinBox (QWidget *parent = 0);
protected:
virtual void focusInEvent(QFocusEvent *event);
virtual void focusOutEvent(QFocusEvent *event);
virtual void wheelEvent(QWheelEvent *event);
};
class DialogueDoubleSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
DialogueDoubleSpinBox (QWidget *parent = 0);
protected:
virtual void focusInEvent(QFocusEvent *event);
virtual void focusOutEvent(QFocusEvent *event);
virtual void wheelEvent(QWheelEvent *event);
};
}
#endif // CSV_WORLD_DIALOGUESPINBOX_H

View file

@ -20,6 +20,7 @@
#include <QPushButton> #include <QPushButton>
#include <QToolButton> #include <QToolButton>
#include <QHeaderView> #include <QHeaderView>
#include <QScrollBar>
#include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
@ -29,8 +30,8 @@
#include "../../model/world/record.hpp" #include "../../model/world/record.hpp"
#include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp"
#include "../../model/world/idtree.hpp" #include "../../model/world/idtree.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/doc/document.hpp"
#include "recordstatusdelegate.hpp" #include "recordstatusdelegate.hpp"
#include "util.hpp" #include "util.hpp"
@ -177,10 +178,11 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::
*/ */
CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent,
CSMWorld::IdTable* table, CSMDoc::Document& document, QAbstractItemModel *model) : CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher,
CSMDoc::Document& document, QAbstractItemModel *model) :
mParent(parent), mParent(parent),
mTable(model ? model : table), mTable(model ? model : table),
mDocument (document), mCommandDispatcher (commandDispatcher), mDocument (document),
mNotEditableDelegate(table, parent) mNotEditableDelegate(table, parent)
{ {
} }
@ -192,7 +194,7 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS
if (delegateIt == mDelegates.end()) if (delegateIt == mDelegates.end())
{ {
delegate = CommandDelegateFactoryCollection::get().makeDelegate ( delegate = CommandDelegateFactoryCollection::get().makeDelegate (
display, mDocument, mParent); display, &mCommandDispatcher, mDocument, mParent);
mDelegates.insert(std::make_pair(display, delegate)); mDelegates.insert(std::make_pair(display, delegate));
} else } else
{ {
@ -209,8 +211,17 @@ void CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor,
void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const QModelIndex& index) const void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const QModelIndex& index) const
{ {
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None;
if (index.parent().isValid())
{
display = static_cast<CSMWorld::ColumnBase::Display>
(static_cast<CSMWorld::IdTree *>(mTable)->nestedHeaderData (index.parent().column(), index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
}
else
{
display = static_cast<CSMWorld::ColumnBase::Display>
(mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
}
QLabel* label = qobject_cast<QLabel*>(editor); QLabel* label = qobject_cast<QLabel*>(editor);
if(label) if(label)
@ -346,59 +357,66 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher()
CSVWorld::EditWidget::~EditWidget() CSVWorld::EditWidget::~EditWidget()
{ {
for (unsigned i = 0; i < mNestedModels.size(); ++i) for (unsigned i = 0; i < mNestedModels.size(); ++i)
{
delete mNestedModels[i]; delete mNestedModels[i];
}
if (mDispatcher)
delete mDispatcher;
if (mNestedTableDispatcher)
delete mNestedTableDispatcher; delete mNestedTableDispatcher;
} }
CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMDoc::Document& document, bool createAndDelete) : CSVWorld::EditWidget::EditWidget(QWidget *parent,
mDispatcher(this, table, document), int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher,
mNestedTableDispatcher(NULL), CSMDoc::Document& document, bool createAndDelete) :
QScrollArea(parent), QScrollArea(parent),
mWidgetMapper(NULL), mWidgetMapper(NULL),
mNestedTableMapper(NULL), mNestedTableMapper(NULL),
mDispatcher(NULL),
mNestedTableDispatcher(NULL),
mMainWidget(NULL), mMainWidget(NULL),
mDocument (document), mTable(table),
mTable(table) mCommandDispatcher (commandDispatcher),
mDocument (document)
{ {
remake (row); remake (row);
connect(&mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), connect(mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)),
this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)));
} }
void CSVWorld::EditWidget::remake(int row) void CSVWorld::EditWidget::remake(int row)
{ {
for (unsigned i = 0; i < mNestedModels.size(); ++i)
{
delete mNestedModels[i];
}
mNestedModels.clear();
delete mNestedTableDispatcher;
if (mMainWidget) if (mMainWidget)
{ {
delete mMainWidget; QWidget *del = this->takeWidget();
mMainWidget = 0; del->deleteLater();
} }
mMainWidget = new QWidget (this); mMainWidget = new QWidget (this);
for (unsigned i = 0; i < mNestedModels.size(); ++i)
delete mNestedModels[i];
mNestedModels.clear();
if (mDispatcher)
delete mDispatcher;
mDispatcher = new DialogueDelegateDispatcher(0/*this*/, mTable, mCommandDispatcher, mDocument);
if (mNestedTableDispatcher)
delete mNestedTableDispatcher;
//not sure if widget mapper can handle deleting the widgets that were mapped //not sure if widget mapper can handle deleting the widgets that were mapped
if (mWidgetMapper) if (mWidgetMapper)
{
delete mWidgetMapper; delete mWidgetMapper;
mWidgetMapper = 0;
}
if (mNestedTableMapper)
{
delete mNestedTableMapper;
mNestedTableMapper = 0;
}
mWidgetMapper = new QDataWidgetMapper (this);
mWidgetMapper = new QDataWidgetMapper (this);
mWidgetMapper->setModel(mTable); mWidgetMapper->setModel(mTable);
mWidgetMapper->setItemDelegate(&mDispatcher); mWidgetMapper->setItemDelegate(mDispatcher);
if (mNestedTableMapper)
delete mNestedTableMapper;
QFrame* line = new QFrame(mMainWidget); QFrame* line = new QFrame(mMainWidget);
line->setObjectName(QString::fromUtf8("line")); line->setObjectName(QString::fromUtf8("line"));
@ -440,11 +458,26 @@ void CSVWorld::EditWidget::remake(int row)
if (mTable->hasChildren(mTable->index(row, i)) && if (mTable->hasChildren(mTable->index(row, i)) &&
!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) !(flags & CSMWorld::ColumnBase::Flag_Dialogue_List))
{ {
mNestedModels.push_back(new CSMWorld::NestedTableProxyModel (mTable->index(row, i), display, dynamic_cast<CSMWorld::IdTree*>(mTable))); mNestedModels.push_back(new CSMWorld::NestedTableProxyModel (
mTable->index(row, i), display, dynamic_cast<CSMWorld::IdTree*>(mTable)));
NestedTable* table = new NestedTable(mDocument, mNestedModels.back(), this); int idColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
int typeColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);
CSMWorld::UniversalId id = CSMWorld::UniversalId(
static_cast<CSMWorld::UniversalId::Type> (mTable->data (mTable->index (row, typeColumn)).toInt()),
mTable->data (mTable->index (row, idColumn)).toString().toUtf8().constData());
NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this);
// FIXME: does not work well when enum delegates are used // FIXME: does not work well when enum delegates are used
//table->resizeColumnsToContents(); //table->resizeColumnsToContents();
if(mTable->index(row, i).data().type() == QVariant::UserType)
{
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
table->setEnabled(false);
}
else
table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::CurrentChanged); table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::CurrentChanged);
int rows = mTable->rowCount(mTable->index(row, i)); int rows = mTable->rowCount(mTable->index(row, i));
@ -457,14 +490,16 @@ void CSVWorld::EditWidget::remake(int row)
new QLabel (mTable->headerData (i, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); new QLabel (mTable->headerData (i, Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget);
label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
if(mTable->index(row, i).data().type() == QVariant::UserType)
label->setEnabled(false);
tablesLayout->addWidget(label); tablesLayout->addWidget(label);
tablesLayout->addWidget(table); tablesLayout->addWidget(table);
} }
else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List))
{ {
mDispatcher.makeDelegate (display); mDispatcher->makeDelegate (display);
QWidget* editor = mDispatcher.makeEditor (display, (mTable->index (row, i))); QWidget* editor = mDispatcher->makeEditor (display, (mTable->index (row, i)));
if (editor) if (editor)
{ {
@ -487,26 +522,30 @@ void CSVWorld::EditWidget::remake(int row)
unlockedLayout->addWidget (editor, unlocked, 1); unlockedLayout->addWidget (editor, unlocked, 1);
++unlocked; ++unlocked;
} }
if(mTable->index(row, i).data().type() == QVariant::UserType)
{
editor->setEnabled(false);
label->setEnabled(false);
}
} }
} }
else else
{ {
mNestedModels.push_back(new CSMWorld::NestedTableProxyModel ( CSMWorld::IdTree *tree = static_cast<CSMWorld::IdTree *>(mTable);
static_cast<CSMWorld::IdTree *>(mTable)->index(row, i),
display, static_cast<CSMWorld::IdTree *>(mTable)));
mNestedTableMapper = new QDataWidgetMapper (this); mNestedTableMapper = new QDataWidgetMapper (this);
mNestedTableMapper->setModel(mNestedModels.back()); mNestedTableMapper->setModel(tree);
// FIXME: lack MIME support? // FIXME: lack MIME support?
mNestedTableDispatcher = mNestedTableDispatcher =
new DialogueDelegateDispatcher (this, mTable, mDocument, mNestedModels.back()); new DialogueDelegateDispatcher (0/*this*/, mTable, mCommandDispatcher, mDocument, tree);
mNestedTableMapper->setRootIndex (tree->index(row, i));
mNestedTableMapper->setItemDelegate(mNestedTableDispatcher); mNestedTableMapper->setItemDelegate(mNestedTableDispatcher);
int columnCount = int columnCount = tree->columnCount(tree->index(row, i));
mTable->columnCount(mTable->getModelIndex (mNestedModels.back()->getParentId(), i));
for (int col = 0; col < columnCount; ++col) for (int col = 0; col < columnCount; ++col)
{ {
int displayRole = mNestedModels.back()->headerData (col, int displayRole = tree->nestedHeaderData (i, col,
Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt(); Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt();
CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display display =
@ -516,16 +555,16 @@ void CSVWorld::EditWidget::remake(int row)
// FIXME: assumed all columns are editable // FIXME: assumed all columns are editable
QWidget* editor = QWidget* editor =
mNestedTableDispatcher->makeEditor (display, mNestedModels.back()->index (0, col)); mNestedTableDispatcher->makeEditor (display, tree->index (0, col, tree->index(row, i)));
if (editor) if (editor)
{ {
mNestedTableMapper->addMapping (editor, col); mNestedTableMapper->addMapping (editor, col);
std::string disString = mNestedModels.back()->headerData (col, std::string disString = tree->nestedHeaderData (i, col,
Qt::Horizontal, Qt::DisplayRole).toString().toStdString(); Qt::Horizontal, Qt::DisplayRole).toString().toStdString();
// Need ot use Qt::DisplayRole in order to get the correct string // Need to use Qt::DisplayRole in order to get the correct string
// from CSMWorld::Columns // from CSMWorld::Columns
QLabel* label = new QLabel (mNestedModels.back()->headerData (col, QLabel* label = new QLabel (tree->nestedHeaderData (i, col,
Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget); Qt::Horizontal, Qt::DisplayRole).toString(), mMainWidget);
label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
@ -534,9 +573,15 @@ void CSVWorld::EditWidget::remake(int row)
unlockedLayout->addWidget (label, unlocked, 0); unlockedLayout->addWidget (label, unlocked, 0);
unlockedLayout->addWidget (editor, unlocked, 1); unlockedLayout->addWidget (editor, unlocked, 1);
++unlocked; ++unlocked;
if(tree->index(0, col, tree->index(row, i)).data().type() == QVariant::UserType)
{
editor->setEnabled(false);
label->setEnabled(false);
} }
} }
mNestedTableMapper->setCurrentModelIndex(mNestedModels.back()->index(0, 0)); }
mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i)));
} }
} }
} }
@ -559,13 +604,14 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
SubView (id), SubView (id),
mEditWidget(0), mEditWidget(0),
mMainLayout(NULL), mMainLayout(NULL),
mUndoStack(document.getUndoStack()),
mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))), mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))),
mUndoStack(document.getUndoStack()),
mLocked(false), mLocked(false),
mDocument(document), mDocument(document),
mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())) mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType()))
{ {
connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&)));
connect(mTable, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex&, int, int)));
changeCurrentId(id.getId()); changeCurrentId(id.getId());
@ -626,7 +672,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
mMainLayout = new QVBoxLayout(mainWidget); mMainLayout = new QVBoxLayout(mainWidget);
mEditWidget = new EditWidget(mainWidget, mTable->getModelIndex(mCurrentId, 0).row(), mTable, document, false); mEditWidget = new EditWidget(mainWidget,
mTable->getModelIndex(mCurrentId, 0).row(), mTable, mCommandDispatcher, document, false);
connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)),
this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)));
@ -727,6 +774,9 @@ void CSVWorld::DialogueSubView::nextId ()
void CSVWorld::DialogueSubView::setEditLock (bool locked) void CSVWorld::DialogueSubView::setEditLock (bool locked)
{ {
if (!mEditWidget) // hack to indicate that mCurrentId is no longer valid
return;
mLocked = locked; mLocked = locked;
QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0));
@ -745,11 +795,47 @@ void CSVWorld::DialogueSubView::dataChanged (const QModelIndex & index)
{ {
QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0)); QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0));
if (currentIndex.isValid() && index.row() == currentIndex.row()) if (currentIndex.isValid() &&
(index.parent().isValid() ? index.parent().row() : index.row()) == currentIndex.row())
{ {
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (currentIndex.row(), 1)).toInt()); CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (currentIndex.row(), 1)).toInt());
mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked); mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked);
// Check if the changed data should force refresh (rebuild) the dialogue subview
int flags = 0;
if (index.parent().isValid()) // TODO: check that index is topLeft
{
flags = static_cast<CSMWorld::IdTree *>(mTable)->nestedHeaderData (index.parent().column(),
index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();
}
else
{
flags = mTable->headerData (index.column(),
Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();
}
if (flags & CSMWorld::ColumnBase::Flag_Dialogue_Refresh)
{
int y = mEditWidget->verticalScrollBar()->value();
mEditWidget->remake (index.parent().isValid() ? index.parent().row() : index.row());
mEditWidget->verticalScrollBar()->setValue(y);
}
}
}
void CSVWorld::DialogueSubView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
QModelIndex currentIndex(mTable->getModelIndex(mCurrentId, 0));
if (currentIndex.isValid() && currentIndex.row() >= start && currentIndex.row() <= end)
{
if(mEditWidget)
{
delete mEditWidget;
mEditWidget = 0;
}
emit closeRequest(this);
} }
} }

View file

@ -109,6 +109,7 @@ namespace CSVWorld
QAbstractItemModel* mTable; QAbstractItemModel* mTable;
CSMWorld::CommandDispatcher& mCommandDispatcher;
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
NotEditableSubDelegate mNotEditableDelegate; NotEditableSubDelegate mNotEditableDelegate;
@ -119,6 +120,7 @@ namespace CSVWorld
public: public:
DialogueDelegateDispatcher(QObject* parent, DialogueDelegateDispatcher(QObject* parent,
CSMWorld::IdTable* table, CSMWorld::IdTable* table,
CSMWorld::CommandDispatcher& commandDispatcher,
CSMDoc::Document& document, CSMDoc::Document& document,
QAbstractItemModel* model = 0); QAbstractItemModel* model = 0);
@ -163,16 +165,18 @@ namespace CSVWorld
Q_OBJECT Q_OBJECT
QDataWidgetMapper *mWidgetMapper; QDataWidgetMapper *mWidgetMapper;
QDataWidgetMapper *mNestedTableMapper; QDataWidgetMapper *mNestedTableMapper;
DialogueDelegateDispatcher mDispatcher; DialogueDelegateDispatcher *mDispatcher;
DialogueDelegateDispatcher *mNestedTableDispatcher; DialogueDelegateDispatcher *mNestedTableDispatcher;
QWidget* mMainWidget; QWidget* mMainWidget;
CSMWorld::IdTable* mTable; CSMWorld::IdTable* mTable;
CSMWorld::CommandDispatcher& mCommandDispatcher;
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
std::vector<CSMWorld::NestedTableProxyModel*> mNestedModels; //Plain, raw C pointers, deleted in the dtor std::vector<CSMWorld::NestedTableProxyModel*> mNestedModels; //Plain, raw C pointers, deleted in the dtor
public: public:
EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table,
CSMWorld::CommandDispatcher& commandDispatcher,
CSMDoc::Document& document, bool createAndDelete = false); CSMDoc::Document& document, bool createAndDelete = false);
virtual ~EditWidget(); virtual ~EditWidget();
@ -231,6 +235,8 @@ namespace CSVWorld
const CSMDoc::Document* document); const CSMDoc::Document* document);
void requestFocus (const std::string& id); void requestFocus (const std::string& id);
void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
}; };
} }

View file

@ -17,8 +17,8 @@ void CSVWorld::DragRecordTable::startDragFromTable (const CSVWorld::DragRecordTa
} }
CSVWorld::DragRecordTable::DragRecordTable (CSMDoc::Document& document, QWidget* parent) : CSVWorld::DragRecordTable::DragRecordTable (CSMDoc::Document& document, QWidget* parent) :
mDocument(document),
QTableView(parent), QTableView(parent),
mDocument(document),
mEditLock(false) mEditLock(false)
{} {}

View file

@ -37,8 +37,8 @@ void CSVWorld::EnumDelegate::addCommands (QAbstractItemModel *model,
CSVWorld::EnumDelegate::EnumDelegate (const std::vector<std::pair<int, QString> >& values, CSVWorld::EnumDelegate::EnumDelegate (const std::vector<std::pair<int, QString> >& values,
CSMDoc::Document& document, QObject *parent) CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent)
: CommandDelegate (document, parent), mValues (values) : CommandDelegate (dispatcher, document, parent), mValues (values)
{ {
} }
@ -142,9 +142,9 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::strin
} }
CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate ( CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (
CSMDoc::Document& document, QObject *parent) const CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const
{ {
return new EnumDelegate (mValues, document, parent); return new EnumDelegate (mValues, dispatcher, document, parent);
} }
void CSVWorld::EnumDelegateFactory::add (int value, const QString& name) void CSVWorld::EnumDelegateFactory::add (int value, const QString& name)

View file

@ -30,7 +30,7 @@ namespace CSVWorld
public: public:
EnumDelegate (const std::vector<std::pair<int, QString> >& values, EnumDelegate (const std::vector<std::pair<int, QString> >& values,
CSMDoc::Document& document, QObject *parent); CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent);
virtual QWidget *createEditor(QWidget *parent, virtual QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem& option, const QStyleOptionViewItem& option,
@ -64,7 +64,7 @@ namespace CSVWorld
EnumDelegateFactory (const std::vector<std::string>& names, bool allowNone = false); EnumDelegateFactory (const std::vector<std::string>& names, bool allowNone = false);
/// \param allowNone Use value of -1 for "none selected" (empty string) /// \param allowNone Use value of -1 for "none selected" (empty string)
virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const;
///< The ownership of the returned CommandDelegate is transferred to the caller. ///< The ownership of the returned CommandDelegate is transferred to the caller.
void add (int value, const QString& name); void add (int value, const QString& name);

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