mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-28 23:36:41 +00:00
Merge pull request #7 from cc9cii/master-cherry-pick
Merge master (cherry pick)
This commit is contained in:
commit
2e6a8e1b18
57 changed files with 1903 additions and 294 deletions
|
@ -59,6 +59,7 @@ Programmers
|
|||
Julien Voisin (jvoisin/ap0)
|
||||
Karl-Felix Glatzer (k1ll)
|
||||
Kevin Poitra (PuppyKevin)
|
||||
Koncord
|
||||
Lars Söderberg (Lazaroth)
|
||||
lazydev
|
||||
Leon Saunders (emoose)
|
||||
|
|
|
@ -421,6 +421,9 @@ IF(NOT WIN32 AND NOT APPLE)
|
|||
IF(BUILD_ESMTOOL)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_ESMTOOL)
|
||||
IF(BUILD_NIFTEST)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_NIFTEST)
|
||||
IF(BUILD_MWINIIMPORTER)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_MWINIIMPORTER)
|
||||
|
@ -617,6 +620,10 @@ if (BUILD_WIZARD)
|
|||
add_subdirectory(apps/wizard)
|
||||
endif()
|
||||
|
||||
if (BUILD_NIFTEST)
|
||||
add_subdirectory(apps/niftest)
|
||||
endif(BUILD_NIFTEST)
|
||||
|
||||
# UnitTests
|
||||
if (BUILD_UNITTESTS)
|
||||
add_subdirectory( apps/openmw_test_suite )
|
||||
|
|
|
@ -309,11 +309,11 @@ bool Launcher::MainDialog::setupGameSettings()
|
|||
mGameSettings.readUserFile(stream);
|
||||
}
|
||||
|
||||
// Now the rest
|
||||
// Now the rest - priority: user > local > global
|
||||
QStringList paths;
|
||||
paths.append(userPath + QString("openmw.cfg"));
|
||||
paths.append(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) {
|
||||
qDebug() << "Loading config file:" << qPrintable(path);
|
||||
|
|
19
apps/niftest/CMakeLists.txt
Normal file
19
apps/niftest/CMakeLists.txt
Normal 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
28
apps/niftest/find_bad_nifs.sh
Executable 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
87
apps/niftest/niftest.cpp
Normal 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;
|
||||
}
|
|
@ -64,7 +64,7 @@ opencs_units (view/world
|
|||
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
|
||||
cellcreator referenceablecreator referencecreator scenesubview
|
||||
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
|
||||
dialoguespinbox recordbuttonbar
|
||||
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/world
|
||||
|
|
|
@ -799,9 +799,9 @@ void CSMDoc::Document::addGmsts()
|
|||
"sBookSkillMessage",
|
||||
"sBounty",
|
||||
"sBreath",
|
||||
"sBribe",
|
||||
"sBribe",
|
||||
"sBribe",
|
||||
"sBribe 10 Gold",
|
||||
"sBribe 100 Gold",
|
||||
"sBribe 1000 Gold",
|
||||
"sBribeFail",
|
||||
"sBribeSuccess",
|
||||
"sBuy",
|
||||
|
|
|
@ -8,6 +8,20 @@ CSMDoc::Message::Message (const CSMWorld::UniversalId& id, const std::string& me
|
|||
: 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_)
|
||||
: mDefault (default_)
|
||||
|
|
|
@ -31,6 +31,8 @@ namespace CSMDoc
|
|||
|
||||
Message (const CSMWorld::UniversalId& id, const std::string& message,
|
||||
const std::string& hint, Severity severity);
|
||||
|
||||
static std::string toString (Severity severity);
|
||||
};
|
||||
|
||||
class Messages
|
||||
|
|
|
@ -189,7 +189,7 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
|
|||
ritd->setDeclaredValues (values);
|
||||
}
|
||||
|
||||
declareSection ("table-input", "Table Input");
|
||||
declareSection ("table-input", "ID Tables");
|
||||
{
|
||||
QString inPlaceEdit ("Edit in Place");
|
||||
QString editRecord ("Edit Record");
|
||||
|
@ -242,9 +242,24 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
|
|||
"Jump to the added or cloned record.");
|
||||
jumpToAdded->setDefaultValue (defaultValue);
|
||||
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 edit ("Edit");
|
||||
|
@ -337,6 +352,15 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
|
|||
"<li>Strict: Promote warning to an error</li>"
|
||||
"</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");
|
||||
formatInt->setDefaultValues (QStringList() << "Dark magenta");
|
||||
formatInt->setToolTip ("(Default: Green) Use one of the following formats:" + tooltip);
|
||||
|
|
|
@ -85,14 +85,8 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
|
|||
|
||||
if (index.column()==mColumnSeverity)
|
||||
{
|
||||
switch (mRows.at (index.row()).mSeverity)
|
||||
{
|
||||
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 QString::fromUtf8 (
|
||||
CSMDoc::Message::toString (mRows.at (index.row()).mSeverity).c_str());
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
|
|
@ -92,7 +92,7 @@ namespace CSMWorld
|
|||
{ ColumnId_Trainer, "Trainer" },
|
||||
{ ColumnId_Spellmaking, "Spellmaking" },
|
||||
{ ColumnId_EnchantingService, "Enchanting Service" },
|
||||
{ ColumnId_RepairService, "Repair Serivce" },
|
||||
{ ColumnId_RepairService, "Repair Service" },
|
||||
{ ColumnId_ApparatusType, "Apparatus Type" },
|
||||
{ ColumnId_ArmorType, "Armor Type" },
|
||||
{ ColumnId_Health, "Health" },
|
||||
|
|
|
@ -58,6 +58,25 @@ void CSMWorld::CreateCommand::applyModifications()
|
|||
{
|
||||
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter)
|
||||
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)
|
||||
|
@ -71,6 +90,11 @@ void CSMWorld::CreateCommand::addValue (int column, const QVariant& 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)
|
||||
{
|
||||
mType = type;
|
||||
|
|
|
@ -48,6 +48,9 @@ namespace CSMWorld
|
|||
class CreateCommand : public QUndoCommand
|
||||
{
|
||||
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:
|
||||
|
||||
|
@ -68,6 +71,8 @@ namespace CSMWorld
|
|||
|
||||
void addValue (int column, const QVariant& value);
|
||||
|
||||
void addNestedValue(int parentColumn, int nestedColumn, const QVariant &value);
|
||||
|
||||
virtual void redo();
|
||||
|
||||
virtual void undo();
|
||||
|
|
|
@ -548,6 +548,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
|
|||
|
||||
mMetaData.addColumn (new StringIdColumn<MetaData> (true));
|
||||
mMetaData.addColumn (new RecordStateColumn<MetaData>);
|
||||
mMetaData.addColumn (new FixedRecordTypeColumn<MetaData> (UniversalId::Type_MetaData));
|
||||
mMetaData.addColumn (new FormatColumn<MetaData>);
|
||||
mMetaData.addColumn (new AuthorColumn<MetaData>);
|
||||
mMetaData.addColumn (new FileDescriptionColumn<MetaData>);
|
||||
|
|
|
@ -7,23 +7,23 @@
|
|||
|
||||
void CSMWorld::IdTableProxyModel::updateColumnMap()
|
||||
{
|
||||
mColumnMap.clear();
|
||||
Q_ASSERT(mSourceModel != NULL);
|
||||
|
||||
mColumnMap.clear();
|
||||
if (mFilter)
|
||||
{
|
||||
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)
|
||||
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)
|
||||
const
|
||||
{
|
||||
Q_ASSERT(mSourceModel != NULL);
|
||||
|
||||
// It is not possible to use filterAcceptsColumn() and check for
|
||||
// sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags)
|
||||
// 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)
|
||||
return true;
|
||||
|
||||
return mFilter->test (
|
||||
dynamic_cast<IdTableBase&> (*sourceModel()), sourceRow, mColumnMap);
|
||||
return mFilter->test (*mSourceModel, sourceRow, mColumnMap);
|
||||
}
|
||||
|
||||
CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
|
||||
: QSortFilterProxyModel (parent)
|
||||
: QSortFilterProxyModel (parent),
|
||||
mSourceModel(NULL)
|
||||
{
|
||||
setSortCaseSensitivity (Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -60,11 +81,52 @@ void CSMWorld::IdTableProxyModel::setFilter (const boost::shared_ptr<CSMFilter::
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
updateColumnMap();
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "../filter/node.hpp"
|
||||
|
||||
#include "columns.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class IdTableProxyModel : public QSortFilterProxyModel
|
||||
|
@ -20,6 +22,15 @@ namespace CSMWorld
|
|||
boost::shared_ptr<CSMFilter::Node> mFilter;
|
||||
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:
|
||||
|
||||
void updateColumnMap();
|
||||
|
@ -30,15 +41,31 @@ namespace CSMWorld
|
|||
|
||||
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 refreshFilter();
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -171,10 +171,10 @@ QModelIndex CSMWorld::IdTree::index (int row, int column, const QModelIndex& par
|
|||
encodedId = this->foldIndexAddress(parent);
|
||||
}
|
||||
|
||||
if (row<0 || row>=idCollection()->getSize())
|
||||
if (row < 0 || row >= rowCount(parent))
|
||||
return QModelIndex();
|
||||
|
||||
if (column<0 || column>=idCollection()->getColumns())
|
||||
if (column < 0 || column >= columnCount(parent))
|
||||
return QModelIndex();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,12 @@ namespace CSMWorld
|
|||
|
||||
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:
|
||||
|
||||
void resetStart(const QString& id);
|
||||
|
|
|
@ -9,16 +9,17 @@ namespace
|
|||
{
|
||||
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)
|
||||
: IdTableProxyModel(parent),
|
||||
mType(type),
|
||||
mSourceModel(NULL),
|
||||
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);
|
||||
}
|
||||
|
@ -26,23 +27,18 @@ CSMWorld::InfoTableProxyModel::InfoTableProxyModel(CSMWorld::UniversalId::Type t
|
|||
void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
|
||||
{
|
||||
IdTableProxyModel::setSourceModel(sourceModel);
|
||||
mSourceModel = dynamic_cast<IdTableBase *>(sourceModel);
|
||||
|
||||
if (mSourceModel != NULL)
|
||||
{
|
||||
connect(mSourceModel,
|
||||
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)));
|
||||
mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId);
|
||||
mFirstRowCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
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 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
|
||||
{
|
||||
Q_ASSERT(mSourceModel != NULL);
|
||||
|
||||
int row = currentRow;
|
||||
int column = mSourceModel->findColumnIndex(mInfoColumnId);
|
||||
int column = mInfoColumnIndex;
|
||||
QString info = toLower(mSourceModel->data(mSourceModel->index(row, column)).toString());
|
||||
|
||||
if (mFirstRowCache.contains(info))
|
||||
|
@ -73,7 +71,41 @@ int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const
|
|||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,25 +16,29 @@ namespace CSMWorld
|
|||
Q_OBJECT
|
||||
|
||||
UniversalId::Type mType;
|
||||
IdTableBase *mSourceModel;
|
||||
Columns::ColumnId mInfoColumnId;
|
||||
///< Contains ID for Topic or Journal ID
|
||||
int mInfoColumnIndex;
|
||||
int mLastAddedSourceRow;
|
||||
|
||||
mutable QHash<QString, int> mFirstRowCache;
|
||||
|
||||
int getFirstInfoRow(int currentRow) const;
|
||||
///< Finds the first row with the same topic (journal entry) as in \a currentRow
|
||||
///< \a currentRow is a row of the source model.
|
||||
|
||||
public:
|
||||
InfoTableProxyModel(UniversalId::Type type, QObject *parent = 0);
|
||||
|
||||
void setSourceModel(QAbstractItemModel *sourceModel);
|
||||
virtual void setSourceModel(QAbstractItemModel *sourceModel);
|
||||
|
||||
protected:
|
||||
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
|
||||
|
||||
private slots:
|
||||
void modelRowsChanged(const QModelIndex &parent, int start, int end);
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -15,3 +15,28 @@ int CSMWorld::NestedCollection::getNestedColumnsCount(int row, int column) const
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef CSM_WOLRD_NESTEDCOLLECTION_H
|
||||
#define CSM_WOLRD_NESTEDCOLLECTION_H
|
||||
|
||||
#include "columns.hpp"
|
||||
|
||||
class QVariant;
|
||||
|
||||
namespace CSMWorld
|
||||
|
@ -33,6 +35,12 @@ namespace CSMWorld
|
|||
virtual int getNestedColumnsCount(int row, int column) const;
|
||||
|
||||
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.
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -161,8 +161,19 @@ namespace CSMWorld
|
|||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedColumnsCount(int row, int column) const
|
||||
{
|
||||
return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).getColumnsCount(
|
||||
Collection<ESXRecordT, IdAccessorT>::getRecord(row));
|
||||
const ColumnBase &nestedColumn = Collection<ESXRecordT, IdAccessorT>::getColumn(column);
|
||||
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>
|
||||
|
|
|
@ -75,10 +75,10 @@ QModelIndex CSMWorld::NestedTableProxyModel::index(int row, int column, const QM
|
|||
{
|
||||
assert (!parent.isValid());
|
||||
|
||||
int rows = mMainModel->rowCount(parent);
|
||||
int columns = mMainModel->columnCount(parent);
|
||||
int numRows = rowCount(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 createIndex(row, column);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <QPainter>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "../../model/tools/reportmodel.hpp"
|
||||
|
||||
|
@ -63,7 +64,7 @@ void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)
|
|||
for (QModelIndexList::const_iterator iter (selectedRows.begin());
|
||||
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')
|
||||
{
|
||||
|
@ -152,7 +153,10 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
|
|||
setSelectionBehavior (QAbstractItemView::SelectRows);
|
||||
setSelectionMode (QAbstractItemView::ExtendedSelection);
|
||||
|
||||
setModel (mModel);
|
||||
mProxyModel = new QSortFilterProxyModel (this);
|
||||
mProxyModel->setSourceModel (mModel);
|
||||
|
||||
setModel (mProxyModel);
|
||||
setColumnHidden (2, true);
|
||||
|
||||
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();
|
||||
++iter)
|
||||
{
|
||||
ids.push_back (mModel->getUniversalId (iter->row()));
|
||||
ids.push_back (mModel->getUniversalId (mProxyModel->mapToSource (*iter).row()));
|
||||
}
|
||||
|
||||
return ids;
|
||||
|
@ -245,13 +249,22 @@ std::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const
|
|||
{
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
std::vector<int> rows;
|
||||
|
||||
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
|
||||
++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')
|
||||
indices.push_back (iter->row());
|
||||
indices.push_back (*iter);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -279,18 +292,28 @@ void CSVTools::ReportTable::showSelection()
|
|||
|
||||
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
|
||||
++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()
|
||||
{
|
||||
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)
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "../world/dragrecordtable.hpp"
|
||||
|
||||
class QAction;
|
||||
class QSortFilterProxyModel;
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
|
@ -31,6 +32,7 @@ namespace CSVTools
|
|||
Action_EditAndRemove
|
||||
};
|
||||
|
||||
QSortFilterProxyModel *mProxyModel;
|
||||
CSMTools::ReportModel *mModel;
|
||||
CSVWorld::CommandDelegate *mIdTypeDelegate;
|
||||
QAction *mShowAction;
|
||||
|
@ -63,11 +65,14 @@ namespace CSVTools
|
|||
|
||||
void clear();
|
||||
|
||||
// Return indices of rows that are suitable for replacement.
|
||||
//
|
||||
// \param selection Only list selected rows.
|
||||
/// Return indices of rows that are suitable for replacement.
|
||||
///
|
||||
/// \param selection Only list selected rows.
|
||||
///
|
||||
/// \return rows in the original model
|
||||
std::vector<int> getReplaceIndices (bool selection) const;
|
||||
|
||||
/// \param index row in the original model
|
||||
void flagAsReplaced (int index);
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -71,8 +71,6 @@ CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc:
|
|||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
|
||||
layout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
layout->addWidget (&mSearchBox);
|
||||
|
||||
layout->addWidget (mTable = new ReportTable (document, id, true), 2);
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
#include <QSpinBox>
|
||||
#include <QLabel>
|
||||
|
||||
#include "../../model/world/commands.hpp"
|
||||
#include "../../model/world/idtree.hpp"
|
||||
|
||||
std::string CSVWorld::CellCreator::getId() const
|
||||
{
|
||||
if (mType->currentIndex()==0)
|
||||
|
@ -20,6 +23,15 @@ std::string CSVWorld::CellCreator::getId() const
|
|||
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,
|
||||
const CSMWorld::UniversalId& id)
|
||||
: GenericCreator (data, undoStack, id)
|
||||
|
@ -95,9 +107,16 @@ void CSVWorld::CellCreator::cloneMode(const std::string& originId,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void CSVWorld::CellCreator::toggleWidgets(bool active)
|
||||
std::string CSVWorld::CellCreator::getErrors() const
|
||||
{
|
||||
CSVWorld::GenericCreator::toggleWidgets(active);
|
||||
mType->setEnabled(active);
|
||||
std::string errors;
|
||||
if (mType->currentIndex() == 0)
|
||||
{
|
||||
errors = GenericCreator::getErrors();
|
||||
}
|
||||
else if (getData().hasId(getId()))
|
||||
{
|
||||
errors = "The Exterior Cell is already exist";
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
|
|
@ -23,17 +23,22 @@ namespace CSVWorld
|
|||
|
||||
virtual std::string getId() const;
|
||||
|
||||
/// Allow subclasses to add additional data to \a command.
|
||||
virtual void configureCreateCommand(CSMWorld::CreateCommand& command) const;
|
||||
|
||||
public:
|
||||
|
||||
CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);
|
||||
|
||||
virtual void reset();
|
||||
|
||||
virtual void toggleWidgets(bool active = true);
|
||||
|
||||
virtual void cloneMode(const std::string& originId,
|
||||
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:
|
||||
|
||||
void setType (int index);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <QComboBox>
|
||||
#include <QHeaderView>
|
||||
#include <QScrollBar>
|
||||
#include <QMenu>
|
||||
|
||||
#include "../../model/world/nestedtableproxymodel.hpp"
|
||||
#include "../../model/world/columnbase.hpp"
|
||||
|
@ -30,6 +31,7 @@
|
|||
#include "../../model/world/idtree.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
#include "../../model/doc/document.hpp"
|
||||
#include "../../model/settings/usersettings.hpp"
|
||||
|
||||
#include "../widget/coloreditor.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=====================================================
|
||||
*/
|
||||
|
||||
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()
|
||||
{
|
||||
for (unsigned i = 0; i < mNestedModels.size(); ++i)
|
||||
|
@ -457,6 +590,11 @@ void CSVWorld::EditWidget::remake(int row)
|
|||
|
||||
tablesLayout->addWidget(label);
|
||||
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))
|
||||
{
|
||||
|
@ -490,6 +628,8 @@ void CSVWorld::EditWidget::remake(int row)
|
|||
editor->setEnabled(false);
|
||||
label->setEnabled(false);
|
||||
}
|
||||
|
||||
createEditorContextMenu(editor, display, row);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -541,6 +681,8 @@ void CSVWorld::EditWidget::remake(int row)
|
|||
editor->setEnabled(false);
|
||||
label->setEnabled(false);
|
||||
}
|
||||
|
||||
createEditorContextMenu(editor, display, row);
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
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)
|
||||
|
@ -717,25 +864,15 @@ void CSVWorld::SimpleDialogueSubView::refreshNpcDialogue (int type, const std::s
|
|||
}
|
||||
}
|
||||
|
||||
CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting)
|
||||
: SimpleDialogueSubView (id, document)
|
||||
void CSVWorld::DialogueSubView::addButtonBar()
|
||||
{
|
||||
// bottom box
|
||||
mBottom = new TableBottomBox (creatorFactory, document, id, this);
|
||||
if (mButtons)
|
||||
return;
|
||||
|
||||
mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
|
||||
|
||||
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
|
||||
this, SLOT (requestFocus (const std::string&)));
|
||||
|
||||
// button bar
|
||||
mButtons = new RecordButtonBar (id, getTable(), mBottom,
|
||||
mButtons = new RecordButtonBar (getUniversalId(), getTable(), mBottom,
|
||||
&getCommandDispatcher(), this);
|
||||
|
||||
// layout
|
||||
getMainLayout().addWidget (mButtons);
|
||||
getMainLayout().addWidget (mBottom);
|
||||
getMainLayout().insertWidget (1, mButtons);
|
||||
|
||||
// connections
|
||||
connect (mButtons, SIGNAL (showPreview()), this, SLOT (showPreview()));
|
||||
|
@ -746,15 +883,54 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id,
|
|||
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)
|
||||
{
|
||||
SimpleDialogueSubView::setEditLock (locked);
|
||||
|
||||
if (mButtons)
|
||||
mButtons->setEditLock (locked);
|
||||
}
|
||||
|
||||
void CSVWorld::DialogueSubView::updateUserSetting (const QString& name, const QStringList& 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef CSV_WORLD_DIALOGUESUBVIEW_H
|
||||
#define CSV_WORLD_DIALOGUESUBVIEW_H
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
|
@ -11,12 +12,14 @@
|
|||
|
||||
#include "../../model/world/columnbase.hpp"
|
||||
#include "../../model/world/commanddispatcher.hpp"
|
||||
#include "../../model/world/universalid.hpp"
|
||||
|
||||
class QDataWidgetMapper;
|
||||
class QSize;
|
||||
class QEvent;
|
||||
class QLabel;
|
||||
class QVBoxLayout;
|
||||
class QMenu;
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
@ -149,6 +152,36 @@ namespace CSVWorld
|
|||
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
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -162,6 +195,9 @@ namespace CSVWorld
|
|||
CSMDoc::Document& mDocument;
|
||||
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:
|
||||
|
||||
EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table,
|
||||
|
@ -171,6 +207,9 @@ namespace CSVWorld
|
|||
virtual ~EditWidget();
|
||||
|
||||
void remake(int row);
|
||||
|
||||
signals:
|
||||
void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint);
|
||||
};
|
||||
|
||||
class SimpleDialogueSubView : public CSVDoc::SubView
|
||||
|
@ -223,6 +262,10 @@ namespace CSVWorld
|
|||
TableBottomBox* mBottom;
|
||||
RecordButtonBar *mButtons;
|
||||
|
||||
private:
|
||||
|
||||
void addButtonBar();
|
||||
|
||||
public:
|
||||
|
||||
DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
|
||||
|
|
|
@ -55,12 +55,10 @@ void CSVWorld::DragRecordTable::dragMoveEvent(QDragMoveEvent *event)
|
|||
if (index.flags() & Qt::ItemIsEditable)
|
||||
{
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event)
|
||||
|
|
239
apps/opencs/view/world/extendedcommandconfigurator.cpp
Normal file
239
apps/opencs/view/world/extendedcommandconfigurator.cpp
Normal 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);
|
||||
}
|
||||
}
|
78
apps/opencs/view/world/extendedcommandconfigurator.hpp
Normal file
78
apps/opencs/view/world/extendedcommandconfigurator.hpp
Normal 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
|
|
@ -161,6 +161,8 @@ CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undo
|
|||
connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create()));
|
||||
|
||||
connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
|
||||
|
||||
connect (&mData, SIGNAL (idListChanged()), this, SLOT (dataIdListChanged()));
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::setEditLock (bool locked)
|
||||
|
@ -291,3 +293,12 @@ void CSVWorld::GenericCreator::scopeChanged (int index)
|
|||
update();
|
||||
updateNamespace();
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::dataIdListChanged()
|
||||
{
|
||||
// If the original ID of cloned record was removed, cancel the creator
|
||||
if (mCloneMode && !mData.hasId(mClonedId))
|
||||
{
|
||||
emit done();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,6 +113,8 @@ namespace CSVWorld
|
|||
void create();
|
||||
|
||||
void scopeChanged (int index);
|
||||
|
||||
void dataIdListChanged();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
#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 <QContextMenuEvent>
|
||||
#include <QMenu>
|
||||
#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,
|
||||
CSMWorld::UniversalId id,
|
||||
CSMWorld::NestedTableProxyModel* model,
|
||||
QWidget* parent,
|
||||
bool editable)
|
||||
: DragRecordTable(document, parent),
|
||||
mAddNewRowAction(0),
|
||||
mRemoveRowAction(0),
|
||||
mEditIdAction(0),
|
||||
mModel(model)
|
||||
{
|
||||
setSelectionBehavior (QAbstractItemView::SelectRows);
|
||||
|
@ -61,6 +63,9 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
|
|||
|
||||
connect(mRemoveRowAction, SIGNAL(triggered()),
|
||||
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)
|
||||
{
|
||||
if (!mRemoveRowAction || !mAddNewRowAction)
|
||||
if (!mEditIdAction)
|
||||
return;
|
||||
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
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)
|
||||
menu.addAction(mRemoveRowAction);
|
||||
|
||||
|
@ -102,3 +116,8 @@ void CSVWorld::NestedTable::addNewRowActionTriggered()
|
|||
selectionModel()->selectedRows().size(),
|
||||
mModel->getParentColumn()));
|
||||
}
|
||||
|
||||
void CSVWorld::NestedTable::editCell()
|
||||
{
|
||||
emit editRequest(mEditIdAction->getCurrentId(), "");
|
||||
}
|
||||
|
|
|
@ -22,12 +22,15 @@ namespace CSMDoc
|
|||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class TableEditIdAction;
|
||||
|
||||
class NestedTable : public DragRecordTable
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QAction *mAddNewRowAction;
|
||||
QAction *mRemoveRowAction;
|
||||
TableEditIdAction *mEditIdAction;
|
||||
CSMWorld::NestedTableProxyModel* mModel;
|
||||
CSMWorld::CommandDispatcher *mDispatcher;
|
||||
|
||||
|
@ -47,6 +50,11 @@ namespace CSVWorld
|
|||
void removeRowActionTriggered();
|
||||
|
||||
void addNewRowActionTriggered();
|
||||
|
||||
void editCell();
|
||||
|
||||
signals:
|
||||
void editRequest(const CSMWorld::UniversalId &id, const std::string &hint);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,6 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo
|
|||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
|
||||
layout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
if (document.getData().getReferenceables().searchId (id.getId())==-1)
|
||||
{
|
||||
std::string referenceableId =
|
||||
|
|
|
@ -17,12 +17,11 @@ void CSVWorld::RecordButtonBar::updateModificationButtons()
|
|||
|
||||
mCloneButton->setDisabled (createAndDeleteDisabled);
|
||||
mAddButton->setDisabled (createAndDeleteDisabled);
|
||||
mDeleteButton->setDisabled (createAndDeleteDisabled);
|
||||
|
||||
bool commandDisabled = !mCommandDispatcher || mLocked;
|
||||
|
||||
mRevertButton->setDisabled (commandDisabled);
|
||||
mDeleteButton->setDisabled (commandDisabled);
|
||||
mDeleteButton->setDisabled (commandDisabled || createAndDeleteDisabled);
|
||||
}
|
||||
|
||||
void CSVWorld::RecordButtonBar::updatePrevNextButtons()
|
||||
|
|
|
@ -31,8 +31,6 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D
|
|||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
|
||||
layout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
layout->addWidget (mBottom = new TableBottomBox (NullCreatorFactory(), document, id, this), 0);
|
||||
|
||||
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
|
|
@ -276,11 +276,11 @@ void CSVWorld::ScriptEdit::lineNumberAreaPaintEvent(QPaintEvent *event)
|
|||
if(textCursor().hasSelection())
|
||||
{
|
||||
QString str = textCursor().selection().toPlainText();
|
||||
int selectedLines = str.count("\n")+1;
|
||||
int offset = str.count("\n");
|
||||
if(textCursor().position() < textCursor().anchor())
|
||||
endBlock += selectedLines;
|
||||
endBlock += offset;
|
||||
else
|
||||
startBlock -= selectedLines;
|
||||
startBlock -= offset;
|
||||
}
|
||||
painter.setBackgroundMode(Qt::OpaqueMode);
|
||||
QFont font = painter.font();
|
||||
|
|
143
apps/opencs/view/world/scripterrortable.cpp
Normal file
143
apps/opencs/view/world/scripterrortable.cpp
Normal 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);
|
||||
}
|
||||
}
|
57
apps/opencs/view/world/scripterrortable.hpp
Normal file
57
apps/opencs/view/world/scripterrortable.hpp
Normal 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
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
#include <QStatusBar>
|
||||
#include <QStackedLayout>
|
||||
#include <QLabel>
|
||||
#include <QSplitter>
|
||||
#include <QTimer>
|
||||
|
||||
#include "../../model/doc/document.hpp"
|
||||
#include "../../model/world/universalid.hpp"
|
||||
|
@ -15,45 +16,96 @@
|
|||
#include "../../model/settings/usersettings.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)
|
||||
: 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;
|
||||
layout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
std::vector<std::string> selection (1, id.getId());
|
||||
mCommandDispatcher.setSelection (selection);
|
||||
|
||||
mBottom = new QWidget(this);
|
||||
QStackedLayout *bottmLayout = new QStackedLayout(mBottom);
|
||||
bottmLayout->setContentsMargins (0, 0, 0, 0);
|
||||
QStatusBar *statusBar = new QStatusBar(mBottom);
|
||||
mStatus = new QLabel(mBottom);
|
||||
statusBar->addWidget (mStatus);
|
||||
bottmLayout->addWidget (statusBar);
|
||||
mBottom->setLayout (bottmLayout);
|
||||
mMain = new QSplitter (this);
|
||||
mMain->setOrientation (Qt::Vertical);
|
||||
mLayout.addWidget (mMain, 2);
|
||||
|
||||
layout->addWidget (mBottom, 0);
|
||||
layout->insertWidget (0, mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this), 2);
|
||||
mEditor = new ScriptEdit (mDocument, ScriptHighlighter::Mode_General, this);
|
||||
mMain->addWidget (mEditor);
|
||||
mMain->setCollapsible (0, false);
|
||||
|
||||
QWidget *widget = new QWidget;
|
||||
widget->setLayout (layout);
|
||||
mErrors = new ScriptErrorTable (document, this);
|
||||
mMain->addWidget (mErrors);
|
||||
|
||||
QWidget *widget = new QWidget (this);;
|
||||
widget->setLayout (&mLayout);
|
||||
setWidget (widget);
|
||||
|
||||
mModel = &dynamic_cast<CSMWorld::IdTable&> (
|
||||
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts));
|
||||
|
||||
for (int i=0; i<mModel->columnCount(); ++i)
|
||||
if (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display)==
|
||||
CSMWorld::ColumnBase::Display_ScriptFile)
|
||||
{
|
||||
mColumn = i;
|
||||
break;
|
||||
}
|
||||
mColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_ScriptText);
|
||||
mStateColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification);
|
||||
|
||||
if (mColumn==-1)
|
||||
throw std::logic_error ("Can't find script column");
|
||||
QString source = mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString();
|
||||
|
||||
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 (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
|
@ -64,35 +116,80 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc:
|
|||
|
||||
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)
|
||||
{
|
||||
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");
|
||||
mBottom->setVisible(showLinenum == "true");
|
||||
}
|
||||
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 ()
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "(" << mEditor->textCursor().blockNumber() + 1 << ", "
|
||||
<< mEditor->textCursor().columnNumber() + 1 << ")";
|
||||
|
||||
mStatus->setText (QString::fromUtf8 (stream.str().c_str()));
|
||||
mBottom->positionChanged (mEditor->textCursor().blockNumber() + 1,
|
||||
mEditor->textCursor().columnNumber() + 1);
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptSubView::setEditLock (bool locked)
|
||||
{
|
||||
mEditor->setReadOnly (locked);
|
||||
|
||||
if (mButtons)
|
||||
mButtons->setEditLock (locked);
|
||||
|
||||
mCommandDispatcher.setEditLock (locked);
|
||||
}
|
||||
|
||||
void CSVWorld::ScriptSubView::useHint (const std::string& hint)
|
||||
|
@ -129,8 +226,12 @@ void CSVWorld::ScriptSubView::textChanged()
|
|||
|
||||
ScriptEdit::ChangeLock lock (*mEditor);
|
||||
|
||||
QString source = mEditor->toPlainText();
|
||||
|
||||
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)
|
||||
|
@ -142,12 +243,21 @@ void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QMo
|
|||
|
||||
QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn);
|
||||
|
||||
if (index.row()>=topLeft.row() && index.row()<=bottomRight.row() &&
|
||||
index.column()>=topLeft.column() && index.column()<=bottomRight.column())
|
||||
if (index.row()>=topLeft.row() && index.row()<=bottomRight.row())
|
||||
{
|
||||
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();
|
||||
mEditor->setPlainText (mModel->data (index).toString());
|
||||
mEditor->setPlainText (source);
|
||||
mEditor->setTextCursor (cursor);
|
||||
|
||||
recompile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,3 +269,42 @@ void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, i
|
|||
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());
|
||||
}
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
#ifndef CSV_WORLD_SCRIPTSUBVIEW_H
|
||||
#define CSV_WORLD_SCRIPTSUBVIEW_H
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "../../model/world/commanddispatcher.hpp"
|
||||
|
||||
#include "../doc/subview.hpp"
|
||||
|
||||
class QModelIndex;
|
||||
class QLabel;
|
||||
class QVBoxLayout;
|
||||
class QSplitter;
|
||||
class QTime;
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
|
@ -19,6 +26,9 @@ namespace CSMWorld
|
|||
namespace CSVWorld
|
||||
{
|
||||
class ScriptEdit;
|
||||
class RecordButtonBar;
|
||||
class TableBottomBox;
|
||||
class ScriptErrorTable;
|
||||
|
||||
class ScriptSubView : public CSVDoc::SubView
|
||||
{
|
||||
|
@ -28,8 +38,24 @@ namespace CSVWorld
|
|||
CSMDoc::Document& mDocument;
|
||||
CSMWorld::IdTable *mModel;
|
||||
int mColumn;
|
||||
QWidget *mBottom;
|
||||
QLabel *mStatus;
|
||||
int mStateColumn;
|
||||
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:
|
||||
|
||||
|
@ -41,6 +67,8 @@ namespace CSVWorld
|
|||
|
||||
virtual void updateUserSetting (const QString& name, const QStringList& value);
|
||||
|
||||
virtual void setStatusBar (bool show);
|
||||
|
||||
public slots:
|
||||
|
||||
void textChanged();
|
||||
|
@ -52,6 +80,14 @@ namespace CSVWorld
|
|||
private slots:
|
||||
|
||||
void updateStatusBar();
|
||||
|
||||
void switchToRow (int row);
|
||||
|
||||
void switchToId (const std::string& id);
|
||||
|
||||
void highlightError (int line, int column);
|
||||
|
||||
void updateRequest();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -30,64 +30,31 @@
|
|||
#include "../../model/settings/usersettings.hpp"
|
||||
|
||||
#include "recordstatusdelegate.hpp"
|
||||
#include "tableeditidaction.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
|
||||
{
|
||||
// configure dispatcher
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
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);
|
||||
mDispatcher->setSelection (getSelectedIds());
|
||||
|
||||
std::vector<CSMWorld::UniversalId> extendedTypes = mDispatcher->getExtendedTypes();
|
||||
|
||||
mDispatcher->setExtendedTypes (extendedTypes);
|
||||
|
||||
// create context menu
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
QMenu menu (this);
|
||||
|
||||
/// \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.
|
||||
|
||||
int currRow = rowAt( event->y() ),
|
||||
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() );
|
||||
}
|
||||
mEditIdAction->setCell(currentRow, currentColumn);
|
||||
menu.addAction(mEditIdAction);
|
||||
menu.addSeparator();
|
||||
}
|
||||
|
||||
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()));
|
||||
addAction (mMoveDownAction);
|
||||
|
||||
mEditCellAction = new QAction( tr("Edit Cell"), this );
|
||||
connect( mEditCellAction, SIGNAL(triggered()), this, SLOT(editCell()) );
|
||||
addAction( mEditCellAction );
|
||||
|
||||
mViewAction = new QAction (tr ("View"), this);
|
||||
connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord()));
|
||||
addAction (mViewAction);
|
||||
|
@ -378,23 +341,25 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
|
|||
connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord()));
|
||||
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);
|
||||
connect (mExtendedDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedDelete()));
|
||||
connect (mExtendedDeleteAction, SIGNAL (triggered()), this, SLOT (executeExtendedDelete()));
|
||||
addAction (mExtendedDeleteAction);
|
||||
|
||||
mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this);
|
||||
connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert()));
|
||||
connect (mExtendedRevertAction, SIGNAL (triggered()), this, SLOT (executeExtendedRevert()));
|
||||
addAction (mExtendedRevertAction);
|
||||
|
||||
mEditIdAction = new TableEditIdAction (*this, this);
|
||||
connect (mEditIdAction, SIGNAL (triggered()), this, SLOT (editCell()));
|
||||
addAction (mEditIdAction);
|
||||
|
||||
connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
|
||||
this, SLOT (tableSizeUpdate()));
|
||||
|
||||
connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
|
||||
this, SLOT (rowsInsertedEvent(const QModelIndex&, int, int)));
|
||||
//connect (mProxyModel, SIGNAL (rowsInserted (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
|
||||
/// 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());
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (!mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
|
||||
|
@ -468,7 +449,7 @@ void CSVWorld::Table::cloneRecord()
|
|||
{
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row());
|
||||
if (selectedRows.size()==1 && !mModel->isDeleted (toClone.getId()))
|
||||
if (selectedRows.size() == 1)
|
||||
{
|
||||
emit cloneRequest (toClone);
|
||||
}
|
||||
|
@ -543,7 +524,7 @@ void CSVWorld::Table::moveDownRecord()
|
|||
|
||||
void CSVWorld::Table::editCell()
|
||||
{
|
||||
emit editRequest( mEditCellId, std::string() );
|
||||
emit editRequest(mEditIdAction->getCurrentId(), "");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (name=="table-input/jump-to-added")
|
||||
|
@ -693,10 +702,6 @@ void CSVWorld::Table::tableSizeUpdate()
|
|||
}
|
||||
|
||||
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()
|
||||
|
@ -816,12 +821,13 @@ void CSVWorld::Table::globalFilterModifiedChanged(int state)
|
|||
recordFilterChanged(mFilter);
|
||||
}
|
||||
|
||||
void CSVWorld::Table::rowsInsertedEvent(const QModelIndex& parent, int start, int end)
|
||||
void CSVWorld::Table::rowAdded(const std::string &id)
|
||||
{
|
||||
tableSizeUpdate();
|
||||
if(mJumpToAddedRecord)
|
||||
{
|
||||
selectRow(end);
|
||||
int idColumn = mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
|
||||
selectRow(mProxyModel->getModelIndex(id, idColumn).row());
|
||||
|
||||
if(mUnselectAfterJump)
|
||||
clearSelection();
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace CSMWorld
|
|||
namespace CSVWorld
|
||||
{
|
||||
class CommandDelegate;
|
||||
class TableEditIdAction;
|
||||
|
||||
///< Table widget
|
||||
class Table : public DragRecordTable
|
||||
|
@ -57,15 +58,14 @@ namespace CSVWorld
|
|||
QAction *mMoveUpAction;
|
||||
QAction *mMoveDownAction;
|
||||
QAction *mViewAction;
|
||||
QAction *mEditCellAction;
|
||||
QAction *mPreviewAction;
|
||||
QAction *mExtendedDeleteAction;
|
||||
QAction *mExtendedRevertAction;
|
||||
TableEditIdAction *mEditIdAction;
|
||||
CSMWorld::IdTableProxyModel *mProxyModel;
|
||||
CSMWorld::IdTableBase *mModel;
|
||||
int mRecordStatusDisplay;
|
||||
CSMWorld::CommandDispatcher *mDispatcher;
|
||||
CSMWorld::UniversalId mEditCellId;
|
||||
std::map<Qt::KeyboardModifiers, DoubleClickAction> mDoubleClickActions;
|
||||
bool mJumpToAddedRecord;
|
||||
bool mUnselectAfterJump;
|
||||
|
@ -97,6 +97,8 @@ namespace CSVWorld
|
|||
|
||||
std::vector<std::string> getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const;
|
||||
|
||||
std::vector<std::string> getSelectedIds() const;
|
||||
|
||||
virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const;
|
||||
|
||||
signals:
|
||||
|
@ -116,6 +118,10 @@ namespace CSVWorld
|
|||
|
||||
void closeRequest();
|
||||
|
||||
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
|
||||
|
||||
void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);
|
||||
|
||||
private slots:
|
||||
|
||||
void editCell();
|
||||
|
@ -132,6 +138,10 @@ namespace CSVWorld
|
|||
|
||||
void previewRecord();
|
||||
|
||||
void executeExtendedDelete();
|
||||
|
||||
void executeExtendedRevert();
|
||||
|
||||
public slots:
|
||||
|
||||
void tableSizeUpdate();
|
||||
|
@ -148,7 +158,7 @@ namespace CSVWorld
|
|||
|
||||
void globalFilterModifiedChanged (int state);
|
||||
|
||||
void rowsInsertedEvent(const QModelIndex& parent, int start, int end);
|
||||
void rowAdded(const std::string &id);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,25 @@
|
|||
#include <QStatusBar>
|
||||
#include <QStackedLayout>
|
||||
#include <QLabel>
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#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()
|
||||
{
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
CSMDoc::Document& document,
|
||||
const CSMWorld::UniversalId& id,
|
||||
QWidget *parent)
|
||||
: QWidget (parent), mShowStatusBar (false), mCreating (false)
|
||||
: QWidget (parent), mShowStatusBar (false), mEditMode(EditMode_None), mHasPosition (false)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mStatusCount[i] = 0;
|
||||
|
@ -52,6 +86,7 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto
|
|||
|
||||
mLayout = new QStackedLayout;
|
||||
mLayout->setContentsMargins (0, 0, 0, 0);
|
||||
connect (mLayout, SIGNAL (currentChanged (int)), this, SLOT (currentWidgetChanged (int)));
|
||||
|
||||
mStatus = new QLabel;
|
||||
|
||||
|
@ -67,19 +102,28 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto
|
|||
|
||||
if (mCreator)
|
||||
{
|
||||
mCreator->installEventFilter(this);
|
||||
mLayout->addWidget (mCreator);
|
||||
|
||||
connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone()));
|
||||
connect (mCreator, SIGNAL (done()), this, SLOT (requestDone()));
|
||||
|
||||
connect (mCreator, 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)
|
||||
{
|
||||
if (mCreator)
|
||||
mCreator->setEditLock (locked);
|
||||
mExtendedConfigurator->setEditLock (locked);
|
||||
}
|
||||
|
||||
CSVWorld::TableBottomBox::~TableBottomBox()
|
||||
|
@ -87,11 +131,25 @@ CSVWorld::TableBottomBox::~TableBottomBox()
|
|||
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)
|
||||
{
|
||||
if (show!=mShowStatusBar)
|
||||
{
|
||||
setVisible (show || mCreating);
|
||||
setVisible (show || (mEditMode != EditMode_None));
|
||||
|
||||
mShowStatusBar = show;
|
||||
|
||||
|
@ -105,7 +163,7 @@ bool CSVWorld::TableBottomBox::canCreateAndDelete() const
|
|||
return mCreator;
|
||||
}
|
||||
|
||||
void CSVWorld::TableBottomBox::createRequestDone()
|
||||
void CSVWorld::TableBottomBox::requestDone()
|
||||
{
|
||||
if (!mShowStatusBar)
|
||||
setVisible (false);
|
||||
|
@ -113,8 +171,12 @@ void CSVWorld::TableBottomBox::createRequestDone()
|
|||
updateStatus();
|
||||
|
||||
mLayout->setCurrentWidget (mStatusBar);
|
||||
mEditMode = EditMode_None;
|
||||
}
|
||||
|
||||
mCreating = false;
|
||||
void CSVWorld::TableBottomBox::currentWidgetChanged(int /*index*/)
|
||||
{
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void CSVWorld::TableBottomBox::selectionSizeChanged (int size)
|
||||
|
@ -152,13 +214,27 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi
|
|||
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()
|
||||
{
|
||||
mCreator->reset();
|
||||
mCreator->toggleWidgets(true);
|
||||
mLayout->setCurrentWidget (mCreator);
|
||||
setVisible (true);
|
||||
mCreating = true;
|
||||
mEditMode = EditMode_Creation;
|
||||
mCreator->focus();
|
||||
}
|
||||
|
||||
|
@ -170,6 +246,16 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
|
|||
mLayout->setCurrentWidget(mCreator);
|
||||
mCreator->toggleWidgets(false);
|
||||
setVisible (true);
|
||||
mCreating = true;
|
||||
mEditMode = EditMode_Creation;
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
#include <QWidget>
|
||||
#include <apps/opencs/model/world/universalid.hpp>
|
||||
|
||||
#include "extendedcommandconfigurator.hpp"
|
||||
|
||||
class QLabel;
|
||||
class QStackedLayout;
|
||||
class QStatusBar;
|
||||
class QUndoStack;
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
|
@ -23,13 +24,21 @@ namespace CSVWorld
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum EditMode { EditMode_None, EditMode_Creation, EditMode_ExtendedConfig };
|
||||
|
||||
bool mShowStatusBar;
|
||||
QLabel *mStatus;
|
||||
QStatusBar *mStatusBar;
|
||||
int mStatusCount[4];
|
||||
|
||||
EditMode mEditMode;
|
||||
Creator *mCreator;
|
||||
bool mCreating;
|
||||
ExtendedCommandConfigurator *mExtendedConfigurator;
|
||||
|
||||
QStackedLayout *mLayout;
|
||||
bool mHasPosition;
|
||||
int mRow;
|
||||
int mColumn;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -37,8 +46,13 @@ namespace CSVWorld
|
|||
TableBottomBox (const TableBottomBox&);
|
||||
TableBottomBox& operator= (const TableBottomBox&);
|
||||
|
||||
void updateSize();
|
||||
|
||||
void updateStatus();
|
||||
|
||||
void extendedConfigRequest(ExtendedCommandConfigurator::Mode mode,
|
||||
const std::vector<std::string> &selectedIds);
|
||||
|
||||
public:
|
||||
|
||||
TableBottomBox (const CreatorFactoryBase& creatorFactory,
|
||||
|
@ -48,6 +62,8 @@ namespace CSVWorld
|
|||
|
||||
virtual ~TableBottomBox();
|
||||
|
||||
virtual bool eventFilter(QObject *object, QEvent *event);
|
||||
|
||||
void setEditLock (bool locked);
|
||||
|
||||
void setStatusBar (bool show);
|
||||
|
@ -65,9 +81,11 @@ namespace CSVWorld
|
|||
|
||||
private slots:
|
||||
|
||||
void createRequestDone();
|
||||
void requestDone();
|
||||
///< \note This slot being called does not imply success.
|
||||
|
||||
void currentWidgetChanged(int index);
|
||||
|
||||
public slots:
|
||||
|
||||
void selectionSizeChanged (int size);
|
||||
|
@ -77,9 +95,16 @@ namespace CSVWorld
|
|||
/// \param deleted Number of deleted records
|
||||
/// \param modified Number of added and modified records
|
||||
|
||||
void positionChanged (int row, int column);
|
||||
|
||||
void noMorePosition();
|
||||
|
||||
void createRequest();
|
||||
void cloneRequest(const std::string& id,
|
||||
const CSMWorld::UniversalId::Type type);
|
||||
|
||||
void extendedDeleteConfigRequest(const std::vector<std::string> &selectedIds);
|
||||
void extendedRevertConfigRequest(const std::vector<std::string> &selectedIds);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
49
apps/opencs/view/world/tableeditidaction.cpp
Normal file
49
apps/opencs/view/world/tableeditidaction.cpp
Normal 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();
|
||||
}
|
31
apps/opencs/view/world/tableeditidaction.hpp
Normal file
31
apps/opencs/view/world/tableeditidaction.hpp
Normal 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
|
|
@ -26,8 +26,6 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
|
|||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
|
||||
layout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
layout->addWidget (mBottom =
|
||||
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)),
|
||||
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&)),
|
||||
mTable, SLOT (requestFocus (const std::string&)));
|
||||
|
@ -185,4 +188,3 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -194,7 +194,10 @@ if (GIT_CHECKOUT)
|
|||
endif (GIT_CHECKOUT)
|
||||
|
||||
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()
|
||||
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
|
|
|
@ -281,8 +281,10 @@ namespace Compiler
|
|||
if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"')
|
||||
{
|
||||
name = name.substr (1, name.size()-2);
|
||||
cont = parser.parseName (name, loc, *this);
|
||||
return true;
|
||||
// allow keywords enclosed in ""
|
||||
/// \todo optionally disable
|
||||
// cont = parser.parseName (name, loc, *this);
|
||||
// return true;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
|
Loading…
Reference in a new issue