1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-29 19:36:43 +00:00

Merge branch 'filter'

This commit is contained in:
Marc Zinnschlag 2013-08-28 12:42:03 +02:00
commit 94e255ea47
16 changed files with 233 additions and 74 deletions

View file

@ -8,6 +8,8 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "../world/columns.hpp" #include "../world/columns.hpp"
#include "../world/data.hpp"
#include "../world/idcollection.hpp"
#include "booleannode.hpp" #include "booleannode.hpp"
#include "ornode.hpp" #include "ornode.hpp"
@ -31,6 +33,7 @@ namespace CSMFilter
Type_OpenSquare, Type_OpenSquare,
Type_CloseSquare, Type_CloseSquare,
Type_Comma, Type_Comma,
Type_OneShot,
Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously. Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously.
Type_Keyword_False, Type_Keyword_False,
Type_Keyword_And, Type_Keyword_And,
@ -44,7 +47,7 @@ namespace CSMFilter
std::string mString; std::string mString;
double mNumber; double mNumber;
Token (Type type); Token (Type type = Type_None);
Token (const std::string& string); Token (const std::string& string);
@ -89,7 +92,7 @@ CSMFilter::Token CSMFilter::Parser::getStringToken()
{ {
char c = mInput[mIndex]; char c = mInput[mIndex];
if (std::isalpha (c) || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' || if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' ||
(!string.empty() && string[0]=='"')) (!string.empty() && string[0]=='"'))
string += c; string += c;
else else
@ -171,14 +174,14 @@ CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token)
{ {
"true", "false", "true", "false",
"and", "or", "not", "and", "or", "not",
"text", "value", "string", "value",
0 0
}; };
std::string string = Misc::StringUtils::lowerCase (token.mString); std::string string = Misc::StringUtils::lowerCase (token.mString);
for (int i=0; sKeywords[i]; ++i) for (int i=0; sKeywords[i]; ++i)
if (sKeywords[i]==string) if (sKeywords[i]==string || (string.size()==1 && sKeywords[i][0]==string[0]))
return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True)); return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True));
return token; return token;
@ -208,9 +211,10 @@ CSMFilter::Token CSMFilter::Parser::getNextToken()
case '[': ++mIndex; return Token (Token::Type_OpenSquare); case '[': ++mIndex; return Token (Token::Type_OpenSquare);
case ']': ++mIndex; return Token (Token::Type_CloseSquare); case ']': ++mIndex; return Token (Token::Type_CloseSquare);
case ',': ++mIndex; return Token (Token::Type_Comma); case ',': ++mIndex; return Token (Token::Type_Comma);
case '!': ++mIndex; return Token (Token::Type_OneShot);
} }
if (c=='"' || c=='_' || std::isalpha (c)) if (c=='"' || c=='_' || std::isalpha (c) || c==':')
return getStringToken(); return getStringToken();
if (c=='-' || c=='.' || std::isdigit (c)) if (c=='-' || c=='.' || std::isdigit (c))
@ -220,10 +224,14 @@ CSMFilter::Token CSMFilter::Parser::getNextToken()
return Token (Token::Type_None); return Token (Token::Type_None);
} }
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseImp (bool allowEmpty) boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseImp (bool allowEmpty, bool ignoreOneShot)
{ {
if (Token token = getNextToken()) if (Token token = getNextToken())
{ {
if (token==Token (Token::Type_OneShot))
token = getNextToken();
if (token)
switch (token.mType) switch (token.mType)
{ {
case Token::Type_Keyword_True: case Token::Type_Keyword_True:
@ -506,9 +514,10 @@ void CSMFilter::Parser::error()
mError = true; mError = true;
} }
CSMFilter::Parser::Parser() : mIndex (0), mError (false) {} CSMFilter::Parser::Parser (const CSMWorld::Data& data)
: mIndex (0), mError (false), mData (data) {}
bool CSMFilter::Parser::parse (const std::string& filter) bool CSMFilter::Parser::parse (const std::string& filter, bool allowPredefined)
{ {
// reset // reset
mFilter.reset(); mFilter.reset();
@ -516,13 +525,23 @@ bool CSMFilter::Parser::parse (const std::string& filter)
mInput = filter; mInput = filter;
mIndex = 0; mIndex = 0;
boost::shared_ptr<Node> node = parseImp (true); Token token;
if (allowPredefined)
token = getNextToken();
if (!allowPredefined || token==Token (Token::Type_OneShot))
{
boost::shared_ptr<Node> node = parseImp (true, token!=Token (Token::Type_OneShot));
if (mError) if (mError)
return false; return false;
if (getNextToken()!=Token (Token::Type_EOS)) if (getNextToken()!=Token (Token::Type_EOS))
{
error();
return false; return false;
}
if (node) if (node)
mFilter = node; mFilter = node;
@ -534,6 +553,38 @@ bool CSMFilter::Parser::parse (const std::string& filter)
return true; return true;
} }
else if (token.mType==Token::Type_String && allowPredefined)
{
if (getNextToken()!=Token (Token::Type_EOS))
{
error();
return false;
}
int index = mData.getFilters().searchId (token.mString);
if (index==-1)
{
error();
return false;
}
const CSMWorld::Record<CSMFilter::Filter>& record = mData.getFilters().getRecord (index);
if (record.isDeleted())
{
error();
return false;
}
return parse (record.get().mFilter, false);
}
else
{
error();
return false;
}
}
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const
{ {

View file

@ -5,6 +5,11 @@
#include "node.hpp" #include "node.hpp"
namespace CSMWorld
{
class Data;
}
namespace CSMFilter namespace CSMFilter
{ {
struct Token; struct Token;
@ -15,6 +20,7 @@ namespace CSMFilter
std::string mInput; std::string mInput;
int mIndex; int mIndex;
bool mError; bool mError;
const CSMWorld::Data& mData;
Token getStringToken(); Token getStringToken();
@ -25,7 +31,7 @@ namespace CSMFilter
Token checkKeywords (const Token& token); Token checkKeywords (const Token& token);
///< Turn string token into keyword token, if possible. ///< Turn string token into keyword token, if possible.
boost::shared_ptr<Node> parseImp (bool allowEmpty = false); boost::shared_ptr<Node> parseImp (bool allowEmpty = false, bool ignoreOneShot = false);
///< Will return a null-pointer, if there is nothing more to parse. ///< Will return a null-pointer, if there is nothing more to parse.
boost::shared_ptr<Node> parseNAry (const Token& keyword); boost::shared_ptr<Node> parseNAry (const Token& keyword);
@ -38,9 +44,9 @@ namespace CSMFilter
public: public:
Parser(); Parser (const CSMWorld::Data& data);
bool parse (const std::string& filter); bool parse (const std::string& filter, bool allowPredefined = true);
///< Discards any previous calls to parse ///< Discards any previous calls to parse
/// ///
/// \return Success? /// \return Success?

View file

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

View file

@ -1191,6 +1191,31 @@ namespace CSMWorld
return true; return true;
} }
}; };
template<typename ESXRecordT>
struct FilterColumn : public Column<ESXRecordT>
{
FilterColumn() : Column<ESXRecordT> (Columns::ColumnId_Filter, ColumnBase::Display_String) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return QString::fromUtf8 (record.get().mFilter.c_str());
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mFilter = data.toString().toUtf8().constData();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
};
} }
#endif #endif

View file

@ -144,6 +144,7 @@ namespace CSMWorld
{ ColumnId_MaxThrust, "Max Thrust" }, { ColumnId_MaxThrust, "Max Thrust" },
{ ColumnId_Magical, "Magical" }, { ColumnId_Magical, "Magical" },
{ ColumnId_Silver, "Silver" }, { ColumnId_Silver, "Silver" },
{ ColumnId_Filter, "Filter" },
{ ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },

View file

@ -138,6 +138,7 @@ namespace CSMWorld
ColumnId_MaxThrust = 106, ColumnId_MaxThrust = 106,
ColumnId_Magical = 107, ColumnId_Magical = 107,
ColumnId_Silver = 108, ColumnId_Silver = 108,
ColumnId_Filter = 109,
// Allocated to a separate value range, so we don't get a collision should we ever need // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // to extend the number of use values.

View file

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

View file

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

View file

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

View file

@ -1,11 +1,27 @@
#include "editwidget.hpp" #include "editwidget.hpp"
CSVFilter::EditWidget::EditWidget (QWidget *parent) #include <QAbstractItemModel>
: QLineEdit (parent)
#include "../../model/world/data.hpp"
CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent)
: QLineEdit (parent), mParser (data)
{ {
mPalette = palette(); mPalette = palette();
connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters);
connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)),
this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)),
Qt::QueuedConnection);
connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)),
Qt::QueuedConnection);
connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (filterRowsInserted (const QModelIndex&, int, int)),
Qt::QueuedConnection);
} }
void CSVFilter::EditWidget::textChanged (const QString& text) void CSVFilter::EditWidget::textChanged (const QString& text)
@ -24,3 +40,19 @@ void CSVFilter::EditWidget::textChanged (const QString& text)
/// \todo improve error reporting; mark only the faulty part /// \todo improve error reporting; mark only the faulty part
} }
} }
void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
textChanged (text());
}
void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end)
{
textChanged (text());
}
void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end)
{
textChanged (text());
}

View file

@ -9,6 +9,13 @@
#include "../../model/filter/parser.hpp" #include "../../model/filter/parser.hpp"
#include "../../model/filter/node.hpp" #include "../../model/filter/node.hpp"
class QModelIndex;
namespace CSMWorld
{
class Data;
}
namespace CSVFilter namespace CSVFilter
{ {
class EditWidget : public QLineEdit class EditWidget : public QLineEdit
@ -20,7 +27,7 @@ namespace CSVFilter
public: public:
EditWidget (QWidget *parent = 0); EditWidget (CSMWorld::Data& data, QWidget *parent = 0);
signals: signals:
@ -29,6 +36,12 @@ namespace CSVFilter
private slots: private slots:
void textChanged (const QString& text); void textChanged (const QString& text);
void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void filterRowsRemoved (const QModelIndex& parent, int start, int end);
void filterRowsInserted (const QModelIndex& parent, int start, int end);
}; };
} }

View file

@ -5,14 +5,14 @@
#include "recordfilterbox.hpp" #include "recordfilterbox.hpp"
CSVFilter::FilterBox::FilterBox (QWidget *parent) CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent)
: QWidget (parent) : QWidget (parent)
{ {
QHBoxLayout *layout = new QHBoxLayout (this); QHBoxLayout *layout = new QHBoxLayout (this);
layout->setContentsMargins (0, 0, 0, 0); layout->setContentsMargins (0, 0, 0, 0);
RecordFilterBox *recordFilterBox = new RecordFilterBox (this); RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this);
layout->addWidget (recordFilterBox); layout->addWidget (recordFilterBox);

View file

@ -5,6 +5,11 @@
#include "../../model/filter/node.hpp" #include "../../model/filter/node.hpp"
namespace CSMWorld
{
class Data;
}
namespace CSVFilter namespace CSVFilter
{ {
class FilterBox : public QWidget class FilterBox : public QWidget
@ -13,7 +18,7 @@ namespace CSVFilter
public: public:
FilterBox (QWidget *parent = 0); FilterBox (CSMWorld::Data& data, QWidget *parent = 0);
signals: signals:

View file

@ -6,7 +6,7 @@
#include "editwidget.hpp" #include "editwidget.hpp"
CSVFilter::RecordFilterBox::RecordFilterBox (QWidget *parent) CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent)
: QWidget (parent) : QWidget (parent)
{ {
QHBoxLayout *layout = new QHBoxLayout (this); QHBoxLayout *layout = new QHBoxLayout (this);
@ -15,7 +15,7 @@ CSVFilter::RecordFilterBox::RecordFilterBox (QWidget *parent)
layout->addWidget (new QLabel ("Record Filter", this)); layout->addWidget (new QLabel ("Record Filter", this));
EditWidget *editWidget = new EditWidget (this); EditWidget *editWidget = new EditWidget (data, this);
layout->addWidget (editWidget); layout->addWidget (editWidget);

View file

@ -9,6 +9,11 @@
#include "../../model/filter/node.hpp" #include "../../model/filter/node.hpp"
namespace CSMWorld
{
class Data;
}
namespace CSVFilter namespace CSVFilter
{ {
class RecordFilterBox : public QWidget class RecordFilterBox : public QWidget
@ -17,7 +22,7 @@ namespace CSVFilter
public: public:
RecordFilterBox (QWidget *parent = 0); RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0);
signals: signals:

View file

@ -25,7 +25,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
layout->insertWidget (0, mTable = layout->insertWidget (0, mTable =
new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2); new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2);
CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (this); CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this);
layout->insertWidget (0, filterBox); layout->insertWidget (0, filterBox);