Merge branch 'pathgrid-edit' into Feature-1278

Conflicts:
	apps/opencs/view/render/cell.cpp
	apps/opencs/view/render/mousestate.cpp
pull/1955/head
cc9cii 9 years ago
commit f7598a67f4

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

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

@ -19,8 +19,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 35)
set(OPENMW_VERSION_RELEASE 1)
set(OPENMW_VERSION_MINOR 36)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "")
set(OPENMW_VERSION_TAGHASH "")
@ -134,9 +134,11 @@ endif()
# Platform specific
if (WIN32)
if(NOT MINGW)
set(Boost_USE_STATIC_LIBS ON)
set(PLATFORM_INCLUDE_DIR "platform")
add_definitions(-DBOOST_ALL_NO_LIB)
endif(NOT MINGW)
# Suppress WinMain(), provided by SDL
add_definitions(-DSDL_MAIN_HANDLED)
@ -351,7 +353,7 @@ endif()
# CXX Compiler settings
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)
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION)

@ -1,12 +1,12 @@
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
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)
* Website: http://www.openmw.org
* IRC: #openmw on irc.freenode.net

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

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

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

@ -25,7 +25,7 @@
using namespace Process;
Launcher::MainDialog::MainDialog(QWidget *parent)
: mGameSettings(mCfgMgr), QMainWindow (parent)
: QMainWindow(parent), mGameSettings (mCfgMgr)
{
setupUi(this);
@ -148,10 +148,10 @@ void Launcher::MainDialog::createPages()
}
bool Launcher::MainDialog::showFirstRunDialog()
Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
{
if (!setupLauncherSettings())
return false;
return FirstRunDialogResultFailure;
if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true"))
{
@ -176,14 +176,14 @@ bool Launcher::MainDialog::showFirstRunDialog()
if (msgBox.clickedButton() == wizardButton)
{
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) {
return false;
return FirstRunDialogResultFailure;
} else {
return true;
return FirstRunDialogResultWizard;
}
}
}
return setup();
return setup() ? FirstRunDialogResultContinue : FirstRunDialogResultFailure;
}
bool Launcher::MainDialog::setup()
@ -490,7 +490,7 @@ bool Launcher::MainDialog::writeSettings()
// Game settings
QFile file(userPath + QString("openmw.cfg"));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
@ -503,10 +503,8 @@ bool Launcher::MainDialog::writeSettings()
return false;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mGameSettings.writeFile(stream);
mGameSettings.writeFileWithComments(file);
file.close();
// Graphics settings
@ -525,6 +523,7 @@ bool Launcher::MainDialog::writeSettings()
return false;
}
QTextStream stream(&file);
stream.setDevice(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));

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

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

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

@ -27,6 +27,7 @@ opencs_units_noqt (model/world
universalid record commands columnbase scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager
)
opencs_hdrs_noqt (model/world
@ -41,13 +42,13 @@ opencs_units (model/tools
opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage
startscriptcheck search searchoperation searchstage pathgridcheck
)
opencs_units (view/doc
viewmanager view operations operation subview startup filedialog newgame
filewidget adjusterwidget loader globaldebugprofilemenu runlogsubview
filewidget adjusterwidget loader globaldebugprofilemenu runlogsubview sizehint
)
@ -64,16 +65,17 @@ opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox
)
opencs_units_noqt (view/world
subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator physicssystem
scripthighlighter idvalidator dialoguecreator physicssystem idcompletiondelegate
)
opencs_units (view/widget
scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton
scenetooltoggle2
scenetooltoggle2 completerpopup
)
opencs_units (view/render

@ -23,8 +23,8 @@
CS::Editor::Editor (OgreInit::OgreInit& ogreInit)
: mUserSettings (mCfgMgr), mOverlaySystem (0), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL), mPid(""), mLock()
mViewManager (mDocumentManager), mPid(""),
mLock(), mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
{
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&)),
this, SLOT(createNewFile (const boost::filesystem::path&)));
connect (&mFileDialog, SIGNAL (rejected()), this, SLOT (cancelFileDialog ()));
connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)),
this, SLOT (createNewGame (const boost::filesystem::path&)));
connect (&mNewGame, SIGNAL (cancelCreateGame()), this, SLOT (cancelCreateGame ()));
}
CS::Editor::~Editor ()
@ -176,12 +178,40 @@ void CS::Editor::createGame()
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()
{
mStartup.hide();
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()
{
mStartup.hide();

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

@ -2251,12 +2251,14 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
const std::vector<std::string>& blacklistedScripts)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager),
mTools (*this), mResDir(resDir),
mTools (*this),
mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")),
mSavingOperation (*this, mProjectPath, encoding),
mSaving (&mSavingOperation),
mRunner (mProjectPath), mPhysics(boost::shared_ptr<CSVWorld::PhysicsSystem>())
mResDir(resDir),
mRunner (mProjectPath), mPhysics(boost::shared_ptr<CSVWorld::PhysicsSystem>()),
mIdCompletionManager(mData)
{
if (mContentFiles.empty())
throw std::runtime_error ("Empty content file sequence");
@ -2487,3 +2489,8 @@ boost::shared_ptr<CSVWorld::PhysicsSystem> CSMDoc::Document::getPhysics ()
return mPhysics;
}
CSMWorld::IdCompletionManager &CSMDoc::Document::getIdCompletionManager()
{
return mIdCompletionManager;
}

@ -13,6 +13,7 @@
#include <components/to_utf8/to_utf8.hpp>
#include "../world/data.hpp"
#include "../world/idcompletionmanager.hpp"
#include "../tools/tools.hpp"
@ -66,6 +67,7 @@ namespace CSMDoc
Blacklist mBlacklist;
Runner mRunner;
boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics;
CSMWorld::IdCompletionManager mIdCompletionManager;
// It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is
// using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
@ -144,6 +146,8 @@ namespace CSMDoc
boost::shared_ptr<CSVWorld::PhysicsSystem> getPhysics();
CSMWorld::IdCompletionManager &getIdCompletionManager();
signals:
void stateChanged (int state, CSMDoc::Document *document);

@ -49,6 +49,11 @@ CSMDoc::DocumentManager::~DocumentManager()
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,
bool new_)
{

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

@ -231,8 +231,29 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)
record.mState==CSMWorld::RecordBase::State_Modified ||
record.mState==CSMWorld::RecordBase::State_ModifiedOnly)
{
mState.getSubRecords()[Misc::StringUtils::lowerCase (record.get().mCell)]
.push_back (i);
std::string cellId = record.get().mOriginalCell.empty() ?
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 =
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));
if (cell.mState==CSMWorld::RecordBase::State_Modified ||
@ -284,7 +305,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
// write references
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)
{
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 ||
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());
}
else if (ref.mState==CSMWorld::RecordBase::State_Deleted)

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

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

@ -9,7 +9,7 @@
CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType,
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,
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);
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;
double value = data.toDouble();

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

@ -143,6 +143,24 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
minWidth->setDefaultValue (325);
minWidth->setRange (50, 10000);
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");
@ -206,6 +224,14 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
shiftCtrlDoubleClick->setDeclaredValues (values);
shiftCtrlDoubleClick->setDefaultValue (editRecordAndClose);
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");
@ -220,9 +246,60 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"Characters after search string");
after->setDefaultValue (10);
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);
}
{
/******************************************************************
* There are three types of values:

@ -0,0 +1,151 @@
#include "pathgridcheck.hpp"
#include <sstream>
#include <algorithm>
#include "../world/universalid.hpp"
#include "../world/idcollection.hpp"
#include "../world/subcellcollection.hpp"
#include "../world/pathgrid.hpp"
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<CSMTools::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
}

@ -0,0 +1,40 @@
#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
{
struct Point
{
unsigned char mConnectionNum;
std::vector<int> mOtherIndex;
Point() : mConnectionNum(0), mOtherIndex(0) {}
};
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

@ -8,12 +8,14 @@
CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection<ESM::Race >& races,
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),
mClasses(classes),
mRaces(races),
mClasses(classes),
mFactions(faction),
mScripts(scripts),
mPlayerPresent(false)
{
}
@ -245,6 +247,9 @@ void CSMTools::ReferenceableCheckStage::bookCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId);
inventoryItemCheck<ESM::Book>(book, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Book>(book, messages, id.toString());
}
void CSMTools::ReferenceableCheckStage::activatorCheck(
@ -265,6 +270,9 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
//Checking for model, IIRC all activators should have a model
if (activator.mModel.empty())
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(
@ -284,6 +292,9 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
inventoryItemCheck<ESM::Potion>(potion, messages, id.toString());
//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());
toolCheck<ESM::Apparatus>(apparatus, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Apparatus>(apparatus, messages, id.toString());
}
void CSMTools::ReferenceableCheckStage::armorCheck(
@ -331,6 +345,9 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
//checking for health. Only positive numbers are allowed, or 0 is illegal
if (armor.mData.mHealth <= 0)
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(
@ -348,6 +365,9 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(
const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId);
inventoryItemCheck<ESM::Clothing>(clothing, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Clothing>(clothing, messages, id.toString());
}
void CSMTools::ReferenceableCheckStage::containerCheck(
@ -377,6 +397,9 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
//checking for name
if (container.mName.empty())
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 (
@ -444,6 +467,9 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
//TODO, find meaning of other values
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 "));
// Check that mentioned scripts exist
scriptCheck<ESM::Creature>(creature, messages, id.toString());
}
void CSMTools::ReferenceableCheckStage::doorCheck(
@ -455,15 +481,18 @@ void CSMTools::ReferenceableCheckStage::doorCheck(
if (baseRecord.isDeleted())
return;
const ESM::Door& Door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId);
const ESM::Door& door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, door.mId);
//usual, name or model
if (Door.mName.empty())
messages.push_back (std::make_pair (id, Door.mId + " has an empty name"));
if (door.mName.empty())
messages.push_back (std::make_pair (id, door.mId + " has an empty name"));
if (Door.mModel.empty())
messages.push_back (std::make_pair (id, Door.mId + " has no model"));
if (door.mModel.empty())
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(
@ -478,10 +507,13 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(
return;
}
const ESM::Ingredient& Ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId);
const ESM::Ingredient& ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get();
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(
@ -542,6 +574,9 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
if (light.mData.mTime == 0)
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(
@ -562,6 +597,9 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck(
inventoryItemCheck<ESM::Lockpick>(lockpick, messages, id.toString());
toolCheck<ESM::Lockpick>(lockpick, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Lockpick>(lockpick, messages, id.toString());
}
void CSMTools::ReferenceableCheckStage::miscCheck(
@ -580,6 +618,9 @@ void CSMTools::ReferenceableCheckStage::miscCheck(
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId);
inventoryItemCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString());
// Check that mentioned scripts exist
scriptCheck<ESM::Miscellaneous>(miscellaneous, messages, id.toString());
}
void CSMTools::ReferenceableCheckStage::npcCheck (
@ -697,6 +738,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
messages.push_back (std::make_pair (id, npc.mId + " has no hair"));
//TODO: reputation, Disposition, rank, everything else
// Check that mentioned scripts exist
scriptCheck<ESM::NPC>(npc, messages, id.toString());
}
void CSMTools::ReferenceableCheckStage::weaponCheck(
@ -773,6 +817,9 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(
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(
@ -792,6 +839,9 @@ void CSMTools::ReferenceableCheckStage::probeCheck(
inventoryItemCheck<ESM::Probe>(probe, messages, id.toString());
toolCheck<ESM::Probe>(probe, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Probe>(probe, messages, id.toString());
}
void CSMTools::ReferenceableCheckStage::repairCheck (
@ -808,6 +858,9 @@ void CSMTools::ReferenceableCheckStage::repairCheck (
inventoryItemCheck<ESM::Repair> (repair, messages, id.toString());
toolCheck<ESM::Repair> (repair, messages, id.toString(), true);
// Check that mentioned scripts exist
scriptCheck<ESM::Repair>(repair, messages, id.toString());
}
void CSMTools::ReferenceableCheckStage::staticCheck (
@ -919,3 +972,13 @@ template<typename List> void CSMTools::ReferenceableCheckStage::listCheck (
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+"\""));
}
}

@ -15,7 +15,8 @@ namespace CSMTools
ReferenceableCheckStage (const CSMWorld::RefIdData& referenceable,
const CSMWorld::IdCollection<ESM::Race>& races,
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 int setup();
@ -46,26 +47,30 @@ namespace CSMTools
//FINAL CHECK
void finalCheck (CSMDoc::Messages& messages);
//TEMPLATE CHECKS
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
//TEMPLATE CHECKS
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
CSMDoc::Messages& messages,
const std::string& someID,
bool enchantable); //for all enchantable items.
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
CSMDoc::Messages& messages,
const std::string& someID); //for non-enchantable items.
template<typename TOOL> void toolCheck(const TOOL& someTool,
template<typename TOOL> void toolCheck(const TOOL& someTool,
CSMDoc::Messages& messages,
const std::string& someID,
bool canbebroken); //for tools with uses.
template<typename TOOL> void toolCheck(const TOOL& someTool,
template<typename TOOL> void toolCheck(const TOOL& someTool,
CSMDoc::Messages& messages,
const std::string& someID); //for tools without uses.
template<typename LIST> void listCheck(const LIST& someList,
template<typename LIST> void listCheck(const LIST& someList,
CSMDoc::Messages& messages,
const std::string& someID);
template<typename TOOL> void scriptCheck(const TOOL& someTool,
CSMDoc::Messages& messages,
const std::string& someID);
@ -73,6 +78,7 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Race>& mRaces;
const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::Script>& mScripts;
bool mPlayerPresent;
};
}

@ -28,7 +28,7 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message
// Check for empty reference id
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 {
// Check for non existing referenced object
if (mReferencables.searchId(cellRef.mRefID) == -1) {

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

@ -276,4 +276,21 @@ void CSMTools::Search::replace (CSMDoc::Document& document, CSMWorld::IdTableBas
new CSMWorld::ModifyCommand (*model, index, QString::fromUtf8 (newText.c_str())));
}
}
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;
}

@ -88,6 +88,10 @@ namespace CSMTools
void replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model,
const CSMWorld::UniversalId& id, const std::string& messageHint,
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;
};
}

@ -26,6 +26,7 @@
#include "referencecheck.hpp"
#include "startscriptcheck.hpp"
#include "searchoperation.hpp"
#include "pathgridcheck.hpp"
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 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()));
@ -96,6 +97,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
CSMWorld::UniversalId( CSMWorld::UniversalId::Type_Meshes )),
mData.getRaces() ));
mVerifierOperation->appendStage (new PathgridCheckStage (mData.getPathgrids()));
mVerifier.setOperation (mVerifierOperation);
}
@ -103,8 +106,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
}
CSMTools::Tools::Tools (CSMDoc::Document& document)
: mDocument (document), mData (document.getData()), mVerifierOperation (0), mNextReportNumber (0),
mSearchOperation (0)
: mDocument (document), mData (document.getData()), mVerifierOperation (0),
mSearchOperation (0), mNextReportNumber (0)
{
// index 0: load error log
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
@ -114,7 +117,7 @@ CSMTools::Tools::Tools (CSMDoc::Document& document)
connect (&mSearch, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));
connect (&mSearch,
SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)),
this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)));
this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)));
}
CSMTools::Tools::~Tools()
@ -130,7 +133,7 @@ CSMTools::Tools::~Tools()
mSearch.abortAndWait();
delete mSearchOperation;
}
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
delete iter->second;
}
@ -149,7 +152,7 @@ CSMWorld::UniversalId CSMTools::Tools::newSearch()
{
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true)));
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1);
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1);
}
void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Search& search)
@ -159,11 +162,11 @@ void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Se
if (!mSearchOperation)
{
mSearchOperation = new SearchOperation (mDocument);
mSearch.setOperation (mSearchOperation);
mSearch.setOperation (mSearchOperation);
}
mSearchOperation->configure (search);
mSearch.start();
}
@ -198,7 +201,7 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId&
id.getType()!=CSMWorld::UniversalId::Type_LoadErrorLog &&
id.getType()!=CSMWorld::UniversalId::Type_Search)
throw std::logic_error ("invalid request for report model: " + id.toString());
return mReports.at (id.getIndex());
}

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

@ -17,14 +17,16 @@ namespace CSMWorld
enum Roles
{
Role_Flags = Qt::UserRole,
Role_Display = Qt::UserRole+1
Role_Display = Qt::UserRole+1,
Role_ColumnId = Qt::UserRole+2
};
enum Flags
{
Flag_Table = 1, // column should be displayed in table 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
@ -72,6 +74,8 @@ namespace CSMWorld
Display_JournalInfo,
Display_Scene,
Display_GlobalVariable,
Display_BodyPart,
Display_Enchantment,
//CONCRETE TYPES ENDS HERE
Display_Integer,
@ -115,6 +119,10 @@ namespace CSMWorld
Display_PartRefType,
Display_AiPackageType,
Display_YesNo,
Display_InfoCondFunc,
Display_InfoCondVar,
Display_InfoCondComp,
Display_RaceSkill,
//top level columns that nest other columns
Display_NestedHeader
@ -195,7 +203,8 @@ namespace CSMWorld
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;

@ -467,8 +467,9 @@ namespace CSMWorld
int mMask;
bool mInverted;
FlagColumn (int columnId, int mask, bool inverted = false)
: Column<ESXRecordT> (columnId, ColumnBase::Display_Boolean), mMask (mask),
FlagColumn (int columnId, int mask,
int flags = ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, bool inverted = false)
: Column<ESXRecordT> (columnId, ColumnBase::Display_Boolean, flags), mMask (mask),
mInverted (inverted)
{}
@ -708,7 +709,7 @@ namespace CSMWorld
struct SleepListColumn : public Column<ESXRecordT>
{
SleepListColumn()
: Column<ESXRecordT> (Columns::ColumnId_SleepEncounter, ColumnBase::Display_String)
: Column<ESXRecordT> (Columns::ColumnId_SleepEncounter, ColumnBase::Display_CreatureLevelledList)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
@ -734,7 +735,7 @@ namespace CSMWorld
template<typename ESXRecordT>
struct TextureColumn : public Column<ESXRecordT>
{
TextureColumn() : Column<ESXRecordT> (Columns::ColumnId_Texture, ColumnBase::Display_String) {}
TextureColumn() : Column<ESXRecordT> (Columns::ColumnId_Texture, ColumnBase::Display_Texture) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
@ -870,7 +871,13 @@ namespace CSMWorld
template<typename 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
{
@ -892,9 +899,41 @@ namespace CSMWorld
}
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;
}
virtual bool isUserEditable() const
{
return false;
}
};
template<typename ESXRecordT>
@ -1230,7 +1269,7 @@ namespace CSMWorld
template<typename ESXRecordT>
struct TrapColumn : public Column<ESXRecordT>
{
TrapColumn() : Column<ESXRecordT> (Columns::ColumnId_Trap, ColumnBase::Display_String) {}
TrapColumn() : Column<ESXRecordT> (Columns::ColumnId_Trap, ColumnBase::Display_Spell) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
@ -1255,7 +1294,7 @@ namespace CSMWorld
template<typename ESXRecordT>
struct FilterColumn : public Column<ESXRecordT>
{
FilterColumn() : Column<ESXRecordT> (Columns::ColumnId_Filter, ColumnBase::Display_String) {}
FilterColumn() : Column<ESXRecordT> (Columns::ColumnId_Filter, ColumnBase::Display_Filter) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
@ -1458,7 +1497,10 @@ namespace CSMWorld
template<typename ESXRecordT>
struct TopicColumn : public Column<ESXRecordT>
{
TopicColumn (bool journal) : Column<ESXRecordT> (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic, ColumnBase::Display_String) {}
TopicColumn (bool journal)
: Column<ESXRecordT> (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic,
journal ? ColumnBase::Display_Journal : ColumnBase::Display_Topic)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
@ -1488,7 +1530,7 @@ namespace CSMWorld
template<typename ESXRecordT>
struct ActorColumn : public Column<ESXRecordT>
{
ActorColumn() : Column<ESXRecordT> (Columns::ColumnId_Actor, ColumnBase::Display_String) {}
ActorColumn() : Column<ESXRecordT> (Columns::ColumnId_Actor, ColumnBase::Display_Npc) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
@ -1791,7 +1833,7 @@ namespace CSMWorld
template<typename ESXRecordT>
struct ModelColumn : public Column<ESXRecordT>
{
ModelColumn() : Column<ESXRecordT> (Columns::ColumnId_Model, ColumnBase::Display_String) {}
ModelColumn() : Column<ESXRecordT> (Columns::ColumnId_Model, ColumnBase::Display_Mesh) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
@ -2119,7 +2161,9 @@ namespace CSMWorld
struct EffectTextureColumn : public Column<ESXRecordT>
{
EffectTextureColumn (Columns::ColumnId columnId)
: Column<ESXRecordT> (columnId, ColumnBase::Display_Texture)
: Column<ESXRecordT> (columnId,
columnId == Columns::ColumnId_Particle ? ColumnBase::Display_Texture
: ColumnBase::Display_Icon)
{
assert (this->mColumnId==Columns::ColumnId_Icon ||
this->mColumnId==Columns::ColumnId_Particle);

@ -172,11 +172,11 @@ namespace CSMWorld
{ ColumnId_Rank, "Rank" },
{ ColumnId_Gender, "Gender" },
{ ColumnId_PcRank, "PC Rank" },
{ ColumnId_ReferenceableId, "Referenceable ID" },
{ ColumnId_ReferenceableId, "Object ID" },
{ ColumnId_ContainerContent, "Content" },
{ ColumnId_ItemCount, "Count" },
{ ColumnId_InventoryItemId, "ID"},
{ ColumnId_InventoryItemId, "Item ID"},
{ ColumnId_CombatState, "Combat" },
{ ColumnId_MagicState, "Magic" },
@ -188,10 +188,10 @@ namespace CSMWorld
{ ColumnId_ActorInventory, "Inventory" },
{ ColumnId_SpellList, "Spells" },
{ ColumnId_SpellId, "ID"},
{ ColumnId_SpellId, "Spell ID"},
{ ColumnId_NpcDestinations, "Destinations" },
{ ColumnId_DestinationCell, "Cell"},
{ ColumnId_DestinationCell, "Dest Cell"},
{ ColumnId_PosX, "Dest X"},
{ ColumnId_PosY, "Dest Y"},
{ ColumnId_PosZ, "Dest Z"},
@ -224,17 +224,17 @@ namespace CSMWorld
{ ColumnId_BoltSound, "Bolt Sound" },
{ ColumnId_PathgridPoints, "Points" },
{ ColumnId_PathgridIndex, "Index" },
{ ColumnId_PathgridIndex, "pIndex" },
{ ColumnId_PathgridPosX, "X" },
{ ColumnId_PathgridPosY, "Y" },
{ ColumnId_PathgridPosZ, "Z" },
{ ColumnId_PathgridEdges, "Edges" },
{ ColumnId_PathgridEdgeIndex, "Index" },
{ ColumnId_PathgridEdgeIndex, "eIndex" },
{ ColumnId_PathgridEdge0, "Point 0" },
{ ColumnId_PathgridEdge1, "Point 1" },
{ ColumnId_RegionSounds, "Sounds" },
{ ColumnId_SoundName, "Name" },
{ ColumnId_SoundName, "Sound Name" },
{ ColumnId_SoundChance, "Chance" },
{ ColumnId_FactionReactions, "Reactions" },
@ -250,7 +250,7 @@ namespace CSMWorld
{ ColumnId_AiPackageList, "Ai Packages" },
{ ColumnId_AiPackageType, "Package" },
{ ColumnId_AiWanderDist, "Wander Dist" },
{ ColumnId_AiDuration, "Duration" },
{ ColumnId_AiDuration, "Ai Duration" },
{ ColumnId_AiWanderToD, "Wander ToD" },
{ ColumnId_AiWanderIdle, "Wander Idle" },
{ ColumnId_AiWanderRepeat, "Wander Repeat" },
@ -260,19 +260,56 @@ namespace CSMWorld
{ ColumnId_PartRefList, "Part Reference" },
{ ColumnId_PartRefType, "Type" },
{ ColumnId_PartRefMale, "Male" },
{ ColumnId_PartRefFemale, "Female" },
{ ColumnId_PartRefMale, "Male Part" },
{ ColumnId_PartRefFemale, "Female Part" },
{ ColumnId_LevelledList,"Levelled List" },
{ ColumnId_LevelledItemId,"Item ID" },
{ ColumnId_LevelledItemId,"Levelled Item" },
{ 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_PowerList, "Powers" },
{ ColumnId_SkillImpact, "Skills" },
{ 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_UseValue2, "Use value 2" },
@ -500,6 +537,18 @@ namespace
"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)
{
switch (column)
@ -528,6 +577,11 @@ namespace
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;
case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;
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;
}

@ -257,12 +257,50 @@ namespace CSMWorld
ColumnId_LevelledItemId = 234,
ColumnId_LevelledItemLevel = 235,
ColumnId_LevelledItemType = 236,
ColumnId_LevelledItemChanceNone = 237,
ColumnId_LevelledItemTypeEach = 237,
ColumnId_LevelledItemChanceNone = 238,
ColumnId_PowerList = 238,
ColumnId_SkillImpact = 239, // impact from magic effects
ColumnId_PowerList = 239,
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
// to extend the number of use values.

@ -2,6 +2,7 @@
#include "commanddispatcher.hpp"
#include <algorithm>
#include <memory>
#include <components/misc/stringops.hpp>
@ -10,6 +11,7 @@
#include "idtable.hpp"
#include "record.hpp"
#include "commands.hpp"
#include "idtableproxymodel.hpp"
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,
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)
@ -131,6 +133,54 @@ std::vector<CSMWorld::UniversalId> CSMWorld::CommandDispatcher::getExtendedTypes
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()
{
if (mLocked)
@ -153,7 +203,15 @@ void CSMWorld::CommandDispatcher::executeDelete()
std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
toString().toUtf8().constData();
mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id));
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));
}
if (rows.size()>1)

@ -7,6 +7,9 @@
#include "universalid.hpp"
class QModelIndex;
class QAbstractItemModel;
namespace CSMDoc
{
class Document;
@ -53,6 +56,12 @@ namespace CSMWorld
/// the extended mode, the returned vector will be empty instead.
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:
void executeDelete();

@ -1,5 +1,8 @@
#include "commands.hpp"
#include <cmath>
#include <sstream>
#include <components/misc/stringops.hpp>
#include <QAbstractItemModel>
@ -11,16 +14,28 @@
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
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))
{
// Replace proxy with actual model
mIndex = proxy->mapToSource (index);
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()
@ -32,6 +47,10 @@ void CSMWorld::ModifyCommand::redo()
void CSMWorld::ModifyCommand::undo()
{
mModel->setData (mIndex, mOld);
if (mHasRecordState)
{
mModel->setData(mRecordStateIndex, mOldRecordState);
}
}
@ -103,8 +122,9 @@ void CSMWorld::RevertCommand::undo()
mModel.setRecord (mId, *mOld);
}
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model,
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());
@ -135,7 +155,7 @@ void CSMWorld::DeleteCommand::redo()
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());
}
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,
const std::string& id,
int nestedRow,
int parentColumn,
QUndoCommand* parent) :
mId(id),
QUndoCommand(parent),
NestedTableStoring(model, id, parentColumn),
mModel(model),
mId(id),
mParentColumn(parentColumn),
QUndoCommand(parent),
mNestedRow(nestedRow),
NestedTableStoring(model, id, parentColumn)
mNestedRow(nestedRow)
{
std::string title =
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)
: mModel(model),
: QUndoCommand(parent),
NestedTableStoring(model, id, parentColumn),
mModel(model),
mId(id),
mNewRow(nestedRow),
mParentColumn(parentColumn),
QUndoCommand(parent),
NestedTableStoring(model, id, parentColumn)
mParentColumn(parentColumn)
{
std::string title =
model.headerData(parentColumn, Qt::Horizontal, Qt::DisplayRole).toString().toUtf8().constData();

@ -30,6 +30,10 @@ namespace CSMWorld
QVariant mNew;
QVariant mOld;
bool mHasRecordState;
QModelIndex mRecordStateIndex;
CSMWorld::RecordBase::State mOldRecordState;
public:
ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_,
@ -110,6 +114,7 @@ namespace CSMWorld
IdTable& mModel;
std::string mId;
RecordBase *mOld;
UniversalId::Type mType;
// not implemented
DeleteCommand (const DeleteCommand&);
@ -117,7 +122,8 @@ namespace CSMWorld
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();
@ -141,6 +147,29 @@ namespace CSMWorld
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
{
NestedTableWrapperBase* mOld;

@ -115,7 +115,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
index = mFactions.getColumns()-1;
mFactions.addAdapter (std::make_pair(&mFactions.getColumn(index), new FactionReactionsAdapter ()));
mFactions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_Faction, ColumnBase::Display_String));
new NestedChildColumn (Columns::ColumnId_Faction, ColumnBase::Display_Faction));
mFactions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_FactionReaction, ColumnBase::Display_Integer));
@ -135,7 +135,26 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
index = mRaces.getColumns()-1;
mRaces.addAdapter (std::make_pair(&mRaces.getColumn(index), new SpellListAdapter<ESM::Race> ()));
mRaces.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String));
new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_Spell));
// 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 RecordStateColumn<ESM::Sound>);
@ -161,7 +180,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
index = mRegions.getColumns()-1;
mRegions.addAdapter (std::make_pair(&mRegions.getColumn(index), new RegionSoundListAdapter ()));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SoundName, ColumnBase::Display_String));
new NestedChildColumn (Columns::ColumnId_SoundName, ColumnBase::Display_Sound));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SoundChance, ColumnBase::Display_Integer));
@ -177,7 +196,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mBirthsigns.addAdapter (std::make_pair(&mBirthsigns.getColumn(index),
new SpellListAdapter<ESM::BirthSign> ()));
mBirthsigns.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_String));
new NestedChildColumn (Columns::ColumnId_SpellId, ColumnBase::Display_Spell));
mSpells.addColumn (new StringIdColumn<ESM::Spell>);
mSpells.addColumn (new RecordStateColumn<ESM::Spell>);
@ -242,6 +261,19 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mTopicInfos.addAdapter (std::make_pair(&mTopicInfos.getColumn(index), new InfoListAdapter ()));
mTopicInfos.getNestableColumn(index)->addColumn(
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 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 NameColumn<Cell>);
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_InteriorSky, ESM::Cell::QuasiEx));
mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater,
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 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 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 VampireColumn<ESM::BodyPart>);
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 ModelColumn<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
// WARNING: The order of the columns below are assumed in PathgridPointListAdapter
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(
new NestedChildColumn (Columns::ColumnId_PathgridPosX, ColumnBase::Display_Integer));
mPathgrids.getNestableColumn(index)->addColumn(
@ -355,7 +411,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
index = mPathgrids.getColumns()-1;
mPathgrids.addAdapter (std::make_pair(&mPathgrids.getColumn(index), new PathgridEdgeListAdapter ()));
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(
new NestedChildColumn (Columns::ColumnId_PathgridEdge0, ColumnBase::Display_Integer));
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 RecordStateColumn<CellRef>);
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 PosColumn<CellRef> (&CellRef::mPos, 0, 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),
UniversalId::Type_TopicInfo);
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 IdTable (&mBodyParts), UniversalId::Type_BodyPart);
addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);
@ -457,6 +515,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
UniversalId::Type_Texture);
addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)),
UniversalId::Type_Video);
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
}
CSMWorld::Data::~Data()
@ -778,7 +838,6 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mReader = 0;
mDialogue = 0;
mRefLoadCache.clear();
mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder);
@ -815,7 +874,6 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
mReader = 0;
mDialogue = 0;
mRefLoadCache.clear();
return true;
}
@ -860,9 +918,16 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
case ESM::REC_CELL:
{
mCells.load (*mReader, mBase);
std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (mCells.getSize()-1));
mRefs.load (*mReader, mCells.getSize()-1, mBase, mRefLoadCache[cellId], messages);
int index = mCells.load (*mReader, mBase);
if (index < 0 || index >= mCells.getSize())
{
// 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;
}

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

@ -74,6 +74,15 @@ namespace CSMWorld
{
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);
if (index==-1)

@ -0,0 +1,110 @@
#include "idcompletionmanager.hpp"
#include <boost/make_shared.hpp>
#include <QCompleter>
#include "../../view/widget/completerpopup.hpp"
#include "data.hpp"
#include "idtablebase.hpp"
namespace
{
std::map<CSMWorld::ColumnBase::Display, CSMWorld::UniversalId::Type> generateModelTypes()
{
std::map<CSMWorld::ColumnBase::Display, CSMWorld::UniversalId::Type> types;
types[CSMWorld::ColumnBase::Display_BodyPart ] = CSMWorld::UniversalId::Type_BodyPart;
types[CSMWorld::ColumnBase::Display_Cell ] = CSMWorld::UniversalId::Type_Cell;
types[CSMWorld::ColumnBase::Display_Class ] = CSMWorld::UniversalId::Type_Class;
types[CSMWorld::ColumnBase::Display_CreatureLevelledList] = CSMWorld::UniversalId::Type_Referenceable;
types[CSMWorld::ColumnBase::Display_Creature ] = CSMWorld::UniversalId::Type_Referenceable;
types[CSMWorld::ColumnBase::Display_Enchantment ] = CSMWorld::UniversalId::Type_Enchantment;
types[CSMWorld::ColumnBase::Display_Faction ] = CSMWorld::UniversalId::Type_Faction;
types[CSMWorld::ColumnBase::Display_GlobalVariable ] = CSMWorld::UniversalId::Type_Global;
types[CSMWorld::ColumnBase::Display_Icon ] = CSMWorld::UniversalId::Type_Icon;
types[CSMWorld::ColumnBase::Display_Mesh ] = CSMWorld::UniversalId::Type_Mesh;
types[CSMWorld::ColumnBase::Display_Miscellaneous ] = CSMWorld::UniversalId::Type_Referenceable;
types[CSMWorld::ColumnBase::Display_Npc ] = CSMWorld::UniversalId::Type_Referenceable;
types[CSMWorld::ColumnBase::Display_Race ] = CSMWorld::UniversalId::Type_Race;
types[CSMWorld::ColumnBase::Display_Region ] = CSMWorld::UniversalId::Type_Region;
types[CSMWorld::ColumnBase::Display_Referenceable ] = CSMWorld::UniversalId::Type_Referenceable;
types[CSMWorld::ColumnBase::Display_Script ] = CSMWorld::UniversalId::Type_Script;
types[CSMWorld::ColumnBase::Display_Skill ] = CSMWorld::UniversalId::Type_Skill;
types[CSMWorld::ColumnBase::Display_Sound ] = CSMWorld::UniversalId::Type_Sound;
types[CSMWorld::ColumnBase::Display_SoundRes ] = CSMWorld::UniversalId::Type_SoundRes;
types[CSMWorld::ColumnBase::Display_Spell ] = CSMWorld::UniversalId::Type_Spell;
types[CSMWorld::ColumnBase::Display_Static ] = CSMWorld::UniversalId::Type_Referenceable;
types[CSMWorld::ColumnBase::Display_Texture ] = CSMWorld::UniversalId::Type_Texture;
types[CSMWorld::ColumnBase::Display_Weapon ] = CSMWorld::UniversalId::Type_Referenceable;
return types;
}
typedef std::map<CSMWorld::ColumnBase::Display,
CSMWorld::UniversalId::Type>::const_iterator ModelTypeConstIterator;
}
const std::map<CSMWorld::ColumnBase::Display, CSMWorld::UniversalId::Type>
CSMWorld::IdCompletionManager::sCompleterModelTypes = generateModelTypes();
std::vector<CSMWorld::ColumnBase::Display> CSMWorld::IdCompletionManager::getDisplayTypes()
{
std::vector<CSMWorld::ColumnBase::Display> types;
ModelTypeConstIterator current = sCompleterModelTypes.begin();
ModelTypeConstIterator end = sCompleterModelTypes.end();
for (; current != end; ++current)
{
types.push_back(current->first);
}
return types;
}
CSMWorld::IdCompletionManager::IdCompletionManager(CSMWorld::Data &data)
{
generateCompleters(data);
}
bool CSMWorld::IdCompletionManager::hasCompleterFor(CSMWorld::ColumnBase::Display display) const
{
return mCompleters.find(display) != mCompleters.end();
}
boost::shared_ptr<QCompleter> CSMWorld::IdCompletionManager::getCompleter(CSMWorld::ColumnBase::Display display)
{
if (!hasCompleterFor(display))
{
throw std::logic_error("This column doesn't have an ID completer");
}
return mCompleters[display];
}
void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data)
{
ModelTypeConstIterator current = sCompleterModelTypes.begin();
ModelTypeConstIterator end = sCompleterModelTypes.end();
for (; current != end; ++current)
{
QAbstractItemModel *model = data.getTableModel(current->second);
CSMWorld::IdTableBase *table = dynamic_cast<CSMWorld::IdTableBase *>(model);
if (table != NULL)
{
int idColumn = table->searchColumnIndex(CSMWorld::Columns::ColumnId_Id);
if (idColumn != -1)
{
boost::shared_ptr<QCompleter> completer = boost::make_shared<QCompleter>(table);
completer->setCompletionColumn(idColumn);
// The completion role must be Qt::DisplayRole to get the ID values from the model
completer->setCompletionRole(Qt::DisplayRole);
completer->setCaseSensitivity(Qt::CaseInsensitive);
QAbstractItemView *popup = new CSVWidget::CompleterPopup();
completer->setPopup(popup); // The completer takes ownership of the popup
completer->setMaxVisibleItems(10);
mCompleters[current->first] = completer;
}
}
}
}

@ -0,0 +1,41 @@
#ifndef CSM_WORLD_IDCOMPLETIONMANAGER_HPP
#define CSM_WORLD_IDCOMPLETIONMANAGER_HPP
#include <vector>
#include <map>
#include <boost/shared_ptr.hpp>
#include "columnbase.hpp"
#include "universalid.hpp"
class QCompleter;
namespace CSMWorld
{
class Data;
/// \brief Creates and stores all ID completers
class IdCompletionManager
{
static const std::map<ColumnBase::Display, UniversalId::Type> sCompleterModelTypes;
std::map<ColumnBase::Display, boost::shared_ptr<QCompleter> > mCompleters;
// Don't allow copying
IdCompletionManager(const IdCompletionManager &);
IdCompletionManager &operator = (const IdCompletionManager &);
void generateCompleters(Data &data);
public:
static std::vector<ColumnBase::Display> getDisplayTypes();
IdCompletionManager(Data &data);
bool hasCompleterFor(ColumnBase::Display display) const;
boost::shared_ptr<QCompleter> getCompleter(ColumnBase::Display display);
};
}
#endif

@ -28,9 +28,18 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const
return mIdCollection->getColumns();
}
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_Display)
return QVariant(mIdCollection->getColumn(index.column()).mDisplayType);
if (role==ColumnBase::Role_ColumnId)
return QVariant (getColumnId (index.column()));
if ((role!=Qt::DisplayRole && role!=Qt::EditRole))
return QVariant();
if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable())
@ -56,6 +65,9 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation
if (role==ColumnBase::Role_Display)
return mIdCollection->getColumn (section).mDisplayType;
if (role==ColumnBase::Role_ColumnId)
return getColumnId (section);
return QVariant();
}
@ -65,8 +77,7 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
{
mIdCollection->setData (index.row(), index.column(), value);
emit dataChanged (CSMWorld::IdTable::index (index.row(), 0),
CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1));
emit dataChanged (index, index);
return true;
}
@ -76,6 +87,9 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const
{
if (!index.isValid())
return 0;
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
if (mIdCollection->getColumn (index.column()).isUserEditable())
@ -145,17 +159,17 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int 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);
if (index==-1)
{
int index = mIdCollection->getAppendIndex (id);
int index = mIdCollection->getAppendIndex (id, type);
beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendRecord (record);
mIdCollection->appendRecord (record, type);
endInsertRows();
}

@ -59,7 +59,8 @@ namespace CSMWorld
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.
const RecordBase& getRecord (const std::string& id) const;

@ -21,6 +21,18 @@ void CSMWorld::IdTableProxyModel::updateColumnMap()
}
}
bool CSMWorld::IdTableProxyModel::filterAcceptsColumn (int sourceColumn, const QModelIndex& sourceParent)
const
{
int flags =
sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();
if (flags & CSMWorld::ColumnBase::Flag_Table)
return true;
else
return false;
}
bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent)
const
{
@ -46,10 +58,16 @@ void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::
{
mFilter = filter;
updateColumnMap();
invalidateFilter();
reset();
}
bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
return QSortFilterProxyModel::lessThan(left, right);
}
void CSMWorld::IdTableProxyModel::refreshFilter()
{
updateColumnMap();
invalidateFilter();
}

@ -24,8 +24,6 @@ namespace CSMWorld
void updateColumnMap();
bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const;
public:
IdTableProxyModel (QObject *parent = 0);
@ -34,9 +32,15 @@ namespace CSMWorld
void setFilter (const boost::shared_ptr<CSMFilter::Node>& filter);
void refreshFilter();
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
virtual bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const;
virtual bool filterAcceptsColumn (int sourceColumn, const QModelIndex& sourceParent) const;
};
}

@ -35,28 +35,29 @@ QVariant CSMWorld::IdTree::data (const QModelIndex & index, int role) const
if (!index.isValid())
return QVariant();
if ((role!=Qt::DisplayRole && role!=Qt::EditRole) || index.row() < 0 || index.column() < 0)
return QVariant();
if (index.internalId() != 0)
{
std::pair<int, int> parentAddress(unfoldIndexAddress(index.internalId()));
const NestableColumn *parentColumn = mNestedCollection->getNestableColumn(parentAddress.second);
if (role == Qt::EditRole &&
!mNestedCollection->getNestableColumn(parentAddress.second)->nestedColumn(index.column()).isEditable())
{
if (role == ColumnBase::Role_Display)
return parentColumn->nestedColumn(index.column()).mDisplayType;
if (role == ColumnBase::Role_ColumnId)
return parentColumn->nestedColumn(index.column()).mColumnId;
if (role == Qt::EditRole && !parentColumn->nestedColumn(index.column()).isEditable())
return QVariant();
if (role != Qt::DisplayRole && role != Qt::EditRole)
return QVariant();
}
return mNestedCollection->getNestedData(parentAddress.first,
parentAddress.second, index.row(), index.column());
}
else
{
if (role==Qt::EditRole && !idCollection()->getColumn (index.column()).isEditable())
return QVariant();
return idCollection()->getData (index.row(), index.column());
return IdTable::data(index, role);
}
}
@ -74,11 +75,14 @@ QVariant CSMWorld::IdTree::nestedHeaderData(int section, int subSection, Qt::Ori
return tr(parentColumn->nestedColumn(subSection).getTitle().c_str());
if (role==ColumnBase::Role_Flags)
return idCollection()->getColumn (section).mFlags;
return parentColumn->nestedColumn(subSection).mFlags;
if (role==ColumnBase::Role_Display)
return parentColumn->nestedColumn(subSection).mDisplayType;
if (role==ColumnBase::Role_ColumnId)
return parentColumn->nestedColumn(subSection).mColumnId;
return QVariant();
}
@ -92,8 +96,8 @@ bool CSMWorld::IdTree::setData (const QModelIndex &index, const QVariant &value,
mNestedCollection->setNestedData(parentAddress.first, parentAddress.second, value, index.row(), index.column());
emit dataChanged (CSMWorld::IdTree::index (parentAddress.first, 0),
CSMWorld::IdTree::index (parentAddress.first, idCollection()->getColumns()-1));
emit dataChanged (index, index);
return true;
}
else

@ -173,6 +173,17 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s
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
RecordConstIterator end = begin;

@ -487,7 +487,7 @@ namespace CSMWorld
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,
@ -534,4 +534,667 @@ namespace CSMWorld
{
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
}
}

@ -7,9 +7,11 @@
#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/attr.hpp> // for converting attributes
#include <components/esm/loadrace.hpp>
#include "nestedcolumnadapter.hpp"
#include "nestedtablewrapper.hpp"
#include "cell.hpp"
namespace ESM
{
@ -295,7 +297,7 @@ namespace CSMWorld
case 0:
{
if (effect.mEffectID >=0 && effect.mEffectID < ESM::MagicEffect::Length)
return effect.mRange;
return effect.mEffectID;
else
throw std::runtime_error("Magic effects ID unexpected value");
}
@ -396,6 +398,106 @@ namespace CSMWorld
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

@ -1,8 +1,18 @@
#include "ref.hpp"
#include <cmath>
CSMWorld::CellRef::CellRef()
{
mRefNum.mIndex = 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));
}

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

@ -2,8 +2,10 @@
#include "refcollection.hpp"
#include <sstream>
#include <iostream>
#include <components/misc/stringops.hpp>
#include <components/esm/loadcell.hpp>
#include "ref.hpp"
#include "cell.hpp"
@ -20,14 +22,75 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CellRef ref;
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))
{
ref.mCell = cell2.mId;
// 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 : "";
/// \todo handle moved references
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;
std::map<ESM::RefNum, std::string>::iterator iter = cache.find (ref.mRefNum);
// ignore content file number
std::map<ESM::RefNum, std::string>::iterator iter = cache.begin();
for (; iter != cache.end(); ++iter)
{
if (ref.mRefNum.mIndex == iter->first.mIndex)
break;
}
if (deleted)
{

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

@ -13,7 +13,7 @@ CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns)
CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const PotionColumns& columns,
const RefIdColumn *autoCalc)
: 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,
@ -468,7 +468,10 @@ CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns)
mClass(NULL),
mFaction(NULL),
mHair(NULL),
mHead(NULL)
mHead(NULL),
mAttributes(NULL),
mSkills(NULL),
mMisc(NULL)
{}
CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns)
@ -496,6 +499,17 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re
if (column==mColumns.mHead)
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 =
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)
: EnchantableColumns (columns) {}

@ -2,6 +2,7 @@
#define CSM_WOLRD_REFIDADAPTERIMP_H
#include <map>
#include <iostream>
#include <QVariant>
@ -791,6 +792,9 @@ namespace CSMWorld
const RefIdColumn *mFaction;
const RefIdColumn *mHair;
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);
};
@ -841,8 +845,100 @@ namespace CSMWorld
///< If the data type does not match an exception is thrown.
};
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>
class EffectsListAdapter;
@ -1829,10 +1925,10 @@ namespace CSMWorld
}
// for non-tables
template <typename ESXRecordT>
class NestedListLevListRefIdAdapter : public NestedRefIdAdapterBase
{
UniversalId::Type mType;
// not implemented
@ -1876,31 +1972,27 @@ namespace CSMWorld
const Record<ESXRecordT>& record =
static_cast<const Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
switch (subColIndex)
if (mType == UniversalId::Type_CreatureLevelledList)
{
case 0:
switch (subColIndex)
{
if (mType == CSMWorld::UniversalId::Type_CreatureLevelledList &&
record.get().mFlags == 0x01)
{
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
throw std::runtime_error("unknown leveled list type");
case 0: return QVariant(); // disable the checkbox editor
case 1: return record.get().mFlags & ESM::CreatureLevList::AllLevels;
case 2: return static_cast<int> (record.get().mChanceNone);
default:
throw std::runtime_error("Trying to access non-existing column in levelled creatues!");
}
}
else
{
switch (subColIndex)
{
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:
throw std::runtime_error("Trying to access non-existing column in levelled items!");
}
case 1: return static_cast<int> (record.get().mChanceNone);
default:
throw std::runtime_error("Trying to access non-existing column in the nested table!");
}
}
@ -1911,34 +2003,63 @@ namespace CSMWorld
static_cast<Record<ESXRecordT>&> (data.getRecord (RefIdData::LocalIndex (row, mType)));
ESXRecordT leveled = record.get();
switch(subColIndex)
if (mType == UniversalId::Type_CreatureLevelledList)
{
case 0:
switch(subColIndex)
{
if (mType == CSMWorld::UniversalId::Type_CreatureLevelledList &&
value.toString().toStdString() == "All Levels")
case 0: return; // return without saving
case 1:
{
leveled.mFlags = 0x01;
break;
if(value.toBool())
{
leveled.mFlags |= ESM::CreatureLevList::AllLevels;
break;
}
else
{
leveled.mFlags &= ~ESM::CreatureLevList::AllLevels;
break;
}
}
else if(mType == CSMWorld::UniversalId::Type_ItemLevelledList &&
value.toString().toStdString() == "Each")
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)
{
case 0:
{
leveled.mFlags = 0x01;
break;
if(value.toBool())
{
leveled.mFlags |= ESM::ItemLevList::Each;
break;
}
else
{
leveled.mFlags &= ~ESM::ItemLevList::Each;
break;
}
}
else if(mType == CSMWorld::UniversalId::Type_ItemLevelledList &&
value.toString().toStdString() == "All Levels")
case 1:
{
leveled.mFlags = 0x02;
break;
if(value.toBool())
{
leveled.mFlags |= ESM::ItemLevList::AllLevels;
break;
}
else
{
leveled.mFlags &= ~ESM::ItemLevList::AllLevels;
break;
}
}
else
return; // return without saving
case 2: leveled.mChanceNone = static_cast<unsigned char>(value.toInt()); break;
default:
throw std::runtime_error("Trying to set non-existing column in levelled items!");
}
case 1: leveled.mChanceNone = static_cast<unsigned char>(value.toInt()); break;
default:
throw std::runtime_error("Trying to access non-existing column in the nested table!");
}
record.setModified (leveled);
@ -1946,7 +2067,7 @@ namespace CSMWorld
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
@ -1955,6 +2076,7 @@ namespace CSMWorld
}
};
// for tables
template <typename ESXRecordT>
class NestedLevListRefIdAdapter : public NestedRefIdAdapterBase
{

@ -99,7 +99,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
EnchantableColumns enchantableColumns (inventoryColumns);
mColumns.push_back (RefIdColumn (Columns::ColumnId_Enchantment, ColumnBase::Display_String));
mColumns.push_back (RefIdColumn (Columns::ColumnId_Enchantment, ColumnBase::Display_Enchantment));
enchantableColumns.mEnchantment = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_EnchantmentPoints, ColumnBase::Display_Integer));
enchantableColumns.mEnchantmentPoints = &mColumns.back();
@ -135,7 +135,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
new NestedInventoryRefIdAdapter<ESM::Creature> (UniversalId::Type_Creature)));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), inventoryMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_String));
new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_Referenceable));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_ItemCount, CSMWorld::ColumnBase::Display_Integer));
@ -150,7 +150,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
new NestedSpellRefIdAdapter<ESM::Creature> (UniversalId::Type_Creature)));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), spellsMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_SpellId, CSMWorld::ColumnBase::Display_String));
new RefIdColumn (Columns::ColumnId_SpellId, CSMWorld::ColumnBase::Display_Spell));
// Nested table
mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcDestinations,
@ -163,7 +163,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
new NestedTravelRefIdAdapter<ESM::Creature> (UniversalId::Type_Creature)));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), destMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_DestinationCell, CSMWorld::ColumnBase::Display_String));
new RefIdColumn (Columns::ColumnId_DestinationCell, CSMWorld::ColumnBase::Display_Cell));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_PosX, CSMWorld::ColumnBase::Display_Float));
mColumns.back().addColumn(
@ -245,7 +245,8 @@ CSMWorld::RefIdCollection::RefIdCollection()
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();
mColumns.push_back (RefIdColumn (Columns::ColumnId_ApparatusType,
@ -288,7 +289,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
new NestedInventoryRefIdAdapter<ESM::Container> (UniversalId::Type_Container)));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), contMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_String));
new RefIdColumn (Columns::ColumnId_InventoryItemId, CSMWorld::ColumnBase::Display_Referenceable));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_ItemCount, CSMWorld::ColumnBase::Display_Integer));
@ -300,7 +301,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
creatureColumns.mSoul = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Scale, ColumnBase::Display_Float));
creatureColumns.mScale = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_OriginalCreature, ColumnBase::Display_String));
mColumns.push_back (RefIdColumn (Columns::ColumnId_OriginalCreature, ColumnBase::Display_Creature));
creatureColumns.mOriginal = &mColumns.back();
mColumns.push_back (
RefIdColumn (Columns::ColumnId_CombatState, ColumnBase::Display_Integer));
@ -408,10 +409,10 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.push_back (RefIdColumn (Columns::ColumnId_Faction, ColumnBase::Display_Faction));
npcColumns.mFaction = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::Columnid_Hair, ColumnBase::Display_String));
mColumns.push_back (RefIdColumn (Columns::Columnid_Hair, ColumnBase::Display_BodyPart));
npcColumns.mHair = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Head, ColumnBase::Display_String));
mColumns.push_back (RefIdColumn (Columns::ColumnId_Head, ColumnBase::Display_BodyPart));
npcColumns.mHead = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Female, ColumnBase::Display_Boolean));
@ -427,6 +428,62 @@ CSMWorld::RefIdCollection::RefIdCollection()
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);
mColumns.push_back (RefIdColumn (Columns::ColumnId_WeaponType, ColumnBase::Display_WeaponType));
@ -482,9 +539,9 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_PartRefType, CSMWorld::ColumnBase::Display_PartRefType));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_PartRefMale, CSMWorld::ColumnBase::Display_String));
new RefIdColumn (Columns::ColumnId_PartRefMale, CSMWorld::ColumnBase::Display_BodyPart));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_PartRefFemale, CSMWorld::ColumnBase::Display_String));
new RefIdColumn (Columns::ColumnId_PartRefFemale, CSMWorld::ColumnBase::Display_BodyPart));
LevListColumns levListColumns (baseColumns);
@ -499,7 +556,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
new NestedLevListRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList)));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), levListMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_LevelledItemId, CSMWorld::ColumnBase::Display_String));
new RefIdColumn (Columns::ColumnId_LevelledItemId, CSMWorld::ColumnBase::Display_Referenceable));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_LevelledItemLevel, CSMWorld::ColumnBase::Display_Integer));
@ -514,7 +571,9 @@ CSMWorld::RefIdCollection::RefIdCollection()
new NestedListLevListRefIdAdapter<ESM::ItemLevList> (UniversalId::Type_ItemLevelledList)));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), nestedListLevListMap));
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(
new RefIdColumn (Columns::ColumnId_LevelledItemChanceNone, CSMWorld::ColumnBase::Display_Integer));

@ -176,7 +176,6 @@ void CSMWorld::RefIdData::erase (const LocalIndex& index, int count)
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (index.second);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
@ -189,6 +188,20 @@ void CSMWorld::RefIdData::erase (const LocalIndex& index, int count)
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);
}

@ -130,7 +130,7 @@ namespace CSMWorld
template<typename RecordT>
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");
mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count);

@ -38,9 +38,9 @@ namespace
{ 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_Referenceables,
"Referenceables", 0 },
"Objects", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References,
"References", 0 },
"Instances", 0 },
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap,
"Region Map", 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_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_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_Potion, "Potion", ":./potion.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_Static, "Static", ":./static.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", 0 },
{ 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_Preview, "Preview", 0 },

@ -18,7 +18,7 @@
#include "adjusterwidget.hpp"
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);
resize(400, 400);

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

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

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

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

@ -2,6 +2,24 @@
#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)
: mUniversalId (id)
{

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

@ -9,6 +9,10 @@
#include <QDockWidget>
#include <QtGui/QApplication>
#include <QDesktopWidget>
#include <QScrollArea>
#include <QHBoxLayout>
#include <QDesktopWidget>
#include <QScrollBar>
#include "../../model/doc/document.hpp"
#include "../../model/settings/usersettings.hpp"
@ -16,6 +20,7 @@
#include "../../model/world/idtable.hpp"
#include "../world/subviews.hpp"
#include "../world/tablesubview.hpp"
#include "../tools/subviews.hpp"
@ -131,11 +136,11 @@ void CSVDoc::View::setupWorldMenu()
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
world->addAction (cells);
QAction *referenceables = new QAction (tr ("Referenceables"), this);
QAction *referenceables = new QAction (tr ("Objects"), this);
connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView()));
world->addAction (referenceables);
QAction *references = new QAction (tr ("References"), this);
QAction *references = new QAction (tr ("Instances"), this);
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
world->addAction (references);
@ -334,8 +339,15 @@ void CSVDoc::View::updateTitle()
void CSVDoc::View::updateSubViewIndicies(SubView *view)
{
if(view && mSubViews.contains(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();
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)
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1),
mViewTotal (totalViews)
mViewTotal (totalViews), mScroll(0), mScrollbarOnly(false)
{
int width = CSMSettings::UserSettings::instance().settingValue
("window/default-width").toInt();
@ -392,15 +404,22 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
width = std::max(width, 300);
height = std::max(height, 300);
// trick to get the window decorations and their sizes
show();
hide();
resize (width - (frameGeometry().width() - geometry().width()),
height - (frameGeometry().height() - geometry().height()));
resize (width, height);
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);
setCentralWidget (&mSubViewWindow);
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
if(settings.settingValue ("window/mainwindow-scrollbar") == "Grow Only")
{
setCentralWidget (&mSubViewWindow);
}
else
{
mScroll = new QScrollArea(this);
mScroll->setWidgetResizable(true);
mScroll->setWidget(&mSubViewWindow);
setCentralWidget(mScroll);
}
mOperations = new Operations;
addDockWidget (Qt::BottomDockWidgetArea, mOperations);
@ -527,6 +546,54 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
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);
updateSubViewIndicies();
@ -774,6 +841,48 @@ void CSVDoc::View::updateUserSetting (const QString &name, const QStringList &li
{
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)
@ -811,7 +920,33 @@ void CSVDoc::View::closeRequest (SubView *subView)
if (mSubViews.size()>1 || mViewTotal<=1 ||
userSettings.setting ("window/hide-subview", QString ("false"))!="true")
{
subView->deleteLater();
mSubViews.removeOne (subView);
}
else if (mViewManager.closeRequest (this))
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);
}

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

@ -1,6 +1,7 @@
#include "viewmanager.hpp"
#include <vector>
#include <map>
#include <QApplication>
@ -10,12 +11,14 @@
#include "../../model/doc/document.hpp"
#include "../../model/world/columns.hpp"
#include "../../model/world/universalid.hpp"
#include "../../model/world/idcompletionmanager.hpp"
#include "../world/util.hpp"
#include "../world/enumdelegate.hpp"
#include "../world/vartypedelegate.hpp"
#include "../world/recordstatusdelegate.hpp"
#include "../world/idtypedelegate.hpp"
#include "../world/idcompletiondelegate.hpp"
#include "../../model/settings/usersettings.hpp"
@ -60,6 +63,14 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType,
new CSVWorld::IdTypeDelegateFactory());
std::vector<CSMWorld::ColumnBase::Display> idCompletionColumns = CSMWorld::IdCompletionManager::getDisplayTypes();
for (std::vector<CSMWorld::ColumnBase::Display>::const_iterator current = idCompletionColumns.begin();
current != idCompletionColumns.end();
++current)
{
mDelegateFactories->add(*current, new CSVWorld::IdCompletionDelegateFactory());
}
struct Mapping
{
CSMWorld::ColumnBase::Display mDisplay;
@ -90,7 +101,10 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_EffectId, CSMWorld::Columns::ColumnId_EffectId, false },
{ CSMWorld::ColumnBase::Display_PartRefType, CSMWorld::Columns::ColumnId_PartRefType, 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)

@ -123,7 +123,6 @@ CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneMana
: mDocument (document), mId (Misc::StringUtils::lowerCase (id))
, mProxyModel(0), mModel(0), mPgIndex(-1), mHandler(new CSMWorld::SignalHandler(this))
, mPhysics(physics), mSceneMgr(sceneManager), mX(0), mY(0)
{
mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
mCellNode->setPosition (origin);

@ -223,12 +223,10 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
{
mReferenceableId =
references.getData (index, columnIndex).toString().toUtf8().constData();
update();
}
update();
adjust();
return true;
}

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

@ -23,7 +23,7 @@
#include "editmode.hpp"
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)
{
setAcceptDrops(true);
@ -265,7 +265,7 @@ unsigned int CSVRender::WorldspaceWidget::getInteractionMask() const
void CSVRender::WorldspaceWidget::addVisibilitySelectorButtons (
CSVWidget::SceneToolToggle2 *tool)
{
tool->addButton (Element_Reference, "References");
tool->addButton (Element_Reference, "Instances");
tool->addButton (Element_Water, "Water");
tool->addButton (Element_Pathgrid, "Pathgrid");
}
@ -274,7 +274,7 @@ void CSVRender::WorldspaceWidget::addEditModeSelectorButtons (CSVWidget::SceneTo
{
/// \todo replace EditMode with suitable subclasses
tool->addButton (
new EditMode (this, QIcon (":placeholder"), Element_Reference, "Reference editing"),
new EditMode (this, QIcon (":placeholder"), Element_Reference, "Instance editing"),
"object");
tool->addButton (
new EditMode (this, QIcon (":placeholder"), Element_Pathgrid, "Pathgrid editing"),

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

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

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

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

@ -19,7 +19,7 @@ QMap <CSVSettings::ViewType, CSVSettings::IViewFactory *>
CSVSettings::Page::Page (const QString &pageName, QList <CSMSettings::Setting *> settingList,
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);

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

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

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

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

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

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

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

@ -7,6 +7,7 @@
#include "../../model/tools/search.hpp"
#include "../../model/tools/reportmodel.hpp"
#include "../../model/world/idtablebase.hpp"
#include "../../model/settings/usersettings.hpp"
#include "reporttable.hpp"
#include "searchbox.hpp"
@ -22,7 +23,13 @@ void CSVTools::SearchSubView::replace (bool selection)
const CSMTools::ReportModel& 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
// in a single string.
for (std::vector<int>::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter)
@ -33,17 +40,34 @@ void CSVTools::SearchSubView::replace (bool selection)
CSMWorld::IdTableBase *table = &dynamic_cast<CSMWorld::IdTableBase&> (
*mDocument.getData().getTableModel (type));
if (table!=currentTable)
{
search.configure (table);
currentTable = table;
}
std::string hint = model.getHint (*iter);
mSearch.replace (mDocument, table, id, hint, replace);
mTable->flagAsReplaced (*iter);
if (search.verify (mDocument, table, id, hint))
{
search.replace (mDocument, table, id, hint, replace);
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)
: CSVDoc::SubView (id), mDocument (document), mPaddingBefore (10), mPaddingAfter (10),
mLocked (false)
: CSVDoc::SubView (id), mDocument (document), mLocked (false)
{
QVBoxLayout *layout = new QVBoxLayout;
@ -65,7 +89,7 @@ CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc:
SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)));
connect (mTable, SIGNAL (replaceRequest()), this, SLOT (replaceRequest()));
connect (&document, SIGNAL (stateChanged (int, CSMDoc::Document *)),
this, SLOT (stateChanged (int, CSMDoc::Document *)));
@ -84,14 +108,6 @@ void CSVTools::SearchSubView::setEditLock (bool locked)
void CSVTools::SearchSubView::updateUserSetting (const QString &name, const QStringList &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)
@ -101,8 +117,13 @@ void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *documen
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.setPadding (mPaddingBefore, mPaddingAfter);
mSearch.setPadding (paddingBefore, paddingAfter);
mTable->clear();
mDocument.runSearch (getUniversalId(), mSearch);

@ -26,14 +26,16 @@ namespace CSVTools
ReportTable *mTable;
SearchBox mSearchBox;
CSMDoc::Document& mDocument;
int mPaddingBefore;
int mPaddingAfter;
CSMTools::Search mSearch;
bool mLocked;
private:
void replace (bool selection);
protected:
void showEvent (QShowEvent *event);
public:

@ -0,0 +1,28 @@
#include "completerpopup.hpp"
CSVWidget::CompleterPopup::CompleterPopup(QWidget *parent)
: QListView(parent)
{
setEditTriggers(QAbstractItemView::NoEditTriggers);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::SingleSelection);
}
int CSVWidget::CompleterPopup::sizeHintForRow(int row) const
{
if (model() == NULL)
{
return -1;
}
if (row < 0 || row >= model()->rowCount())
{
return -1;
}
ensurePolished();
QModelIndex index = model()->index(row, modelColumn());
QStyleOptionViewItem option = viewOptions();
QAbstractItemDelegate *delegate = itemDelegate(index);
return delegate->sizeHint(option, index).height();
}

@ -0,0 +1,17 @@
#ifndef CSV_WIDGET_COMPLETERPOPUP_HPP
#define CSV_WIDGET_COMPLETERPOPUP_HPP
#include <QListView>
namespace CSVWidget
{
class CompleterPopup : public QListView
{
public:
CompleterPopup(QWidget *parent = 0);
virtual int sizeHintForRow(int row) const;
};
}
#endif

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

@ -6,11 +6,12 @@
CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values,
const IconList &icons,
CSMWorld::CommandDispatcher *dispatcher,
CSMDoc::Document& document,
const QString &pageName,
const QString &settingName,
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),
mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName)
{
@ -136,9 +137,9 @@ void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, QString enumName,
}
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);
}

@ -38,12 +38,9 @@ namespace CSVWorld
QString mSettingKey;
public:
explicit DataDisplayDelegate (const ValueList & values,
const IconList & icons,
CSMDoc::Document& document,
const QString &pageName,
const QString &settingName,
QObject *parent);
DataDisplayDelegate (const ValueList & values, const IconList & icons,
CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document,
const QString &pageName, const QString &settingName, QObject *parent);
~DataDisplayDelegate();
@ -82,7 +79,7 @@ namespace CSVWorld
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.
protected:

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

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

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

Loading…
Cancel
Save