1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-31 14:36:39 +00:00

Merge pull request #7 from cc9cii/master-cherry-pick

Merge master (cherry pick)
This commit is contained in:
cc9cii 2015-07-30 14:21:08 +10:00
commit 2e6a8e1b18
57 changed files with 1903 additions and 294 deletions

View file

@ -59,6 +59,7 @@ Programmers
Julien Voisin (jvoisin/ap0) Julien Voisin (jvoisin/ap0)
Karl-Felix Glatzer (k1ll) Karl-Felix Glatzer (k1ll)
Kevin Poitra (PuppyKevin) Kevin Poitra (PuppyKevin)
Koncord
Lars Söderberg (Lazaroth) Lars Söderberg (Lazaroth)
lazydev lazydev
Leon Saunders (emoose) Leon Saunders (emoose)

View file

@ -421,6 +421,9 @@ IF(NOT WIN32 AND NOT APPLE)
IF(BUILD_ESMTOOL) IF(BUILD_ESMTOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL) ENDIF(BUILD_ESMTOOL)
IF(BUILD_NIFTEST)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
ENDIF(BUILD_NIFTEST)
IF(BUILD_MWINIIMPORTER) IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER) ENDIF(BUILD_MWINIIMPORTER)
@ -617,6 +620,10 @@ if (BUILD_WIZARD)
add_subdirectory(apps/wizard) add_subdirectory(apps/wizard)
endif() endif()
if (BUILD_NIFTEST)
add_subdirectory(apps/niftest)
endif(BUILD_NIFTEST)
# UnitTests # UnitTests
if (BUILD_UNITTESTS) if (BUILD_UNITTESTS)
add_subdirectory( apps/openmw_test_suite ) add_subdirectory( apps/openmw_test_suite )

View file

@ -309,11 +309,11 @@ bool Launcher::MainDialog::setupGameSettings()
mGameSettings.readUserFile(stream); mGameSettings.readUserFile(stream);
} }
// Now the rest // Now the rest - priority: user > local > global
QStringList paths; QStringList paths;
paths.append(userPath + QString("openmw.cfg"));
paths.append(QString("openmw.cfg"));
paths.append(globalPath + QString("openmw.cfg")); paths.append(globalPath + QString("openmw.cfg"));
paths.append(QString("openmw.cfg"));
paths.append(userPath + QString("openmw.cfg"));
foreach (const QString &path, paths) { foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path); qDebug() << "Loading config file:" << qPrintable(path);

View file

@ -0,0 +1,19 @@
set(NIFTEST
niftest.cpp
)
source_group(components\\nif\\tests FILES ${NIFTEST})
# Main executable
add_executable(niftest
${NIFTEST}
)
target_link_libraries(niftest
${Boost_FILESYSTEM_LIBRARY}
components
)
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(niftest gcov)
endif()

28
apps/niftest/find_bad_nifs.sh Executable file
View file

@ -0,0 +1,28 @@
#!/bin/bash
#Script to test all nif files (both loose, and in BSA archives) in data files directory
#The user input as an absolute path
DATAFILESDIR="`readlink -m "$1"`"
#Program used to test
TEST_PROG="`pwd`/niftest"
#Make sure our files don't bother anyone
NIFTEST_TMP_DIR="/tmp/niftest_$RANDOM/"
mkdir "$NIFTEST_TMP_DIR"
cd "$NIFTEST_TMP_DIR"
find "$DATAFILESDIR" -iname *bsa > nifs.txt
find "$DATAFILESDIR" -iname *nif >> nifs.txt
sed -e 's/.*/\"&\"/' nifs.txt > quoted_nifs.txt
xargs --arg-file=quoted_nifs.txt "$TEST_PROG" 2>&1 | tee nif_out.txt
# xargs --arg-file=quoted_nifs.txt "$TEST_PROG" 2> nif_out.txt >/dev/null
echo "List of bad NIF Files:"
cat nif_out.txt|grep File:|cut -d ' ' -f 2-
rm nifs.txt
rm quoted_nifs.txt
rm nif_out.txt
rmdir "$NIFTEST_TMP_DIR"

87
apps/niftest/niftest.cpp Normal file
View file

@ -0,0 +1,87 @@
///Program to test .nif files both on the FileSystem and in BSA archives.
#include <iostream>
#include <fstream>
#include <components/nif/niffile.hpp>
#include <components/files/constrainedfilestream.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/bsaarchive.hpp>
///See if the file has the named extension
bool hasExtension(std::string filename, std::string extensionToFind)
{
std::string extension = filename.substr(filename.find_last_of(".")+1);
//Convert strings to lower case for comparison
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower);
if(extension == extensionToFind)
return true;
else
return false;
}
///See if the file has the "nif" extension.
bool isNIF(std::string filename)
{
return hasExtension(filename,"nif");
}
///See if the file has the "bsa" extension.
bool isBSA(std::string filename)
{
return hasExtension(filename,"bsa");
}
///Check all the nif files in the given BSA archive
void readBSA(std::string filename)
{
VFS::Manager myManager(false);
myManager.addArchive(new VFS::BsaArchive(filename));
myManager.buildIndex();
std::map<std::string, VFS::File*> files=myManager.getIndex();
for(std::map<std::string, VFS::File*>::const_iterator it=files.begin(); it!=files.end(); ++it)
{
std::string name = it->first;
if(isNIF(name))
{
// std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile temp_nif(myManager.get(name),name);
}
}
}
int main(int argc, char **argv)
{
std::cout << "Reading Files" << std::endl;
for(int i = 1; i<argc;i++)
{
std::string name = argv[i];
try{
if(isNIF(name))
{
//std::cout << "Decoding: " << name << std::endl;
Nif::NIFFile temp_nif(Files::openConstrainedFileStream(name.c_str()),name);
}
else if(isBSA(name))
{
std::cout << "Reading BSA File: " << name << std::endl;
readBSA(name);
}
else
{
std::cerr << "ERROR: \"" << name << "\" is not a nif or bsa file!" << std::endl;
}
}
catch (std::exception& e)
{
std::cerr << "ERROR, an exception has occurred: " << e.what() << std::endl;
}
}
return 0;
}

View file

@ -64,7 +64,7 @@ opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview cellcreator referenceablecreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world

View file

@ -799,9 +799,9 @@ void CSMDoc::Document::addGmsts()
"sBookSkillMessage", "sBookSkillMessage",
"sBounty", "sBounty",
"sBreath", "sBreath",
"sBribe", "sBribe 10 Gold",
"sBribe", "sBribe 100 Gold",
"sBribe", "sBribe 1000 Gold",
"sBribeFail", "sBribeFail",
"sBribeSuccess", "sBribeSuccess",
"sBuy", "sBuy",

View file

@ -8,6 +8,20 @@ CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& me
: mId (id), mMessage (message), mHint (hint), mSeverity (severity) : mId (id), mMessage (message), mHint (hint), mSeverity (severity)
{} {}
std::string CSMDoc::Message::toString (Severity severity)
{
switch (severity)
{
case CSMDoc::Message::Severity_Info: return "Information";
case CSMDoc::Message::Severity_Warning: return "Warning";
case CSMDoc::Message::Severity_Error: return "Error";
case CSMDoc::Message::Severity_SeriousError: return "Serious Error";
case CSMDoc::Message::Severity_Default: break;
}
return "";
}
CSMDoc::Messages::Messages (Message::Severity default_) CSMDoc::Messages::Messages (Message::Severity default_)
: mDefault (default_) : mDefault (default_)

View file

@ -31,6 +31,8 @@ namespace CSMDoc
Message (const CSMWorld::UniversalId& id, const std::string& message, Message (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint, Severity severity); const std::string& hint, Severity severity);
static std::string toString (Severity severity);
}; };
class Messages class Messages

View file

@ -189,7 +189,7 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
ritd->setDeclaredValues (values); ritd->setDeclaredValues (values);
} }
declareSection ("table-input", "Table Input"); declareSection ("table-input", "ID Tables");
{ {
QString inPlaceEdit ("Edit in Place"); QString inPlaceEdit ("Edit in Place");
QString editRecord ("Edit Record"); QString editRecord ("Edit Record");
@ -242,9 +242,24 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"Jump to the added or cloned record."); "Jump to the added or cloned record.");
jumpToAdded->setDefaultValue (defaultValue); jumpToAdded->setDefaultValue (defaultValue);
jumpToAdded->setDeclaredValues (jumpValues); jumpToAdded->setDeclaredValues (jumpValues);
Setting *extendedConfig = createSetting (Type_CheckBox, "extended-config",
"Manually specify affected record types for an extended delete/revert");
extendedConfig->setDefaultValue("false");
extendedConfig->setToolTip("Delete and revert commands have an extended form that also affects "
"associated records.\n\n"
"If this option is enabled, types of affected records are selected "
"manually before a command execution.\nOtherwise, all associated "
"records are deleted/reverted immediately.");
} }
declareSection ("report-input", "Report Input"); declareSection ("dialogues", "ID Dialogues");
{
Setting *toolbar = createSetting (Type_CheckBox, "toolbar", "Show toolbar");
toolbar->setDefaultValue ("true");
}
declareSection ("report-input", "Reports");
{ {
QString none ("None"); QString none ("None");
QString edit ("Edit"); QString edit ("Edit");
@ -337,6 +352,15 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
"<li>Strict: Promote warning to an error</li>" "<li>Strict: Promote warning to an error</li>"
"</ul>"); "</ul>");
Setting *toolbar = createSetting (Type_CheckBox, "toolbar", "Show toolbar");
toolbar->setDefaultValue ("true");
Setting *delay = createSetting (Type_SpinBox, "compile-delay",
"Delay between updating of source errors");
delay->setDefaultValue (100);
delay->setRange (0, 10000);
delay->setToolTip ("Delay in milliseconds");
Setting *formatInt = createSetting (Type_LineEdit, "colour-int", "Highlight Colour: Int"); Setting *formatInt = createSetting (Type_LineEdit, "colour-int", "Highlight Colour: Int");
formatInt->setDefaultValues (QStringList() << "Dark magenta"); formatInt->setDefaultValues (QStringList() << "Dark magenta");
formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip); formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip);

View file

@ -85,14 +85,8 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
if (index.column()==mColumnSeverity) if (index.column()==mColumnSeverity)
{ {
switch (mRows.at (index.row()).mSeverity) return QString::fromUtf8 (
{ CSMDoc::Message::toString (mRows.at (index.row()).mSeverity).c_str());
case CSMDoc::Message::Severity_Info: return "Information";
case CSMDoc::Message::Severity_Warning: return "Warning";
case CSMDoc::Message::Severity_Error: return "Error";
case CSMDoc::Message::Severity_SeriousError: return "Serious Error";
case CSMDoc::Message::Severity_Default: break;
}
} }
return QVariant(); return QVariant();

View file

@ -92,7 +92,7 @@ namespace CSMWorld
{ ColumnId_Trainer, "Trainer" }, { ColumnId_Trainer, "Trainer" },
{ ColumnId_Spellmaking, "Spellmaking" }, { ColumnId_Spellmaking, "Spellmaking" },
{ ColumnId_EnchantingService, "Enchanting Service" }, { ColumnId_EnchantingService, "Enchanting Service" },
{ ColumnId_RepairService, "Repair Serivce" }, { ColumnId_RepairService, "Repair Service" },
{ ColumnId_ApparatusType, "Apparatus Type" }, { ColumnId_ApparatusType, "Apparatus Type" },
{ ColumnId_ArmorType, "Armor Type" }, { ColumnId_ArmorType, "Armor Type" },
{ ColumnId_Health, "Health" }, { ColumnId_Health, "Health" },

View file

@ -58,6 +58,25 @@ void CSMWorld::CreateCommand::applyModifications()
{ {
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter)
mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second);
if (!mNestedValues.empty())
{
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel);
if (tree == NULL)
{
throw std::logic_error("CSMWorld::CreateCommand: Attempt to add nested values to the non-nested model");
}
std::map<int, std::pair<int, QVariant> >::const_iterator current = mNestedValues.begin();
std::map<int, std::pair<int, QVariant> >::const_iterator end = mNestedValues.end();
for (; current != end; ++current)
{
QModelIndex index = tree->index(0,
current->second.first,
tree->getNestedModelIndex(mId, current->first));
tree->setData(index, current->second.second);
}
}
} }
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent) CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
@ -71,6 +90,11 @@ void CSMWorld::CreateCommand::addValue (int column, const QVariant& value)
mValues[column] = value; mValues[column] = value;
} }
void CSMWorld::CreateCommand::addNestedValue(int parentColumn, int nestedColumn, const QVariant &value)
{
mNestedValues[parentColumn] = std::make_pair(nestedColumn, value);
}
void CSMWorld::CreateCommand::setType (UniversalId::Type type) void CSMWorld::CreateCommand::setType (UniversalId::Type type)
{ {
mType = type; mType = type;

View file

@ -48,6 +48,9 @@ namespace CSMWorld
class CreateCommand : public QUndoCommand class CreateCommand : public QUndoCommand
{ {
std::map<int, QVariant> mValues; std::map<int, QVariant> mValues;
std::map<int, std::pair<int, QVariant> > mNestedValues;
///< Parameter order: a parent column, a nested column, a data.
///< A nested row has index of 0.
protected: protected:
@ -68,6 +71,8 @@ namespace CSMWorld
void addValue (int column, const QVariant& value); void addValue (int column, const QVariant& value);
void addNestedValue(int parentColumn, int nestedColumn, const QVariant &value);
virtual void redo(); virtual void redo();
virtual void undo(); virtual void undo();

View file

@ -548,6 +548,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mMetaData.addColumn (new StringIdColumn<MetaData> (true)); mMetaData.addColumn (new StringIdColumn<MetaData> (true));
mMetaData.addColumn (new RecordStateColumn<MetaData>); mMetaData.addColumn (new RecordStateColumn<MetaData>);
mMetaData.addColumn (new FixedRecordTypeColumn<MetaData> (UniversalId::Type_MetaData));
mMetaData.addColumn (new FormatColumn<MetaData>); mMetaData.addColumn (new FormatColumn<MetaData>);
mMetaData.addColumn (new AuthorColumn<MetaData>); mMetaData.addColumn (new AuthorColumn<MetaData>);
mMetaData.addColumn (new FileDescriptionColumn<MetaData>); mMetaData.addColumn (new FileDescriptionColumn<MetaData>);

View file

@ -7,23 +7,23 @@
void CSMWorld::IdTableProxyModel::updateColumnMap() void CSMWorld::IdTableProxyModel::updateColumnMap()
{ {
mColumnMap.clear(); Q_ASSERT(mSourceModel != NULL);
mColumnMap.clear();
if (mFilter) if (mFilter)
{ {
std::vector<int> columns = mFilter->getReferencedColumns(); std::vector<int> columns = mFilter->getReferencedColumns();
const IdTableBase& table = dynamic_cast<const IdTableBase&> (*sourceModel());
for (std::vector<int>::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter) for (std::vector<int>::const_iterator iter (columns.begin()); iter!=columns.end(); ++iter)
mColumnMap.insert (std::make_pair (*iter, mColumnMap.insert (std::make_pair (*iter,
table.searchColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (*iter)))); mSourceModel->searchColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (*iter))));
} }
} }
bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent)
const const
{ {
Q_ASSERT(mSourceModel != NULL);
// It is not possible to use filterAcceptsColumn() and check for // It is not possible to use filterAcceptsColumn() and check for
// sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags) // sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags)
// because the sourceColumn parameter excludes the hidden columns, i.e. wrong columns can // because the sourceColumn parameter excludes the hidden columns, i.e. wrong columns can
@ -35,19 +35,40 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI
if (!mFilter) if (!mFilter)
return true; return true;
return mFilter->test ( return mFilter->test (*mSourceModel, sourceRow, mColumnMap);
dynamic_cast<IdTableBase&> (*sourceModel()), sourceRow, mColumnMap);
} }
CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
: QSortFilterProxyModel (parent) : QSortFilterProxyModel (parent),
mSourceModel(NULL)
{ {
setSortCaseSensitivity (Qt::CaseInsensitive); setSortCaseSensitivity (Qt::CaseInsensitive);
} }
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
{ {
return mapFromSource (dynamic_cast<IdTableBase&> (*sourceModel()).getModelIndex (id, column)); Q_ASSERT(mSourceModel != NULL);
return mapFromSource(mSourceModel->getModelIndex (id, column));
}
void CSMWorld::IdTableProxyModel::setSourceModel(QAbstractItemModel *model)
{
QSortFilterProxyModel::setSourceModel(model);
mSourceModel = dynamic_cast<IdTableBase *>(sourceModel());
connect(mSourceModel,
SIGNAL(rowsInserted(const QModelIndex &, int, int)),
this,
SLOT(sourceRowsInserted(const QModelIndex &, int, int)));
connect(mSourceModel,
SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
this,
SLOT(sourceRowsRemoved(const QModelIndex &, int, int)));
connect(mSourceModel,
SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
this,
SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex &)));
} }
void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::Node>& filter) void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::Node>& filter)
@ -60,11 +81,52 @@ void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::
bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{ {
Columns::ColumnId id = static_cast<Columns::ColumnId>(left.data(ColumnBase::Role_ColumnId).toInt());
EnumColumnCache::const_iterator valuesIt = mEnumColumnCache.find(id);
if (valuesIt == mEnumColumnCache.end())
{
if (Columns::hasEnums(id))
{
valuesIt = mEnumColumnCache.insert(std::make_pair(id, Columns::getEnums(id))).first;
}
}
if (valuesIt != mEnumColumnCache.end())
{
return valuesIt->second[left.data().toInt()] < valuesIt->second[right.data().toInt()];
}
return QSortFilterProxyModel::lessThan(left, right); return QSortFilterProxyModel::lessThan(left, right);
} }
QString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const
{
Q_ASSERT(mSourceModel != NULL);
int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id);
return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString();
}
void CSMWorld::IdTableProxyModel::refreshFilter() void CSMWorld::IdTableProxyModel::refreshFilter()
{ {
updateColumnMap(); updateColumnMap();
invalidateFilter(); invalidateFilter();
} }
void CSMWorld::IdTableProxyModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int end)
{
refreshFilter();
if (!parent.isValid())
{
emit rowAdded(getRecordId(end).toUtf8().constData());
}
}
void CSMWorld::IdTableProxyModel::sourceRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/)
{
refreshFilter();
}
void CSMWorld::IdTableProxyModel::sourceDataChanged(const QModelIndex &/*topLeft*/, const QModelIndex &/*bottomRight*/)
{
refreshFilter();
}

View file

@ -11,6 +11,8 @@
#include "../filter/node.hpp" #include "../filter/node.hpp"
#include "columns.hpp"
namespace CSMWorld namespace CSMWorld
{ {
class IdTableProxyModel : public QSortFilterProxyModel class IdTableProxyModel : public QSortFilterProxyModel
@ -20,6 +22,15 @@ namespace CSMWorld
boost::shared_ptr<CSMFilter::Node> mFilter; boost::shared_ptr<CSMFilter::Node> mFilter;
std::map<int, int> mColumnMap; // column ID, column index in this model (or -1) std::map<int, int> mColumnMap; // column ID, column index in this model (or -1)
// Cache of enum values for enum columns (e.g. Modified, Record Type).
// Used to speed up comparisons during the sort by such columns.
typedef std::map<Columns::ColumnId, std::vector<std::string> > EnumColumnCache;
mutable EnumColumnCache mEnumColumnCache;
protected:
IdTableBase *mSourceModel;
private: private:
void updateColumnMap(); void updateColumnMap();
@ -30,15 +41,31 @@ namespace CSMWorld
virtual QModelIndex getModelIndex (const std::string& id, int column) const; virtual QModelIndex getModelIndex (const std::string& id, int column) const;
virtual void setSourceModel(QAbstractItemModel *model);
void setFilter (const boost::shared_ptr<CSMFilter::Node>& filter); void setFilter (const boost::shared_ptr<CSMFilter::Node>& filter);
void refreshFilter(); void refreshFilter();
protected: protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const; virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
virtual bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const; virtual bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const;
QString getRecordId(int sourceRow) const;
protected slots:
virtual void sourceRowsInserted(const QModelIndex &parent, int start, int end);
virtual void sourceRowsRemoved(const QModelIndex &parent, int start, int end);
virtual void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
signals:
void rowAdded(const std::string &id);
}; };
} }

View file

@ -171,10 +171,10 @@ QModelIndex CSMWorld::IdTree::index (int row, int column, const QModelIndex& par
encodedId = this->foldIndexAddress(parent); encodedId = this->foldIndexAddress(parent);
} }
if (row<0 || row>=idCollection()->getSize()) if (row < 0 || row >= rowCount(parent))
return QModelIndex(); return QModelIndex();
if (column<0 || column>=idCollection()->getColumns()) if (column < 0 || column >= columnCount(parent))
return QModelIndex(); return QModelIndex();
return createIndex(row, column, encodedId); // store internal id return createIndex(row, column, encodedId); // store internal id
@ -266,3 +266,13 @@ void CSMWorld::IdTree::updateNpcAutocalc (int type, const std::string& id)
{ {
emit refreshNpcDialogue (type, id); emit refreshNpcDialogue (type, id);
} }
int CSMWorld::IdTree::searchNestedColumnIndex(int parentColumn, Columns::ColumnId id)
{
return mNestedCollection->searchNestedColumnIndex(parentColumn, id);
}
int CSMWorld::IdTree::findNestedColumnIndex(int parentColumn, Columns::ColumnId id)
{
return mNestedCollection->findNestedColumnIndex(parentColumn, id);
}

View file

@ -73,6 +73,12 @@ namespace CSMWorld
virtual bool hasChildren (const QModelIndex& index) const; virtual bool hasChildren (const QModelIndex& index) const;
virtual int searchNestedColumnIndex(int parentColumn, Columns::ColumnId id);
///< \return the column index or -1 if the requested column wasn't found.
virtual int findNestedColumnIndex(int parentColumn, Columns::ColumnId id);
///< \return the column index or throws an exception if the requested column wasn't found.
signals: signals:
void resetStart(const QString& id); void resetStart(const QString& id);

View file

@ -9,16 +9,17 @@ namespace
{ {
QString toLower(const QString &str) QString toLower(const QString &str)
{ {
return QString::fromUtf8(Misc::StringUtils::lowerCase(str.toStdString()).c_str()); return QString::fromUtf8(Misc::StringUtils::lowerCase(str.toUtf8().constData()).c_str());
} }
} }
CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type type, QObject *parent) CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type type, QObject *parent)
: IdTableProxyModel(parent), : IdTableProxyModel(parent),
mType(type), mType(type),
mSourceModel(NULL),
mInfoColumnId(type == UniversalId::Type_TopicInfos ? Columns::ColumnId_Topic : mInfoColumnId(type == UniversalId::Type_TopicInfos ? Columns::ColumnId_Topic :
Columns::ColumnId_Journal) Columns::ColumnId_Journal),
mInfoColumnIndex(-1),
mLastAddedSourceRow(-1)
{ {
Q_ASSERT(type == UniversalId::Type_TopicInfos || type == UniversalId::Type_JournalInfos); Q_ASSERT(type == UniversalId::Type_TopicInfos || type == UniversalId::Type_JournalInfos);
} }
@ -26,23 +27,18 @@ CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type t
void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceModel) void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{ {
IdTableProxyModel::setSourceModel(sourceModel); IdTableProxyModel::setSourceModel(sourceModel);
mSourceModel = dynamic_cast<IdTableBase *>(sourceModel);
if (mSourceModel != NULL) if (mSourceModel != NULL)
{ {
connect(mSourceModel, mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId);
SIGNAL(rowsInserted(const QModelIndex &, int, int)),
this,
SLOT(modelRowsChanged(const QModelIndex &, int, int)));
connect(mSourceModel,
SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
this,
SLOT(modelRowsChanged(const QModelIndex &, int, int)));
mFirstRowCache.clear(); mFirstRowCache.clear();
} }
} }
bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{ {
Q_ASSERT(mSourceModel != NULL);
QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column()); QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column());
QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column()); QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column());
@ -56,8 +52,10 @@ bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QMod
int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const
{ {
Q_ASSERT(mSourceModel != NULL);
int row = currentRow; int row = currentRow;
int column = mSourceModel->findColumnIndex(mInfoColumnId); int column = mInfoColumnIndex;
QString info = toLower(mSourceModel->data(mSourceModel->index(row, column)).toString()); QString info = toLower(mSourceModel->data(mSourceModel->index(row, column)).toString());
if (mFirstRowCache.contains(info)) if (mFirstRowCache.contains(info))
@ -73,7 +71,41 @@ int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const
return row; return row;
} }
void CSMWorld::InfoTableProxyModel::modelRowsChanged(const QModelIndex &/*parent*/, int /*start*/, int /*end*/) void CSMWorld::InfoTableProxyModel::sourceRowsRemoved(const QModelIndex &/*parent*/, int /*start*/, int /*end*/)
{ {
refreshFilter();
mFirstRowCache.clear(); mFirstRowCache.clear();
} }
void CSMWorld::InfoTableProxyModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int end)
{
refreshFilter();
if (!parent.isValid())
{
mFirstRowCache.clear();
// We can't re-sort the model here, because the topic of the added row isn't set yet.
// Store the row index for using in the first dataChanged() after this row insertion.
mLastAddedSourceRow = end;
}
}
void CSMWorld::InfoTableProxyModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
refreshFilter();
if (mLastAddedSourceRow != -1 &&
topLeft.row() <= mLastAddedSourceRow && bottomRight.row() >= mLastAddedSourceRow)
{
// Now the topic of the last added row is set,
// so we can re-sort the model to ensure the corrent position of this row
int column = sortColumn();
Qt::SortOrder order = sortOrder();
sort(mInfoColumnIndex); // Restore the correct position of an added row
sort(column, order); // Restore the original sort order
emit rowAdded(getRecordId(mLastAddedSourceRow).toUtf8().constData());
// Make sure that we perform a re-sorting only in the first dataChanged() after a row insertion
mLastAddedSourceRow = -1;
}
}

View file

@ -16,25 +16,29 @@ namespace CSMWorld
Q_OBJECT Q_OBJECT
UniversalId::Type mType; UniversalId::Type mType;
IdTableBase *mSourceModel;
Columns::ColumnId mInfoColumnId; Columns::ColumnId mInfoColumnId;
///< Contains ID for Topic or Journal ID ///< Contains ID for Topic or Journal ID
int mInfoColumnIndex;
int mLastAddedSourceRow;
mutable QHash<QString, int> mFirstRowCache; mutable QHash<QString, int> mFirstRowCache;
int getFirstInfoRow(int currentRow) const; int getFirstInfoRow(int currentRow) const;
///< Finds the first row with the same topic (journal entry) as in \a currentRow ///< Finds the first row with the same topic (journal entry) as in \a currentRow
///< \a currentRow is a row of the source model.
public: public:
InfoTableProxyModel(UniversalId::Type type, QObject *parent = 0); InfoTableProxyModel(UniversalId::Type type, QObject *parent = 0);
void setSourceModel(QAbstractItemModel *sourceModel); virtual void setSourceModel(QAbstractItemModel *sourceModel);
protected: protected:
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
private slots: protected slots:
void modelRowsChanged(const QModelIndex &parent, int start, int end); virtual void sourceRowsInserted(const QModelIndex &parent, int start, int end);
virtual void sourceRowsRemoved(const QModelIndex &parent, int start, int end);
virtual void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
}; };
} }

View file

@ -15,3 +15,28 @@ int CSMWorld::NestedCollection::getNestedColumnsCount(int row, int column) const
{ {
return 0; return 0;
} }
int CSMWorld::NestedCollection::searchNestedColumnIndex(int parentColumn, Columns::ColumnId id)
{
// Assumed that the parentColumn is always a valid index
const NestableColumn *parent = getNestableColumn(parentColumn);
int nestedColumnCount = getNestedColumnsCount(0, parentColumn);
for (int i = 0; i < nestedColumnCount; ++i)
{
if (parent->nestedColumn(i).mColumnId == id)
{
return i;
}
}
return -1;
}
int CSMWorld::NestedCollection::findNestedColumnIndex(int parentColumn, Columns::ColumnId id)
{
int index = searchNestedColumnIndex(parentColumn, id);
if (index == -1)
{
throw std::logic_error("CSMWorld::NestedCollection: No such nested column");
}
return index;
}

View file

@ -1,6 +1,8 @@
#ifndef CSM_WOLRD_NESTEDCOLLECTION_H #ifndef CSM_WOLRD_NESTEDCOLLECTION_H
#define CSM_WOLRD_NESTEDCOLLECTION_H #define CSM_WOLRD_NESTEDCOLLECTION_H
#include "columns.hpp"
class QVariant; class QVariant;
namespace CSMWorld namespace CSMWorld
@ -33,6 +35,12 @@ namespace CSMWorld
virtual int getNestedColumnsCount(int row, int column) const; virtual int getNestedColumnsCount(int row, int column) const;
virtual NestableColumn *getNestableColumn(int column) = 0; virtual NestableColumn *getNestableColumn(int column) = 0;
virtual int searchNestedColumnIndex(int parentColumn, Columns::ColumnId id);
///< \return the column index or -1 if the requested column wasn't found.
virtual int findNestedColumnIndex(int parentColumn, Columns::ColumnId id);
///< \return the column index or throws an exception if the requested column wasn't found.
}; };
} }

View file

@ -161,8 +161,19 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedColumnsCount(int row, int column) const int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedColumnsCount(int row, int column) const
{ {
return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).getColumnsCount( const ColumnBase &nestedColumn = Collection<ESXRecordT, IdAccessorT>::getColumn(column);
Collection<ESXRecordT, IdAccessorT>::getRecord(row)); int numRecords = Collection<ESXRecordT, IdAccessorT>::getSize();
if (row >= 0 && row < numRecords)
{
const Record<ESXRecordT>& record = Collection<ESXRecordT, IdAccessorT>::getRecord(row);
return getAdapter(nestedColumn).getColumnsCount(record);
}
else
{
// If the row is invalid (or there no records), retrieve the column count using a blank record
const Record<ESXRecordT> record;
return getAdapter(nestedColumn).getColumnsCount(record);
}
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>

View file

@ -75,10 +75,10 @@ QModelIndex CSMWorld::NestedTableProxyModel::index(int row, int column, const QM
{ {
assert (!parent.isValid()); assert (!parent.isValid());
int rows = mMainModel->rowCount(parent); int numRows = rowCount(parent);
int columns = mMainModel->columnCount(parent); int numColumns = columnCount(parent);
if (row < 0 || row >= rows || column < 0 || column >= columns) if (row < 0 || row >= numRows || column < 0 || column >= numColumns)
return QModelIndex(); return QModelIndex();
return createIndex(row, column); return createIndex(row, column);

View file

@ -11,6 +11,7 @@
#include <QPainter> #include <QPainter>
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include <QMouseEvent> #include <QMouseEvent>
#include <QSortFilterProxyModel>
#include "../../model/tools/reportmodel.hpp" #include "../../model/tools/reportmodel.hpp"
@ -63,7 +64,7 @@ void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)
for (QModelIndexList::const_iterator iter (selectedRows.begin()); for (QModelIndexList::const_iterator iter (selectedRows.begin());
iter!=selectedRows.end(); ++iter) iter!=selectedRows.end(); ++iter)
{ {
QString hint = mModel->data (mModel->index (iter->row(), 2)).toString(); QString hint = mProxyModel->data (mProxyModel->index (iter->row(), 2)).toString();
if (!hint.isEmpty() && hint[0]=='R') if (!hint.isEmpty() && hint[0]=='R')
{ {
@ -152,7 +153,10 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection); setSelectionMode (QAbstractItemView::ExtendedSelection);
setModel (mModel); mProxyModel = new QSortFilterProxyModel (this);
mProxyModel->setSourceModel (mModel);
setModel (mProxyModel);
setColumnHidden (2, true); setColumnHidden (2, true);
mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0, mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0,
@ -197,7 +201,7 @@ std::vector<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() co
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter) ++iter)
{ {
ids.push_back (mModel->getUniversalId (iter->row())); ids.push_back (mModel->getUniversalId (mProxyModel->mapToSource (*iter).row()));
} }
return ids; return ids;
@ -245,13 +249,22 @@ std::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector<int> rows;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter) ++iter)
{ {
QString hint = mModel->data (mModel->index (iter->row(), 2)).toString(); rows.push_back (mProxyModel->mapToSource (*iter).row());
}
std::sort (rows.begin(), rows.end());
for (std::vector<int>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
{
QString hint = mModel->data (mModel->index (*iter, 2)).toString();
if (!hint.isEmpty() && hint[0]=='R') if (!hint.isEmpty() && hint[0]=='R')
indices.push_back (iter->row()); indices.push_back (*iter);
} }
} }
else else
@ -279,18 +292,28 @@ void CSVTools::ReportTable::showSelection()
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter) ++iter)
emit editRequest (mModel->getUniversalId (iter->row()), mModel->getHint (iter->row())); {
int row = mProxyModel->mapToSource (*iter).row();
emit editRequest (mModel->getUniversalId (row), mModel->getHint (row));
}
} }
void CSVTools::ReportTable::removeSelection() void CSVTools::ReportTable::removeSelection()
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
std::reverse (selectedRows.begin(), selectedRows.end()); std::vector<int> rows;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); for (QModelIndexList::iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter) ++iter)
mModel->removeRows (iter->row(), 1); {
rows.push_back (mProxyModel->mapToSource (*iter).row());
}
std::sort (rows.begin(), rows.end());
for (std::vector<int>::const_reverse_iterator iter (rows.rbegin()); iter!=rows.rend(); ++iter)
mProxyModel->removeRows (*iter, 1);
selectionModel()->clear(); selectionModel()->clear();
} }

View file

@ -6,6 +6,7 @@
#include "../world/dragrecordtable.hpp" #include "../world/dragrecordtable.hpp"
class QAction; class QAction;
class QSortFilterProxyModel;
namespace CSMTools namespace CSMTools
{ {
@ -31,6 +32,7 @@ namespace CSVTools
Action_EditAndRemove Action_EditAndRemove
}; };
QSortFilterProxyModel *mProxyModel;
CSMTools::ReportModel *mModel; CSMTools::ReportModel *mModel;
CSVWorld::CommandDelegate *mIdTypeDelegate; CSVWorld::CommandDelegate *mIdTypeDelegate;
QAction *mShowAction; QAction *mShowAction;
@ -63,11 +65,14 @@ namespace CSVTools
void clear(); void clear();
// Return indices of rows that are suitable for replacement. /// Return indices of rows that are suitable for replacement.
// ///
// \param selection Only list selected rows. /// \param selection Only list selected rows.
///
/// \return rows in the original model
std::vector<int> getReplaceIndices (bool selection) const; std::vector<int> getReplaceIndices (bool selection) const;
/// \param index row in the original model
void flagAsReplaced (int index); void flagAsReplaced (int index);
private slots: private slots:

View file

@ -71,8 +71,6 @@ CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc:
{ {
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins (QMargins (0, 0, 0, 0));
layout->addWidget (&mSearchBox); layout->addWidget (&mSearchBox);
layout->addWidget (mTable = new ReportTable (document, id, true), 2); layout->addWidget (mTable = new ReportTable (document, id, true), 2);

View file

@ -8,6 +8,9 @@
#include <QSpinBox> #include <QSpinBox>
#include <QLabel> #include <QLabel>
#include "../../model/world/commands.hpp"
#include "../../model/world/idtree.hpp"
std::string CSVWorld::CellCreator::getId() const std::string CSVWorld::CellCreator::getId() const
{ {
if (mType->currentIndex()==0) if (mType->currentIndex()==0)
@ -20,6 +23,15 @@ std::string CSVWorld::CellCreator::getId() const
return stream.str(); return stream.str();
} }
void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const
{
CSMWorld::IdTree *model = dynamic_cast<CSMWorld::IdTree *>(getData().getTableModel(getCollectionId()));
Q_ASSERT(model != NULL);
int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell);
int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior);
command.addNestedValue(parentIndex, index, mType->currentIndex() == 0);
}
CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const CSMWorld::UniversalId& id)
: GenericCreator (data, undoStack, id) : GenericCreator (data, undoStack, id)
@ -95,9 +107,16 @@ void CSVWorld::CellCreator::cloneMode(const std::string& originId,
} }
} }
std::string CSVWorld::CellCreator::getErrors() const
void CSVWorld::CellCreator::toggleWidgets(bool active)
{ {
CSVWorld::GenericCreator::toggleWidgets(active); std::string errors;
mType->setEnabled(active); if (mType->currentIndex() == 0)
{
errors = GenericCreator::getErrors();
}
else if (getData().hasId(getId()))
{
errors = "The Exterior Cell is already exist";
}
return errors;
} }

View file

@ -23,17 +23,22 @@ namespace CSVWorld
virtual std::string getId() const; virtual std::string getId() const;
/// Allow subclasses to add additional data to \a command.
virtual void configureCreateCommand(CSMWorld::CreateCommand& command) const;
public: public:
CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);
virtual void reset(); virtual void reset();
virtual void toggleWidgets(bool active = true);
virtual void cloneMode(const std::string& originId, virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type); const CSMWorld::UniversalId::Type type);
virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error.
private slots: private slots:
void setType (int index); void setType (int index);

View file

@ -19,6 +19,7 @@
#include <QComboBox> #include <QComboBox>
#include <QHeaderView> #include <QHeaderView>
#include <QScrollBar> #include <QScrollBar>
#include <QMenu>
#include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
@ -30,6 +31,7 @@
#include "../../model/world/idtree.hpp" #include "../../model/world/idtree.hpp"
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/settings/usersettings.hpp"
#include "../widget/coloreditor.hpp" #include "../widget/coloreditor.hpp"
#include "../widget/droplineedit.hpp" #include "../widget/droplineedit.hpp"
@ -314,10 +316,141 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher()
} }
} }
CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Display display)
: QObject(widget),
mWidget(widget),
mIdType(CSMWorld::TableMimeData::convertEnums(display))
{
Q_ASSERT(mWidget != NULL);
Q_ASSERT(CSMWorld::ColumnBase::isId(display));
Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None);
mWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(mWidget,
SIGNAL(customContextMenuRequested(const QPoint &)),
this,
SLOT(showContextMenu(const QPoint &)));
mEditIdAction = new QAction(this);
connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editIdRequest()));
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(mWidget);
if (lineEdit != NULL)
{
mContextMenu = lineEdit->createStandardContextMenu();
}
else
{
mContextMenu = new QMenu(mWidget);
}
}
void CSVWorld::IdContextMenu::excludeId(const std::string &id)
{
mExcludedIds.insert(id);
}
QString CSVWorld::IdContextMenu::getWidgetValue() const
{
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(mWidget);
QLabel *label = qobject_cast<QLabel *>(mWidget);
QString value = "";
if (lineEdit != NULL)
{
value = lineEdit->text();
}
else if (label != NULL)
{
value = label->text();
}
return value;
}
void CSVWorld::IdContextMenu::addEditIdActionToMenu(const QString &text)
{
mEditIdAction->setText(text);
if (mContextMenu->actions().isEmpty())
{
mContextMenu->addAction(mEditIdAction);
}
else if (mContextMenu->actions().first() != mEditIdAction)
{
QAction *action = mContextMenu->actions().first();
mContextMenu->insertAction(action, mEditIdAction);
mContextMenu->insertSeparator(action);
}
}
void CSVWorld::IdContextMenu::removeEditIdActionFromMenu()
{
if (mContextMenu->actions().isEmpty())
{
return;
}
if (mContextMenu->actions().first() == mEditIdAction)
{
mContextMenu->removeAction(mEditIdAction);
if (!mContextMenu->actions().isEmpty() && mContextMenu->actions().first()->isSeparator())
{
mContextMenu->removeAction(mContextMenu->actions().first());
}
}
}
void CSVWorld::IdContextMenu::showContextMenu(const QPoint &pos)
{
QString value = getWidgetValue();
bool isExcludedId = mExcludedIds.find(value.toUtf8().constData()) != mExcludedIds.end();
if (!value.isEmpty() && !isExcludedId)
{
addEditIdActionToMenu("Edit '" + value + "'");
}
else
{
removeEditIdActionFromMenu();
}
if (!mContextMenu->actions().isEmpty())
{
mContextMenu->exec(mWidget->mapToGlobal(pos));
}
}
void CSVWorld::IdContextMenu::editIdRequest()
{
CSMWorld::UniversalId editId(mIdType, getWidgetValue().toUtf8().constData());
emit editIdRequest(editId, "");
}
/* /*
=============================================================EditWidget===================================================== =============================================================EditWidget=====================================================
*/ */
void CSVWorld::EditWidget::createEditorContextMenu(QWidget *editor,
CSMWorld::ColumnBase::Display display,
int currentRow) const
{
Q_ASSERT(editor != NULL);
if (CSMWorld::ColumnBase::isId(display) &&
CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None)
{
int idColumn = mTable->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
QString id = mTable->data(mTable->index(currentRow, idColumn)).toString();
IdContextMenu *menu = new IdContextMenu(editor, display);
// Current ID is already opened, so no need to create Edit 'ID' action for it
menu->excludeId(id.toUtf8().constData());
connect(menu,
SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)),
this,
SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)));
}
}
CSVWorld::EditWidget::~EditWidget() CSVWorld::EditWidget::~EditWidget()
{ {
for (unsigned i = 0; i < mNestedModels.size(); ++i) for (unsigned i = 0; i < mNestedModels.size(); ++i)
@ -457,6 +590,11 @@ void CSVWorld::EditWidget::remake(int row)
tablesLayout->addWidget(label); tablesLayout->addWidget(label);
tablesLayout->addWidget(table); tablesLayout->addWidget(table);
connect(table,
SIGNAL(editRequest(const CSMWorld::UniversalId &, const std::string &)),
this,
SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)));
} }
else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List))
{ {
@ -490,6 +628,8 @@ void CSVWorld::EditWidget::remake(int row)
editor->setEnabled(false); editor->setEnabled(false);
label->setEnabled(false); label->setEnabled(false);
} }
createEditorContextMenu(editor, display, row);
} }
} }
else else
@ -541,6 +681,8 @@ void CSVWorld::EditWidget::remake(int row)
editor->setEnabled(false); editor->setEnabled(false);
label->setEnabled(false); label->setEnabled(false);
} }
createEditorContextMenu(editor, display, row);
} }
} }
mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i))); mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i)));
@ -617,6 +759,11 @@ CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::Universa
mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
dataChanged(mTable->getModelIndex (getUniversalId().getId(), 0)); dataChanged(mTable->getModelIndex (getUniversalId().getId(), 0));
connect(mEditWidget,
SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)),
this,
SIGNAL(focusId(const CSMWorld::UniversalId &, const std::string &)));
} }
void CSVWorld::SimpleDialogueSubView::setEditLock (bool locked) void CSVWorld::SimpleDialogueSubView::setEditLock (bool locked)
@ -717,25 +864,15 @@ void CSVWorld::SimpleDialogueSubView::refreshNpcDialogue (int type, const std::s
} }
} }
CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, void CSVWorld::DialogueSubView::addButtonBar()
CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting)
: SimpleDialogueSubView (id, document)
{ {
// bottom box if (mButtons)
mBottom = new TableBottomBox (creatorFactory, document, id, this); return;
mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); mButtons = new RecordButtonBar (getUniversalId(), getTable(), mBottom,
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
this, SLOT (requestFocus (const std::string&)));
// button bar
mButtons = new RecordButtonBar (id, getTable(), mBottom,
&getCommandDispatcher(), this); &getCommandDispatcher(), this);
// layout getMainLayout().insertWidget (1, mButtons);
getMainLayout().addWidget (mButtons);
getMainLayout().addWidget (mBottom);
// connections // connections
connect (mButtons, SIGNAL (showPreview()), this, SLOT (showPreview())); connect (mButtons, SIGNAL (showPreview()), this, SLOT (showPreview()));
@ -746,15 +883,54 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id,
mButtons, SLOT (universalIdChanged (const CSMWorld::UniversalId&))); mButtons, SLOT (universalIdChanged (const CSMWorld::UniversalId&)));
} }
CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id,
CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting)
: SimpleDialogueSubView (id, document), mButtons (0)
{
// bottom box
mBottom = new TableBottomBox (creatorFactory, document, id, this);
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
this, SLOT (requestFocus (const std::string&)));
// button bar
if (CSMSettings::UserSettings::instance().setting ("dialogues/toolbar", QString("true")) == "true")
addButtonBar();
// layout
getMainLayout().addWidget (mBottom);
}
void CSVWorld::DialogueSubView::setEditLock (bool locked) void CSVWorld::DialogueSubView::setEditLock (bool locked)
{ {
SimpleDialogueSubView::setEditLock (locked); SimpleDialogueSubView::setEditLock (locked);
if (mButtons)
mButtons->setEditLock (locked); mButtons->setEditLock (locked);
} }
void CSVWorld::DialogueSubView::updateUserSetting (const QString& name, const QStringList& value) void CSVWorld::DialogueSubView::updateUserSetting (const QString& name, const QStringList& value)
{ {
SimpleDialogueSubView::updateUserSetting (name, value); SimpleDialogueSubView::updateUserSetting (name, value);
if (name=="dialogues/toolbar")
{
if (value.at(0)==QString ("true"))
{
addButtonBar();
}
else
{
if (mButtons)
{
getMainLayout().removeWidget (mButtons);
delete mButtons;
mButtons = 0;
}
}
}
if (mButtons)
mButtons->updateUserSetting (name, value); mButtons->updateUserSetting (name, value);
} }

View file

@ -1,6 +1,7 @@
#ifndef CSV_WORLD_DIALOGUESUBVIEW_H #ifndef CSV_WORLD_DIALOGUESUBVIEW_H
#define CSV_WORLD_DIALOGUESUBVIEW_H #define CSV_WORLD_DIALOGUESUBVIEW_H
#include <set>
#include <map> #include <map>
#include <memory> #include <memory>
@ -11,12 +12,14 @@
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
#include "../../model/world/commanddispatcher.hpp" #include "../../model/world/commanddispatcher.hpp"
#include "../../model/world/universalid.hpp"
class QDataWidgetMapper; class QDataWidgetMapper;
class QSize; class QSize;
class QEvent; class QEvent;
class QLabel; class QLabel;
class QVBoxLayout; class QVBoxLayout;
class QMenu;
namespace CSMWorld namespace CSMWorld
{ {
@ -149,6 +152,36 @@ namespace CSVWorld
CSMWorld::ColumnBase::Display display); CSMWorld::ColumnBase::Display display);
}; };
/// A context menu with "Edit 'ID'" action for editors in the dialogue subview
class IdContextMenu : public QObject
{
Q_OBJECT
QWidget *mWidget;
CSMWorld::UniversalId::Type mIdType;
std::set<std::string> mExcludedIds;
///< A list of IDs that should not have the Edit 'ID' action.
QMenu *mContextMenu;
QAction *mEditIdAction;
QString getWidgetValue() const;
void addEditIdActionToMenu(const QString &text);
void removeEditIdActionFromMenu();
public:
IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Display display);
void excludeId(const std::string &id);
private slots:
void showContextMenu(const QPoint &pos);
void editIdRequest();
signals:
void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint);
};
class EditWidget : public QScrollArea class EditWidget : public QScrollArea
{ {
Q_OBJECT Q_OBJECT
@ -162,6 +195,9 @@ namespace CSVWorld
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
std::vector<CSMWorld::NestedTableProxyModel*> mNestedModels; //Plain, raw C pointers, deleted in the dtor std::vector<CSMWorld::NestedTableProxyModel*> mNestedModels; //Plain, raw C pointers, deleted in the dtor
void createEditorContextMenu(QWidget *editor,
CSMWorld::ColumnBase::Display display,
int currentRow) const;
public: public:
EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table,
@ -171,6 +207,9 @@ namespace CSVWorld
virtual ~EditWidget(); virtual ~EditWidget();
void remake(int row); void remake(int row);
signals:
void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint);
}; };
class SimpleDialogueSubView : public CSVDoc::SubView class SimpleDialogueSubView : public CSVDoc::SubView
@ -223,6 +262,10 @@ namespace CSVWorld
TableBottomBox* mBottom; TableBottomBox* mBottom;
RecordButtonBar *mButtons; RecordButtonBar *mButtons;
private:
void addButtonBar();
public: public:
DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,

View file

@ -55,12 +55,10 @@ void CSVWorld::DragRecordTable::dragMoveEvent(QDragMoveEvent *event)
if (index.flags() & Qt::ItemIsEditable) if (index.flags() & Qt::ItemIsEditable)
{ {
event->accept(); event->accept();
return;
} }
} }
else
{
event->ignore(); event->ignore();
}
} }
void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event) void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event)

View file

@ -0,0 +1,239 @@
#include "extendedcommandconfigurator.hpp"
#include <algorithm>
#include <QPushButton>
#include <QGroupBox>
#include <QCheckBox>
#include <QLabel>
#include <QLayout>
#include "../../model/doc/document.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "../../model/world/data.hpp"
CSVWorld::ExtendedCommandConfigurator::ExtendedCommandConfigurator(CSMDoc::Document &document,
const CSMWorld::UniversalId &id,
QWidget *parent)
: QWidget(parent),
mNumUsedCheckBoxes(0),
mNumChecked(0),
mMode(Mode_None),
mData(document.getData()),
mEditLock(false)
{
mCommandDispatcher = new CSMWorld::CommandDispatcher(document, id, this);
connect(&mData, SIGNAL(idListChanged()), this, SLOT(dataIdListChanged()));
mPerformButton = new QPushButton(this);
mPerformButton->setDefault(true);
mPerformButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
connect(mPerformButton, SIGNAL(clicked(bool)), this, SLOT(performExtendedCommand()));
mCancelButton = new QPushButton("Cancel", this);
mCancelButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
connect(mCancelButton, SIGNAL(clicked(bool)), this, SIGNAL(done()));
mTypeGroup = new QGroupBox(this);
QGridLayout *groupLayout = new QGridLayout(mTypeGroup);
groupLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
mTypeGroup->setLayout(groupLayout);
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->setSizeConstraint(QLayout::SetNoConstraint);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(mTypeGroup);
mainLayout->addWidget(mPerformButton);
mainLayout->addWidget(mCancelButton);
}
void CSVWorld::ExtendedCommandConfigurator::configure(CSVWorld::ExtendedCommandConfigurator::Mode mode,
const std::vector<std::string> &selectedIds)
{
mMode = mode;
if (mMode != Mode_None)
{
mPerformButton->setText((mMode == Mode_Delete) ? "Extended Delete" : "Extended Revert");
mSelectedIds = selectedIds;
mCommandDispatcher->setSelection(mSelectedIds);
setupCheckBoxes(mCommandDispatcher->getExtendedTypes());
setupGroupLayout();
lockWidgets(mEditLock);
}
}
void CSVWorld::ExtendedCommandConfigurator::setEditLock(bool locked)
{
if (mEditLock != locked)
{
mEditLock = locked;
lockWidgets(mEditLock);
}
}
void CSVWorld::ExtendedCommandConfigurator::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
setupGroupLayout();
}
void CSVWorld::ExtendedCommandConfigurator::setupGroupLayout()
{
if (mMode == Mode_None)
{
return;
}
int groupWidth = mTypeGroup->geometry().width();
QGridLayout *layout = qobject_cast<QGridLayout *>(mTypeGroup->layout());
// Find the optimal number of rows to place the checkboxes within the available space
int divider = 1;
do
{
while (layout->itemAt(0) != NULL)
{
layout->removeItem(layout->itemAt(0));
}
int counter = 0;
int itemsPerRow = mNumUsedCheckBoxes / divider;
CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin();
CheckBoxMap::const_iterator end = mTypeCheckBoxes.end();
for (; current != end; ++current)
{
if (counter < mNumUsedCheckBoxes)
{
int row = counter / itemsPerRow;
int column = counter - (counter / itemsPerRow) * itemsPerRow;
layout->addWidget(current->first, row, column);
}
++counter;
}
divider *= 2;
}
while (groupWidth < mTypeGroup->sizeHint().width() && divider <= mNumUsedCheckBoxes);
}
void CSVWorld::ExtendedCommandConfigurator::setupCheckBoxes(const std::vector<CSMWorld::UniversalId> &types)
{
// Make sure that we have enough checkboxes
int numTypes = static_cast<int>(types.size());
int numCheckBoxes = static_cast<int>(mTypeCheckBoxes.size());
if (numTypes > numCheckBoxes)
{
for (int i = numTypes - numCheckBoxes; i > 0; --i)
{
QCheckBox *checkBox = new QCheckBox(mTypeGroup);
connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxStateChanged(int)));
mTypeCheckBoxes.insert(std::make_pair(checkBox, CSMWorld::UniversalId::Type_None));
}
}
// Set up the checkboxes
int counter = 0;
CheckBoxMap::iterator current = mTypeCheckBoxes.begin();
CheckBoxMap::iterator end = mTypeCheckBoxes.end();
for (; current != end; ++current)
{
if (counter < numTypes)
{
CSMWorld::UniversalId type = types[counter];
current->first->setText(QString::fromUtf8(type.getTypeName().c_str()));
current->first->setChecked(true);
current->second = type;
++counter;
}
else
{
current->first->hide();
}
}
mNumChecked = mNumUsedCheckBoxes = numTypes;
}
void CSVWorld::ExtendedCommandConfigurator::lockWidgets(bool locked)
{
mPerformButton->setEnabled(!mEditLock && mNumChecked > 0);
CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin();
CheckBoxMap::const_iterator end = mTypeCheckBoxes.end();
for (int i = 0; current != end && i < mNumUsedCheckBoxes; ++current, ++i)
{
current->first->setEnabled(!mEditLock);
}
}
void CSVWorld::ExtendedCommandConfigurator::performExtendedCommand()
{
std::vector<CSMWorld::UniversalId> types;
CheckBoxMap::const_iterator current = mTypeCheckBoxes.begin();
CheckBoxMap::const_iterator end = mTypeCheckBoxes.end();
for (; current != end; ++current)
{
if (current->first->isChecked())
{
types.push_back(current->second);
}
}
mCommandDispatcher->setExtendedTypes(types);
if (mMode == Mode_Delete)
{
mCommandDispatcher->executeExtendedDelete();
}
else
{
mCommandDispatcher->executeExtendedRevert();
}
emit done();
}
void CSVWorld::ExtendedCommandConfigurator::checkBoxStateChanged(int state)
{
switch (state)
{
case Qt::Unchecked:
--mNumChecked;
break;
case Qt::Checked:
++mNumChecked;
break;
case Qt::PartiallyChecked: // Not used
break;
}
mPerformButton->setEnabled(mNumChecked > 0);
}
void CSVWorld::ExtendedCommandConfigurator::dataIdListChanged()
{
bool idsRemoved = false;
for (int i = 0; i < static_cast<int>(mSelectedIds.size()); ++i)
{
if (!mData.hasId(mSelectedIds[i]))
{
std::swap(mSelectedIds[i], mSelectedIds.back());
mSelectedIds.pop_back();
idsRemoved = true;
--i;
}
}
// If all selected IDs were removed, cancel the configurator
if (mSelectedIds.empty())
{
emit done();
return;
}
if (idsRemoved)
{
mCommandDispatcher->setSelection(mSelectedIds);
}
}

View file

@ -0,0 +1,78 @@
#ifndef CSVWORLD_EXTENDEDCOMMANDCONFIGURATOR_HPP
#define CSVWORLD_EXTENDEDCOMMANDCONFIGURATOR_HPP
#include <map>
#include <QWidget>
#include "../../model/world/universalid.hpp"
class QPushButton;
class QGroupBox;
class QCheckBox;
class QLabel;
class QHBoxLayout;
namespace CSMDoc
{
class Document;
}
namespace CSMWorld
{
class CommandDispatcher;
class Data;
}
namespace CSVWorld
{
class ExtendedCommandConfigurator : public QWidget
{
Q_OBJECT
public:
enum Mode { Mode_None, Mode_Delete, Mode_Revert };
private:
typedef std::map<QCheckBox *, CSMWorld::UniversalId> CheckBoxMap;
QPushButton *mPerformButton;
QPushButton *mCancelButton;
QGroupBox *mTypeGroup;
CheckBoxMap mTypeCheckBoxes;
int mNumUsedCheckBoxes;
int mNumChecked;
Mode mMode;
CSMWorld::CommandDispatcher *mCommandDispatcher;
CSMWorld::Data &mData;
std::vector<std::string> mSelectedIds;
bool mEditLock;
void setupGroupLayout();
void setupCheckBoxes(const std::vector<CSMWorld::UniversalId> &types);
void lockWidgets(bool locked);
public:
ExtendedCommandConfigurator(CSMDoc::Document &document,
const CSMWorld::UniversalId &id,
QWidget *parent = 0);
void configure(Mode mode, const std::vector<std::string> &selectedIds);
void setEditLock(bool locked);
protected:
virtual void resizeEvent(QResizeEvent *event);
private slots:
void performExtendedCommand();
void checkBoxStateChanged(int state);
void dataIdListChanged();
signals:
void done();
};
}
#endif

View file

@ -161,6 +161,8 @@ CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undo
connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create())); connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create()));
connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
connect (&mData, SIGNAL (idListChanged()), this, SLOT (dataIdListChanged()));
} }
void CSVWorld::GenericCreator::setEditLock (bool locked) void CSVWorld::GenericCreator::setEditLock (bool locked)
@ -291,3 +293,12 @@ void CSVWorld::GenericCreator::scopeChanged (int index)
update(); update();
updateNamespace(); updateNamespace();
} }
void CSVWorld::GenericCreator::dataIdListChanged()
{
// If the original ID of cloned record was removed, cancel the creator
if (mCloneMode && !mData.hasId(mClonedId))
{
emit done();
}
}

View file

@ -113,6 +113,8 @@ namespace CSVWorld
void create(); void create();
void scopeChanged (int index); void scopeChanged (int index);
void dataIdListChanged();
}; };
} }

View file

@ -1,23 +1,25 @@
#include "nestedtable.hpp" #include "nestedtable.hpp"
#include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/universalid.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "util.hpp"
#include <QHeaderView> #include <QHeaderView>
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include <QMenu> #include <QMenu>
#include <QDebug> #include <QDebug>
#include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/universalid.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "tableeditidaction.hpp"
#include "util.hpp"
CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
CSMWorld::UniversalId id, CSMWorld::UniversalId id,
CSMWorld::NestedTableProxyModel* model, CSMWorld::NestedTableProxyModel* model,
QWidget* parent, QWidget* parent,
bool editable) bool editable)
: DragRecordTable(document, parent), : DragRecordTable(document, parent),
mAddNewRowAction(0), mEditIdAction(0),
mRemoveRowAction(0),
mModel(model) mModel(model)
{ {
setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionBehavior (QAbstractItemView::SelectRows);
@ -61,6 +63,9 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
connect(mRemoveRowAction, SIGNAL(triggered()), connect(mRemoveRowAction, SIGNAL(triggered()),
this, SLOT(removeRowActionTriggered())); this, SLOT(removeRowActionTriggered()));
mEditIdAction = new TableEditIdAction(*this, this);
connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editCell()));
} }
} }
@ -72,13 +77,22 @@ std::vector<CSMWorld::UniversalId> CSVWorld::NestedTable::getDraggedRecords() co
void CSVWorld::NestedTable::contextMenuEvent (QContextMenuEvent *event) void CSVWorld::NestedTable::contextMenuEvent (QContextMenuEvent *event)
{ {
if (!mRemoveRowAction || !mAddNewRowAction) if (!mEditIdAction)
return; return;
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
QMenu menu(this); QMenu menu(this);
int currentRow = rowAt(event->y());
int currentColumn = columnAt(event->x());
if (mEditIdAction->isValidIdCell(currentRow, currentColumn))
{
mEditIdAction->setCell(currentRow, currentColumn);
menu.addAction(mEditIdAction);
menu.addSeparator();
}
if (selectionModel()->selectedRows().size() == 1) if (selectionModel()->selectedRows().size() == 1)
menu.addAction(mRemoveRowAction); menu.addAction(mRemoveRowAction);
@ -102,3 +116,8 @@ void CSVWorld::NestedTable::addNewRowActionTriggered()
selectionModel()->selectedRows().size(), selectionModel()->selectedRows().size(),
mModel->getParentColumn())); mModel->getParentColumn()));
} }
void CSVWorld::NestedTable::editCell()
{
emit editRequest(mEditIdAction->getCurrentId(), "");
}

View file

@ -22,12 +22,15 @@ namespace CSMDoc
namespace CSVWorld namespace CSVWorld
{ {
class TableEditIdAction;
class NestedTable : public DragRecordTable class NestedTable : public DragRecordTable
{ {
Q_OBJECT Q_OBJECT
QAction *mAddNewRowAction; QAction *mAddNewRowAction;
QAction *mRemoveRowAction; QAction *mRemoveRowAction;
TableEditIdAction *mEditIdAction;
CSMWorld::NestedTableProxyModel* mModel; CSMWorld::NestedTableProxyModel* mModel;
CSMWorld::CommandDispatcher *mDispatcher; CSMWorld::CommandDispatcher *mDispatcher;
@ -47,6 +50,11 @@ namespace CSVWorld
void removeRowActionTriggered(); void removeRowActionTriggered();
void addNewRowActionTriggered(); void addNewRowActionTriggered();
void editCell();
signals:
void editRequest(const CSMWorld::UniversalId &id, const std::string &hint);
}; };
} }

View file

@ -13,8 +13,6 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo
{ {
QHBoxLayout *layout = new QHBoxLayout; QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins (QMargins (0, 0, 0, 0));
if (document.getData().getReferenceables().searchId (id.getId())==-1) if (document.getData().getReferenceables().searchId (id.getId())==-1)
{ {
std::string referenceableId = std::string referenceableId =

View file

@ -17,12 +17,11 @@ void CSVWorld::RecordButtonBar::updateModificationButtons()
mCloneButton->setDisabled (createAndDeleteDisabled); mCloneButton->setDisabled (createAndDeleteDisabled);
mAddButton->setDisabled (createAndDeleteDisabled); mAddButton->setDisabled (createAndDeleteDisabled);
mDeleteButton->setDisabled (createAndDeleteDisabled);
bool commandDisabled = !mCommandDispatcher || mLocked; bool commandDisabled = !mCommandDispatcher || mLocked;
mRevertButton->setDisabled (commandDisabled); mRevertButton->setDisabled (commandDisabled);
mDeleteButton->setDisabled (commandDisabled); mDeleteButton->setDisabled (commandDisabled || createAndDeleteDisabled);
} }
void CSVWorld::RecordButtonBar::updatePrevNextButtons() void CSVWorld::RecordButtonBar::updatePrevNextButtons()

View file

@ -31,8 +31,6 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D
{ {
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins (QMargins (0, 0, 0, 0));
layout->addWidget (mBottom = new TableBottomBox (NullCreatorFactory(), document, id, this), 0); layout->addWidget (mBottom = new TableBottomBox (NullCreatorFactory(), document, id, this), 0);
mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); mLayout->setContentsMargins (QMargins (0, 0, 0, 0));

View file

@ -276,11 +276,11 @@ void CSVWorld::ScriptEdit::lineNumberAreaPaintEvent(QPaintEvent *event)
if(textCursor().hasSelection()) if(textCursor().hasSelection())
{ {
QString str = textCursor().selection().toPlainText(); QString str = textCursor().selection().toPlainText();
int selectedLines = str.count("\n")+1; int offset = str.count("\n");
if(textCursor().position() < textCursor().anchor()) if(textCursor().position() < textCursor().anchor())
endBlock += selectedLines; endBlock += offset;
else else
startBlock -= selectedLines; startBlock -= offset;
} }
painter.setBackgroundMode(Qt::OpaqueMode); painter.setBackgroundMode(Qt::OpaqueMode);
QFont font = painter.font(); QFont font = painter.font();

View file

@ -0,0 +1,143 @@
#include "scripterrortable.hpp"
#include <QHeaderView>
#include <components/compiler/tokenloc.hpp>
#include <components/compiler/scanner.hpp>
#include <components/compiler/fileparser.hpp>
#include <components/compiler/exception.hpp>
#include <components/compiler/extensions0.hpp>
#include "../../model/doc/document.hpp"
#include "../../model/settings/usersettings.hpp"
void CSVWorld::ScriptErrorTable::report (const std::string& message, const Compiler::TokenLoc& loc, Type type)
{
std::ostringstream stream;
stream << message << " (" << loc.mLiteral << ")";
addMessage (stream.str(), type==Compiler::ErrorHandler::WarningMessage ?
CSMDoc::Message::Severity_Warning : CSMDoc::Message::Severity_Error,
loc.mLine, loc.mColumn-loc.mLiteral.length());
}
void CSVWorld::ScriptErrorTable::report (const std::string& message, Type type)
{
addMessage (message, type==Compiler::ErrorHandler::WarningMessage ?
CSMDoc::Message::Severity_Warning : CSMDoc::Message::Severity_Error);
}
void CSVWorld::ScriptErrorTable::addMessage (const std::string& message,
CSMDoc::Message::Severity severity, int line, int column)
{
int row = rowCount();
setRowCount (row+1);
QTableWidgetItem *severityItem = new QTableWidgetItem (
QString::fromUtf8 (CSMDoc::Message::toString (severity).c_str()));
severityItem->setFlags (severityItem->flags() ^ Qt::ItemIsEditable);
setItem (row, 0, severityItem);
if (line!=-1)
{
QTableWidgetItem *lineItem = new QTableWidgetItem;
lineItem->setData (Qt::DisplayRole, line+1);
lineItem->setFlags (lineItem->flags() ^ Qt::ItemIsEditable);
setItem (row, 1, lineItem);
QTableWidgetItem *columnItem = new QTableWidgetItem;
columnItem->setData (Qt::DisplayRole, column);
columnItem->setFlags (columnItem->flags() ^ Qt::ItemIsEditable);
setItem (row, 3, columnItem);
}
QTableWidgetItem *messageItem = new QTableWidgetItem (QString::fromUtf8 (message.c_str()));
messageItem->setFlags (messageItem->flags() ^ Qt::ItemIsEditable);
setItem (row, 2, messageItem);
}
void CSVWorld::ScriptErrorTable::setWarningsMode (const QString& value)
{
if (value=="Ignore")
Compiler::ErrorHandler::setWarningsMode (0);
else if (value=="Normal")
Compiler::ErrorHandler::setWarningsMode (1);
else if (value=="Strict")
Compiler::ErrorHandler::setWarningsMode (2);
}
CSVWorld::ScriptErrorTable::ScriptErrorTable (const CSMDoc::Document& document, QWidget *parent)
: QTableWidget (parent), mContext (document.getData())
{
setColumnCount (4);
QStringList headers;
headers << "Severity" << "Line" << "Description";
setHorizontalHeaderLabels (headers);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
horizontalHeader()->setSectionResizeMode (0, QHeaderView::ResizeToContents);
horizontalHeader()->setSectionResizeMode (1, QHeaderView::ResizeToContents);
#else
horizontalHeader()->setResizeMode (0, QHeaderView::ResizeToContents);
horizontalHeader()->setResizeMode (1, QHeaderView::ResizeToContents);
#endif
horizontalHeader()->setStretchLastSection (true);
verticalHeader()->hide();
setColumnHidden (3, true);
setSelectionMode (QAbstractItemView::NoSelection);
Compiler::registerExtensions (mExtensions);
mContext.setExtensions (&mExtensions);
setWarningsMode (CSMSettings::UserSettings::instance().settingValue ("script-editor/warnings"));
connect (this, SIGNAL (cellClicked (int, int)), this, SLOT (cellClicked (int, int)));
}
void CSVWorld::ScriptErrorTable::updateUserSetting (const QString& name, const QStringList& value)
{
if (name=="script-editor/warnings" && !value.isEmpty())
setWarningsMode (value.at (0));
}
void CSVWorld::ScriptErrorTable::update (const std::string& source)
{
clear();
try
{
std::istringstream input (source);
Compiler::Scanner scanner (*this, input, mContext.getExtensions());
Compiler::FileParser parser (*this, mContext);
scanner.scan (parser);
}
catch (const Compiler::SourceException&)
{
// error has already been reported via error handler
}
catch (const std::exception& error)
{
addMessage (error.what(), CSMDoc::Message::Severity_SeriousError);
}
}
void CSVWorld::ScriptErrorTable::clear()
{
setRowCount (0);
}
void CSVWorld::ScriptErrorTable::cellClicked (int row, int column)
{
if (item (row, 1))
{
int scriptLine = item (row, 1)->data (Qt::DisplayRole).toInt();
int scriptColumn = item (row, 3)->data (Qt::DisplayRole).toInt();
emit highlightError (scriptLine-1, scriptColumn);
}
}

View file

@ -0,0 +1,57 @@
#ifndef CSV_WORLD_SCRIPTERRORTABLE_H
#define CSV_WORLD_SCRIPTERRORTABLE_H
#include <QTableWidget>
#include <components/compiler/errorhandler.hpp>
#include <components/compiler/extensions.hpp>
#include "../../model/world/scriptcontext.hpp"
#include "../../model/doc/messages.hpp"
namespace CSMDoc
{
class Document;
}
namespace CSVWorld
{
class ScriptErrorTable : public QTableWidget, private Compiler::ErrorHandler
{
Q_OBJECT
Compiler::Extensions mExtensions;
CSMWorld::ScriptContext mContext;
virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type);
///< Report error to the user.
virtual void report (const std::string& message, Type type);
///< Report a file related error
void addMessage (const std::string& message, CSMDoc::Message::Severity severity,
int line = -1, int column = -1);
void setWarningsMode (const QString& value);
public:
ScriptErrorTable (const CSMDoc::Document& document, QWidget *parent = 0);
void updateUserSetting (const QString& name, const QStringList& value);
void update (const std::string& source);
void clear();
private slots:
void cellClicked (int row, int column);
signals:
void highlightError (int line, int column);
};
}
#endif

View file

@ -4,7 +4,8 @@
#include <QStatusBar> #include <QStatusBar>
#include <QStackedLayout> #include <QStackedLayout>
#include <QLabel> #include <QSplitter>
#include <QTimer>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
@ -15,45 +16,96 @@
#include "../../model/settings/usersettings.hpp" #include "../../model/settings/usersettings.hpp"
#include "scriptedit.hpp" #include "scriptedit.hpp"
#include "recordbuttonbar.hpp"
#include "tablebottombox.hpp"
#include "genericcreator.hpp"
#include "scripterrortable.hpp"
void CSVWorld::ScriptSubView::addButtonBar()
{
if (mButtons)
return;
mButtons = new RecordButtonBar (getUniversalId(), *mModel, mBottom, &mCommandDispatcher, this);
mLayout.insertWidget (1, mButtons);
connect (mButtons, SIGNAL (switchToRow (int)), this, SLOT (switchToRow (int)));
connect (this, SIGNAL (universalIdChanged (const CSMWorld::UniversalId&)),
mButtons, SLOT (universalIdChanged (const CSMWorld::UniversalId&)));
}
void CSVWorld::ScriptSubView::recompile()
{
if (!mCompileDelay->isActive() && !isDeleted())
mCompileDelay->start (
CSMSettings::UserSettings::instance().setting ("script-editor/compile-delay").toInt());
}
bool CSVWorld::ScriptSubView::isDeleted() const
{
return mModel->data (mModel->getModelIndex (getUniversalId().getId(), mStateColumn)).toInt()
==CSMWorld::RecordBase::State_Deleted;
}
void CSVWorld::ScriptSubView::updateDeletedState()
{
if (isDeleted())
{
mErrors->clear();
mEditor->setEnabled (false);
}
else
{
mEditor->setEnabled (true);
recompile();
}
}
CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: SubView (id), mDocument (document), mColumn (-1), mBottom(0), mStatus(0) : SubView (id), mDocument (document), mColumn (-1), mBottom(0), mButtons (0),
mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType()))
{ {
QVBoxLayout *layout = new QVBoxLayout; std::vector<std::string> selection (1, id.getId());
layout->setContentsMargins (QMargins (0, 0, 0, 0)); mCommandDispatcher.setSelection (selection);
mBottom = new QWidget(this); mMain = new QSplitter (this);
QStackedLayout *bottmLayout = new QStackedLayout(mBottom); mMain->setOrientation (Qt::Vertical);
bottmLayout->setContentsMargins (0, 0, 0, 0); mLayout.addWidget (mMain, 2);
QStatusBar *statusBar = new QStatusBar(mBottom);
mStatus = new QLabel(mBottom);
statusBar->addWidget (mStatus);
bottmLayout->addWidget (statusBar);
mBottom->setLayout (bottmLayout);
layout->addWidget (mBottom, 0); mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this);
layout->insertWidget (0, mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this), 2); mMain->addWidget (mEditor);
mMain->setCollapsible (0, false);
QWidget *widget = new QWidget; mErrors = new ScriptErrorTable (document, this);
widget->setLayout (layout); mMain->addWidget (mErrors);
QWidget *widget = new QWidget (this);;
widget->setLayout (&mLayout);
setWidget (widget); setWidget (widget);
mModel = &dynamic_cast<CSMWorld::IdTable&> ( mModel = &dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts));
for (int i=0; i<mModel->columnCount(); ++i) mColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_ScriptText);
if (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display)== mStateColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification);
CSMWorld::ColumnBase::Display_ScriptFile)
{
mColumn = i;
break;
}
if (mColumn==-1) QString source = mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString();
throw std::logic_error ("Can't find script column");
mEditor->setPlainText (mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString()); mEditor->setPlainText (source);
// bottom box and buttons
mBottom = new TableBottomBox (CreatorFactory<GenericCreator>(), document, id, this);
if (CSMSettings::UserSettings::instance().setting ("script-editor/toolbar", QString("true")) == "true")
addButtonBar();
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
this, SLOT (switchToId (const std::string&)));
mLayout.addWidget (mBottom);
// signals
connect (mEditor, SIGNAL (textChanged()), this, SLOT (textChanged())); connect (mEditor, SIGNAL (textChanged()), this, SLOT (textChanged()));
connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
@ -64,35 +116,80 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc:
updateStatusBar(); updateStatusBar();
connect(mEditor, SIGNAL(cursorPositionChanged()), this, SLOT(updateStatusBar())); connect(mEditor, SIGNAL(cursorPositionChanged()), this, SLOT(updateStatusBar()));
mErrors->update (source.toUtf8().constData());
connect (mErrors, SIGNAL (highlightError (int, int)),
this, SLOT (highlightError (int, int)));
mCompileDelay = new QTimer (this);
mCompileDelay->setSingleShot (true);
connect (mCompileDelay, SIGNAL (timeout()), this, SLOT (updateRequest()));
updateDeletedState();
} }
void CSVWorld::ScriptSubView::updateUserSetting (const QString& name, const QStringList& value) void CSVWorld::ScriptSubView::updateUserSetting (const QString& name, const QStringList& value)
{ {
if (name == "script-editor/show-linenum") if (name == "script-editor/show-linenum")
{ {
std::string showLinenum = value.at(0).toStdString(); std::string showLinenum = value.at(0).toUtf8().constData();
mEditor->showLineNum(showLinenum == "true"); mEditor->showLineNum(showLinenum == "true");
mBottom->setVisible(showLinenum == "true"); mBottom->setVisible(showLinenum == "true");
} }
else if (name == "script-editor/mono-font") else if (name == "script-editor/mono-font")
{ {
mEditor->setMonoFont(value.at(0).toStdString() == "true"); mEditor->setMonoFont (value.at(0)==QString ("true"));
} }
else if (name=="script-editor/toolbar")
{
if (value.at(0)==QString ("true"))
{
addButtonBar();
}
else
{
if (mButtons)
{
mLayout.removeWidget (mButtons);
delete mButtons;
mButtons = 0;
}
}
}
else if (name=="script-editor/compile-delay")
{
mCompileDelay->setInterval (value.at (0).toInt());
}
if (mButtons)
mButtons->updateUserSetting (name, value);
mErrors->updateUserSetting (name, value);
if (name=="script-editor/warnings")
recompile();
}
void CSVWorld::ScriptSubView::setStatusBar (bool show)
{
mBottom->setStatusBar (show);
} }
void CSVWorld::ScriptSubView::updateStatusBar () void CSVWorld::ScriptSubView::updateStatusBar ()
{ {
std::ostringstream stream; mBottom->positionChanged (mEditor->textCursor().blockNumber() + 1,
mEditor->textCursor().columnNumber() + 1);
stream << "(" << mEditor->textCursor().blockNumber() + 1 << ", "
<< mEditor->textCursor().columnNumber() + 1 << ")";
mStatus->setText (QString::fromUtf8 (stream.str().c_str()));
} }
void CSVWorld::ScriptSubView::setEditLock (bool locked) void CSVWorld::ScriptSubView::setEditLock (bool locked)
{ {
mEditor->setReadOnly (locked); mEditor->setReadOnly (locked);
if (mButtons)
mButtons->setEditLock (locked);
mCommandDispatcher.setEditLock (locked);
} }
void CSVWorld::ScriptSubView::useHint (const std::string& hint) void CSVWorld::ScriptSubView::useHint (const std::string& hint)
@ -129,8 +226,12 @@ void CSVWorld::ScriptSubView::textChanged()
ScriptEdit::ChangeLock lock (*mEditor); ScriptEdit::ChangeLock lock (*mEditor);
QString source = mEditor->toPlainText();
mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel, mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel,
mModel->getModelIndex (getUniversalId().getId(), mColumn), mEditor->toPlainText())); mModel->getModelIndex (getUniversalId().getId(), mColumn), source));
recompile();
} }
void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
@ -142,12 +243,21 @@ void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QMo
QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
if (index.row()>=topLeft.row() && index.row()<=bottomRight.row() && if (index.row()>=topLeft.row() && index.row()<=bottomRight.row())
index.column()>=topLeft.column() && index.column()<=bottomRight.column())
{ {
if (mStateColumn>=topLeft.column() && mStateColumn<=bottomRight.column())
updateDeletedState();
if (mColumn>=topLeft.column() && mColumn<=bottomRight.column())
{
QString source = mModel->data (index).toString();
QTextCursor cursor = mEditor->textCursor(); QTextCursor cursor = mEditor->textCursor();
mEditor->setPlainText (mModel->data (index).toString()); mEditor->setPlainText (source);
mEditor->setTextCursor (cursor); mEditor->setTextCursor (cursor);
recompile();
}
} }
} }
@ -159,3 +269,42 @@ void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, i
emit closeRequest(); emit closeRequest();
} }
void CSVWorld::ScriptSubView::switchToRow (int row)
{
int idColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
std::string id = mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData();
setUniversalId (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, id));
mEditor->setPlainText (mModel->data (mModel->index (row, mColumn)).toString());
std::vector<std::string> selection (1, id);
mCommandDispatcher.setSelection (selection);
updateDeletedState();
}
void CSVWorld::ScriptSubView::switchToId (const std::string& id)
{
switchToRow (mModel->getModelIndex (id, 0).row());
}
void CSVWorld::ScriptSubView::highlightError (int line, int column)
{
QTextCursor cursor = mEditor->textCursor();
cursor.movePosition (QTextCursor::Start);
if (cursor.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, line))
cursor.movePosition (QTextCursor::Right, QTextCursor::MoveAnchor, column);
mEditor->setFocus();
mEditor->setTextCursor (cursor);
}
void CSVWorld::ScriptSubView::updateRequest()
{
QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
QString source = mModel->data (index).toString();
mErrors->update (source.toUtf8().constData());
}

View file

@ -1,10 +1,17 @@
#ifndef CSV_WORLD_SCRIPTSUBVIEW_H #ifndef CSV_WORLD_SCRIPTSUBVIEW_H
#define CSV_WORLD_SCRIPTSUBVIEW_H #define CSV_WORLD_SCRIPTSUBVIEW_H
#include <QVBoxLayout>
#include "../../model/world/commanddispatcher.hpp"
#include "../doc/subview.hpp" #include "../doc/subview.hpp"
class QModelIndex; class QModelIndex;
class QLabel; class QLabel;
class QVBoxLayout;
class QSplitter;
class QTime;
namespace CSMDoc namespace CSMDoc
{ {
@ -19,6 +26,9 @@ namespace CSMWorld
namespace CSVWorld namespace CSVWorld
{ {
class ScriptEdit; class ScriptEdit;
class RecordButtonBar;
class TableBottomBox;
class ScriptErrorTable;
class ScriptSubView : public CSVDoc::SubView class ScriptSubView : public CSVDoc::SubView
{ {
@ -28,8 +38,24 @@ namespace CSVWorld
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
CSMWorld::IdTable *mModel; CSMWorld::IdTable *mModel;
int mColumn; int mColumn;
QWidget *mBottom; int mStateColumn;
QLabel *mStatus; TableBottomBox *mBottom;
RecordButtonBar *mButtons;
CSMWorld::CommandDispatcher mCommandDispatcher;
QVBoxLayout mLayout;
QSplitter *mMain;
ScriptErrorTable *mErrors;
QTimer *mCompileDelay;
private:
void addButtonBar();
void recompile();
bool isDeleted() const;
void updateDeletedState();
public: public:
@ -41,6 +67,8 @@ namespace CSVWorld
virtual void updateUserSetting (const QString& name, const QStringList& value); virtual void updateUserSetting (const QString& name, const QStringList& value);
virtual void setStatusBar (bool show);
public slots: public slots:
void textChanged(); void textChanged();
@ -52,6 +80,14 @@ namespace CSVWorld
private slots: private slots:
void updateStatusBar(); void updateStatusBar();
void switchToRow (int row);
void switchToId (const std::string& id);
void highlightError (int line, int column);
void updateRequest();
}; };
} }

View file

@ -30,64 +30,31 @@
#include "../../model/settings/usersettings.hpp" #include "../../model/settings/usersettings.hpp"
#include "recordstatusdelegate.hpp" #include "recordstatusdelegate.hpp"
#include "tableeditidaction.hpp"
#include "util.hpp" #include "util.hpp"
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
{ {
// configure dispatcher // configure dispatcher
QModelIndexList selectedRows = selectionModel()->selectedRows(); mDispatcher->setSelection (getSelectedIds());
std::vector<std::string> records;
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
int row = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)).row();
records.push_back (mModel->data (
mModel->index (row, columnIndex)).toString().toUtf8().constData());
}
mDispatcher->setSelection (records);
std::vector<CSMWorld::UniversalId> extendedTypes = mDispatcher->getExtendedTypes(); std::vector<CSMWorld::UniversalId> extendedTypes = mDispatcher->getExtendedTypes();
mDispatcher->setExtendedTypes (extendedTypes); mDispatcher->setExtendedTypes (extendedTypes);
// create context menu // create context menu
QModelIndexList selectedRows = selectionModel()->selectedRows();
QMenu menu (this); QMenu menu (this);
/// \todo add menu items for select all and clear selection /// \todo add menu items for select all and clear selection
int currentRow = rowAt(event->y());
int currentColumn = columnAt(event->x());
if (mEditIdAction->isValidIdCell(currentRow, currentColumn))
{ {
// Request UniversalId editing from table columns. mEditIdAction->setCell(currentRow, currentColumn);
menu.addAction(mEditIdAction);
int currRow = rowAt( event->y() ), menu.addSeparator();
currCol = columnAt( event->x() );
currRow = mProxyModel->mapToSource(mProxyModel->index( currRow, 0 )).row();
CSMWorld::ColumnBase::Display colDisplay =
static_cast<CSMWorld::ColumnBase::Display>(
mModel->headerData(
currCol,
Qt::Horizontal,
CSMWorld::ColumnBase::Role_Display ).toInt());
QString cellData = mModel->data(mModel->index( currRow, currCol )).toString();
CSMWorld::UniversalId::Type colType = CSMWorld::TableMimeData::convertEnums( colDisplay );
if ( !cellData.isEmpty()
&& colType != CSMWorld::UniversalId::Type_None )
{
mEditCellAction->setText(tr("Edit '").append(cellData).append("'"));
menu.addAction( mEditCellAction );
mEditCellId = CSMWorld::UniversalId( colType, cellData.toUtf8().constData() );
}
} }
if (!mEditLock && !(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) if (!mEditLock && !(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
@ -366,10 +333,6 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord()));
addAction (mMoveDownAction); addAction (mMoveDownAction);
mEditCellAction = new QAction( tr("Edit Cell"), this );
connect( mEditCellAction, SIGNAL(triggered()), this, SLOT(editCell()) );
addAction( mEditCellAction );
mViewAction = new QAction (tr ("View"), this); mViewAction = new QAction (tr ("View"), this);
connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord())); connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord()));
addAction (mViewAction); addAction (mViewAction);
@ -378,23 +341,25 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord())); connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord()));
addAction (mPreviewAction); addAction (mPreviewAction);
/// \todo add a user option, that redirects the extended action to an input panel (in
/// the bottom bar) that lets the user select which record collections should be
/// modified.
mExtendedDeleteAction = new QAction (tr ("Extended Delete Record"), this); mExtendedDeleteAction = new QAction (tr ("Extended Delete Record"), this);
connect (mExtendedDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedDelete())); connect (mExtendedDeleteAction, SIGNAL (triggered()), this, SLOT (executeExtendedDelete()));
addAction (mExtendedDeleteAction); addAction (mExtendedDeleteAction);
mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this); mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this);
connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert())); connect (mExtendedRevertAction, SIGNAL (triggered()), this, SLOT (executeExtendedRevert()));
addAction (mExtendedRevertAction); addAction (mExtendedRevertAction);
mEditIdAction = new TableEditIdAction (*this, this);
connect (mEditIdAction, SIGNAL (triggered()), this, SLOT (editCell()));
addAction (mEditIdAction);
connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate())); this, SLOT (tableSizeUpdate()));
connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), //connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (rowsInsertedEvent(const QModelIndex&, int, int))); // this, SLOT (rowsInsertedEvent(const QModelIndex&, int, int)));
connect (mProxyModel, SIGNAL (rowAdded (const std::string &)),
this, SLOT (rowAdded (const std::string &)));
/// \note This signal could instead be connected to a slot that filters out changes not affecting /// \note This signal could instead be connected to a slot that filters out changes not affecting
/// the records status column (for permanence reasons) /// the records status column (for permanence reasons)
@ -451,6 +416,22 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData()); mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData());
} }
std::vector<std::string> CSVWorld::Table::getSelectedIds() const
{
std::vector<std::string> ids;
QModelIndexList selectedRows = selectionModel()->selectedRows();
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
for (QModelIndexList::const_iterator iter (selectedRows.begin());
iter != selectedRows.end();
++iter)
{
int row = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)).row();
ids.push_back (mModel->data (mModel->index (row, columnIndex)).toString().toUtf8().constData());
}
return ids;
}
void CSVWorld::Table::editRecord() void CSVWorld::Table::editRecord()
{ {
if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
@ -468,7 +449,7 @@ void CSVWorld::Table::cloneRecord()
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row());
if (selectedRows.size()==1 && !mModel->isDeleted (toClone.getId())) if (selectedRows.size() == 1)
{ {
emit cloneRequest (toClone); emit cloneRequest (toClone);
} }
@ -543,7 +524,7 @@ void CSVWorld::Table::moveDownRecord()
void CSVWorld::Table::editCell() void CSVWorld::Table::editCell()
{ {
emit editRequest( mEditCellId, std::string() ); emit editRequest(mEditIdAction->getCurrentId(), "");
} }
void CSVWorld::Table::viewRecord() void CSVWorld::Table::viewRecord()
@ -583,6 +564,34 @@ void CSVWorld::Table::previewRecord()
} }
} }
void CSVWorld::Table::executeExtendedDelete()
{
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
QString configSetting = settings.settingValue ("table-input/extended-config");
if (configSetting == "true")
{
emit extendedDeleteConfigRequest(getSelectedIds());
}
else
{
QMetaObject::invokeMethod(mDispatcher, "executeExtendedDelete", Qt::QueuedConnection);
}
}
void CSVWorld::Table::executeExtendedRevert()
{
CSMSettings::UserSettings &settings = CSMSettings::UserSettings::instance();
QString configSetting = settings.settingValue ("table-input/extended-config");
if (configSetting == "true")
{
emit extendedRevertConfigRequest(getSelectedIds());
}
else
{
QMetaObject::invokeMethod(mDispatcher, "executeExtendedRevert", Qt::QueuedConnection);
}
}
void CSVWorld::Table::updateUserSetting (const QString &name, const QStringList &list) void CSVWorld::Table::updateUserSetting (const QString &name, const QStringList &list)
{ {
if (name=="table-input/jump-to-added") if (name=="table-input/jump-to-added")
@ -693,10 +702,6 @@ void CSVWorld::Table::tableSizeUpdate()
} }
emit tableSizeChanged (size, deleted, modified); emit tableSizeChanged (size, deleted, modified);
// not really related to tableSizeUpdate() but placed here for convenience rather than
// creating a bunch of extra connections & slot
mProxyModel->refreshFilter();
} }
void CSVWorld::Table::selectionSizeUpdate() void CSVWorld::Table::selectionSizeUpdate()
@ -816,12 +821,13 @@ void CSVWorld::Table::globalFilterModifiedChanged(int state)
recordFilterChanged(mFilter); recordFilterChanged(mFilter);
} }
void CSVWorld::Table::rowsInsertedEvent(const QModelIndex& parent, int start, int end) void CSVWorld::Table::rowAdded(const std::string &id)
{ {
tableSizeUpdate(); tableSizeUpdate();
if(mJumpToAddedRecord) if(mJumpToAddedRecord)
{ {
selectRow(end); int idColumn = mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
selectRow(mProxyModel->getModelIndex(id, idColumn).row());
if(mUnselectAfterJump) if(mUnselectAfterJump)
clearSelection(); clearSelection();

View file

@ -30,6 +30,7 @@ namespace CSMWorld
namespace CSVWorld namespace CSVWorld
{ {
class CommandDelegate; class CommandDelegate;
class TableEditIdAction;
///< Table widget ///< Table widget
class Table : public DragRecordTable class Table : public DragRecordTable
@ -57,15 +58,14 @@ namespace CSVWorld
QAction *mMoveUpAction; QAction *mMoveUpAction;
QAction *mMoveDownAction; QAction *mMoveDownAction;
QAction *mViewAction; QAction *mViewAction;
QAction *mEditCellAction;
QAction *mPreviewAction; QAction *mPreviewAction;
QAction *mExtendedDeleteAction; QAction *mExtendedDeleteAction;
QAction *mExtendedRevertAction; QAction *mExtendedRevertAction;
TableEditIdAction *mEditIdAction;
CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTableBase *mModel; CSMWorld::IdTableBase *mModel;
int mRecordStatusDisplay; int mRecordStatusDisplay;
CSMWorld::CommandDispatcher *mDispatcher; CSMWorld::CommandDispatcher *mDispatcher;
CSMWorld::UniversalId mEditCellId;
std::map<Qt::KeyboardModifiers, DoubleClickAction> mDoubleClickActions; std::map<Qt::KeyboardModifiers, DoubleClickAction> mDoubleClickActions;
bool mJumpToAddedRecord; bool mJumpToAddedRecord;
bool mUnselectAfterJump; bool mUnselectAfterJump;
@ -97,6 +97,8 @@ namespace CSVWorld
std::vector<std::string> getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; std::vector<std::string> getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const;
std::vector<std::string> getSelectedIds() const;
virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const; virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const;
signals: signals:
@ -116,6 +118,10 @@ namespace CSVWorld
void closeRequest(); void closeRequest();
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);
private slots: private slots:
void editCell(); void editCell();
@ -132,6 +138,10 @@ namespace CSVWorld
void previewRecord(); void previewRecord();
void executeExtendedDelete();
void executeExtendedRevert();
public slots: public slots:
void tableSizeUpdate(); void tableSizeUpdate();
@ -148,7 +158,7 @@ namespace CSVWorld
void globalFilterModifiedChanged (int state); void globalFilterModifiedChanged (int state);
void rowsInsertedEvent(const QModelIndex& parent, int start, int end); void rowAdded(const std::string &id);
}; };
} }

View file

@ -6,9 +6,25 @@
#include <QStatusBar> #include <QStatusBar>
#include <QStackedLayout> #include <QStackedLayout>
#include <QLabel> #include <QLabel>
#include <QEvent>
#include <QKeyEvent>
#include "creator.hpp" #include "creator.hpp"
void CSVWorld::TableBottomBox::updateSize()
{
// Make sure that the size of the bottom box is determined by the currently visible widget
for (int i = 0; i < mLayout->count(); ++i)
{
QSizePolicy::Policy verPolicy = QSizePolicy::Ignored;
if (mLayout->widget(i) == mLayout->currentWidget())
{
verPolicy = QSizePolicy::Expanding;
}
mLayout->widget(i)->setSizePolicy(QSizePolicy::Expanding, verPolicy);
}
}
void CSVWorld::TableBottomBox::updateStatus() void CSVWorld::TableBottomBox::updateStatus()
{ {
if (mShowStatusBar) if (mShowStatusBar)
@ -35,15 +51,33 @@ void CSVWorld::TableBottomBox::updateStatus()
} }
} }
if (mHasPosition)
{
if (!first)
stream << " -- ";
stream << "(" << mRow << ", " << mColumn << ")";
}
mStatus->setText (QString::fromUtf8 (stream.str().c_str())); mStatus->setText (QString::fromUtf8 (stream.str().c_str()));
} }
} }
void CSVWorld::TableBottomBox::extendedConfigRequest(CSVWorld::ExtendedCommandConfigurator::Mode mode,
const std::vector<std::string> &selectedIds)
{
mExtendedConfigurator->configure (mode, selectedIds);
mLayout->setCurrentWidget (mExtendedConfigurator);
mEditMode = EditMode_ExtendedConfig;
setVisible (true);
mExtendedConfigurator->setFocus();
}
CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory,
CSMDoc::Document& document, CSMDoc::Document& document,
const CSMWorld::UniversalId& id, const CSMWorld::UniversalId& id,
QWidget *parent) QWidget *parent)
: QWidget (parent), mShowStatusBar (false), mCreating (false) : QWidget (parent), mShowStatusBar (false), mEditMode(EditMode_None), mHasPosition (false)
{ {
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
mStatusCount[i] = 0; mStatusCount[i] = 0;
@ -52,6 +86,7 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto
mLayout = new QStackedLayout; mLayout = new QStackedLayout;
mLayout->setContentsMargins (0, 0, 0, 0); mLayout->setContentsMargins (0, 0, 0, 0);
connect (mLayout, SIGNAL (currentChanged (int)), this, SLOT (currentWidgetChanged (int)));
mStatus = new QLabel; mStatus = new QLabel;
@ -67,19 +102,28 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto
if (mCreator) if (mCreator)
{ {
mCreator->installEventFilter(this);
mLayout->addWidget (mCreator); mLayout->addWidget (mCreator);
connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone())); connect (mCreator, SIGNAL (done()), this, SLOT (requestDone()));
connect (mCreator, SIGNAL (requestFocus (const std::string&)), connect (mCreator, SIGNAL (requestFocus (const std::string&)),
this, SIGNAL (requestFocus (const std::string&))); this, SIGNAL (requestFocus (const std::string&)));
} }
mExtendedConfigurator = new ExtendedCommandConfigurator (document, id, this);
mExtendedConfigurator->installEventFilter(this);
mLayout->addWidget (mExtendedConfigurator);
connect (mExtendedConfigurator, SIGNAL (done()), this, SLOT (requestDone()));
updateSize();
} }
void CSVWorld::TableBottomBox::setEditLock (bool locked) void CSVWorld::TableBottomBox::setEditLock (bool locked)
{ {
if (mCreator) if (mCreator)
mCreator->setEditLock (locked); mCreator->setEditLock (locked);
mExtendedConfigurator->setEditLock (locked);
} }
CSVWorld::TableBottomBox::~TableBottomBox() CSVWorld::TableBottomBox::~TableBottomBox()
@ -87,11 +131,25 @@ CSVWorld::TableBottomBox::~TableBottomBox()
delete mCreator; delete mCreator;
} }
bool CSVWorld::TableBottomBox::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Escape)
{
requestDone();
return true;
}
}
return QWidget::eventFilter(object, event);
}
void CSVWorld::TableBottomBox::setStatusBar (bool show) void CSVWorld::TableBottomBox::setStatusBar (bool show)
{ {
if (show!=mShowStatusBar) if (show!=mShowStatusBar)
{ {
setVisible (show || mCreating); setVisible (show || (mEditMode != EditMode_None));
mShowStatusBar = show; mShowStatusBar = show;
@ -105,7 +163,7 @@ bool CSVWorld::TableBottomBox::canCreateAndDelete() const
return mCreator; return mCreator;
} }
void CSVWorld::TableBottomBox::createRequestDone() void CSVWorld::TableBottomBox::requestDone()
{ {
if (!mShowStatusBar) if (!mShowStatusBar)
setVisible (false); setVisible (false);
@ -113,8 +171,12 @@ void CSVWorld::TableBottomBox::createRequestDone()
updateStatus(); updateStatus();
mLayout->setCurrentWidget (mStatusBar); mLayout->setCurrentWidget (mStatusBar);
mEditMode = EditMode_None;
}
mCreating = false; void CSVWorld::TableBottomBox::currentWidgetChanged(int /*index*/)
{
updateSize();
} }
void CSVWorld::TableBottomBox::selectionSizeChanged (int size) void CSVWorld::TableBottomBox::selectionSizeChanged (int size)
@ -152,13 +214,27 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi
updateStatus(); updateStatus();
} }
void CSVWorld::TableBottomBox::positionChanged (int row, int column)
{
mRow = row;
mColumn = column;
mHasPosition = true;
updateStatus();
}
void CSVWorld::TableBottomBox::noMorePosition()
{
mHasPosition = false;
updateStatus();
}
void CSVWorld::TableBottomBox::createRequest() void CSVWorld::TableBottomBox::createRequest()
{ {
mCreator->reset(); mCreator->reset();
mCreator->toggleWidgets(true); mCreator->toggleWidgets(true);
mLayout->setCurrentWidget (mCreator); mLayout->setCurrentWidget (mCreator);
setVisible (true); setVisible (true);
mCreating = true; mEditMode = EditMode_Creation;
mCreator->focus(); mCreator->focus();
} }
@ -170,6 +246,16 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
mLayout->setCurrentWidget(mCreator); mLayout->setCurrentWidget(mCreator);
mCreator->toggleWidgets(false); mCreator->toggleWidgets(false);
setVisible (true); setVisible (true);
mCreating = true; mEditMode = EditMode_Creation;
mCreator->focus(); mCreator->focus();
} }
void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds)
{
extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds);
}
void CSVWorld::TableBottomBox::extendedRevertConfigRequest(const std::vector<std::string> &selectedIds)
{
extendedConfigRequest(ExtendedCommandConfigurator::Mode_Revert, selectedIds);
}

View file

@ -4,10 +4,11 @@
#include <QWidget> #include <QWidget>
#include <apps/opencs/model/world/universalid.hpp> #include <apps/opencs/model/world/universalid.hpp>
#include "extendedcommandconfigurator.hpp"
class QLabel; class QLabel;
class QStackedLayout; class QStackedLayout;
class QStatusBar; class QStatusBar;
class QUndoStack;
namespace CSMDoc namespace CSMDoc
{ {
@ -23,13 +24,21 @@ namespace CSVWorld
{ {
Q_OBJECT Q_OBJECT
enum EditMode { EditMode_None, EditMode_Creation, EditMode_ExtendedConfig };
bool mShowStatusBar; bool mShowStatusBar;
QLabel *mStatus; QLabel *mStatus;
QStatusBar *mStatusBar; QStatusBar *mStatusBar;
int mStatusCount[4]; int mStatusCount[4];
EditMode mEditMode;
Creator *mCreator; Creator *mCreator;
bool mCreating; ExtendedCommandConfigurator *mExtendedConfigurator;
QStackedLayout *mLayout; QStackedLayout *mLayout;
bool mHasPosition;
int mRow;
int mColumn;
private: private:
@ -37,8 +46,13 @@ namespace CSVWorld
TableBottomBox (const TableBottomBox&); TableBottomBox (const TableBottomBox&);
TableBottomBox& operator= (const TableBottomBox&); TableBottomBox& operator= (const TableBottomBox&);
void updateSize();
void updateStatus(); void updateStatus();
void extendedConfigRequest(ExtendedCommandConfigurator::Mode mode,
const std::vector<std::string> &selectedIds);
public: public:
TableBottomBox (const CreatorFactoryBase& creatorFactory, TableBottomBox (const CreatorFactoryBase& creatorFactory,
@ -48,6 +62,8 @@ namespace CSVWorld
virtual ~TableBottomBox(); virtual ~TableBottomBox();
virtual bool eventFilter(QObject *object, QEvent *event);
void setEditLock (bool locked); void setEditLock (bool locked);
void setStatusBar (bool show); void setStatusBar (bool show);
@ -65,9 +81,11 @@ namespace CSVWorld
private slots: private slots:
void createRequestDone(); void requestDone();
///< \note This slot being called does not imply success. ///< \note This slot being called does not imply success.
void currentWidgetChanged(int index);
public slots: public slots:
void selectionSizeChanged (int size); void selectionSizeChanged (int size);
@ -77,9 +95,16 @@ namespace CSVWorld
/// \param deleted Number of deleted records /// \param deleted Number of deleted records
/// \param modified Number of added and modified records /// \param modified Number of added and modified records
void positionChanged (int row, int column);
void noMorePosition();
void createRequest(); void createRequest();
void cloneRequest(const std::string& id, void cloneRequest(const std::string& id,
const CSMWorld::UniversalId::Type type); const CSMWorld::UniversalId::Type type);
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);
}; };
} }

View file

@ -0,0 +1,49 @@
#include "tableeditidaction.hpp"
#include <QTableView>
#include "../../model/world/tablemimedata.hpp"
CSVWorld::TableEditIdAction::CellData CSVWorld::TableEditIdAction::getCellData(int row, int column) const
{
QModelIndex index = mTable.model()->index(row, column);
if (index.isValid())
{
QVariant display = mTable.model()->data(index, CSMWorld::ColumnBase::Role_Display);
QString value = mTable.model()->data(index).toString();
return std::make_pair(static_cast<CSMWorld::ColumnBase::Display>(display.toInt()), value);
}
return std::make_pair(CSMWorld::ColumnBase::Display_None, "");
}
CSVWorld::TableEditIdAction::TableEditIdAction(const QTableView &table, QWidget *parent)
: QAction(parent),
mTable(table),
mCurrentId(CSMWorld::UniversalId::Type_None)
{}
void CSVWorld::TableEditIdAction::setCell(int row, int column)
{
CellData data = getCellData(row, column);
CSMWorld::UniversalId::Type idType = CSMWorld::TableMimeData::convertEnums(data.first);
if (idType != CSMWorld::UniversalId::Type_None)
{
mCurrentId = CSMWorld::UniversalId(idType, data.second.toUtf8().constData());
setText("Edit '" + data.second + "'");
}
}
CSMWorld::UniversalId CSVWorld::TableEditIdAction::getCurrentId() const
{
return mCurrentId;
}
bool CSVWorld::TableEditIdAction::isValidIdCell(int row, int column) const
{
CellData data = getCellData(row, column);
CSMWorld::UniversalId::Type idType = CSMWorld::TableMimeData::convertEnums(data.first);
return CSMWorld::ColumnBase::isId(data.first) &&
idType != CSMWorld::UniversalId::Type_None &&
!data.second.isEmpty();
}

View file

@ -0,0 +1,31 @@
#ifndef CSVWORLD_TABLEEDITIDACTION_HPP
#define CSVWORLD_TABLEEDITIDACTION_HPP
#include <QAction>
#include "../../model/world/columnbase.hpp"
#include "../../model/world/universalid.hpp"
class QTableView;
namespace CSVWorld
{
class TableEditIdAction : public QAction
{
const QTableView &mTable;
CSMWorld::UniversalId mCurrentId;
typedef std::pair<CSMWorld::ColumnBase::Display, QString> CellData;
CellData getCellData(int row, int column) const;
public:
TableEditIdAction(const QTableView &table, QWidget *parent = 0);
void setCell(int row, int column);
CSMWorld::UniversalId getCurrentId() const;
bool isValidIdCell(int row, int column) const;
};
}
#endif

View file

@ -26,8 +26,6 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
{ {
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins (QMargins (0, 0, 0, 0));
layout->addWidget (mBottom = layout->addWidget (mBottom =
new TableBottomBox (creatorFactory, document, id, this), 0); new TableBottomBox (creatorFactory, document, id, this), 0);
@ -91,6 +89,11 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)),
mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)));
connect (mTable, SIGNAL(extendedDeleteConfigRequest(const std::vector<std::string> &)),
mBottom, SLOT(extendedDeleteConfigRequest(const std::vector<std::string> &)));
connect (mTable, SIGNAL(extendedRevertConfigRequest(const std::vector<std::string> &)),
mBottom, SLOT(extendedRevertConfigRequest(const std::vector<std::string> &)));
} }
connect (mBottom, SIGNAL (requestFocus (const std::string&)), connect (mBottom, SIGNAL (requestFocus (const std::string&)),
mTable, SLOT (requestFocus (const std::string&))); mTable, SLOT (requestFocus (const std::string&)));
@ -185,4 +188,3 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event)
} }
return false; return false;
} }

View file

@ -194,7 +194,10 @@ if (GIT_CHECKOUT)
endif (GIT_CHECKOUT) endif (GIT_CHECKOUT)
if (WIN32) if (WIN32)
target_link_libraries(components shlwapi) target_link_libraries(components shlwapi)
if(MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOGDI")
endif(MINGW)
endif() endif()
# Fix for not visible pthreads functions for linker with glibc 2.15 # Fix for not visible pthreads functions for linker with glibc 2.15

View file

@ -281,8 +281,10 @@ namespace Compiler
if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"') if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"')
{ {
name = name.substr (1, name.size()-2); name = name.substr (1, name.size()-2);
cont = parser.parseName (name, loc, *this); // allow keywords enclosed in ""
return true; /// \todo optionally disable
// cont = parser.parseName (name, loc, *this);
// return true;
} }
int i = 0; int i = 0;