Merge branch 'filter'

actorid
Marc Zinnschlag 12 years ago
commit 94e255ea47

@ -8,6 +8,8 @@
#include <components/misc/stringops.hpp>
#include "../world/columns.hpp"
#include "../world/data.hpp"
#include "../world/idcollection.hpp"
#include "booleannode.hpp"
#include "ornode.hpp"
@ -31,6 +33,7 @@ namespace CSMFilter
Type_OpenSquare,
Type_CloseSquare,
Type_Comma,
Type_OneShot,
Type_Keyword_True, ///< \attention Keyword enums must be arranged continuously.
Type_Keyword_False,
Type_Keyword_And,
@ -44,7 +47,7 @@ namespace CSMFilter
std::string mString;
double mNumber;
Token (Type type);
Token (Type type = Type_None);
Token (const std::string& string);
@ -89,7 +92,7 @@ CSMFilter::Token CSMFilter::Parser::getStringToken()
{
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 += c;
else
@ -171,14 +174,14 @@ CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token)
{
"true", "false",
"and", "or", "not",
"text", "value",
"string", "value",
0
};
std::string string = Misc::StringUtils::lowerCase (token.mString);
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;
@ -208,9 +211,10 @@ CSMFilter::Token CSMFilter::Parser::getNextToken()
case '[': ++mIndex; return Token (Token::Type_OpenSquare);
case ']': ++mIndex; return Token (Token::Type_CloseSquare);
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();
if (c=='-' || c=='.' || std::isdigit (c))
@ -220,54 +224,58 @@ CSMFilter::Token CSMFilter::Parser::getNextToken()
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())
{
switch (token.mType)
{
case Token::Type_Keyword_True:
if (token==Token (Token::Type_OneShot))
token = getNextToken();
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (true));
if (token)
switch (token.mType)
{
case Token::Type_Keyword_True:
case Token::Type_Keyword_False:
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (true));
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (false));
case Token::Type_Keyword_False:
case Token::Type_Keyword_And:
case Token::Type_Keyword_Or:
return boost::shared_ptr<CSMFilter::Node> (new BooleanNode (false));
return parseNAry (token);
case Token::Type_Keyword_And:
case Token::Type_Keyword_Or:
case Token::Type_Keyword_Not:
{
boost::shared_ptr<CSMFilter::Node> node = parseImp();
return parseNAry (token);
if (mError)
return boost::shared_ptr<Node>();
case Token::Type_Keyword_Not:
{
boost::shared_ptr<CSMFilter::Node> node = parseImp();
return boost::shared_ptr<CSMFilter::Node> (new NotNode (node));
}
if (mError)
return boost::shared_ptr<Node>();
case Token::Type_Keyword_Text:
return boost::shared_ptr<CSMFilter::Node> (new NotNode (node));
}
return parseText();
case Token::Type_Keyword_Text:
case Token::Type_Keyword_Value:
return parseText();
return parseValue();
case Token::Type_Keyword_Value:
case Token::Type_EOS:
return parseValue();
if (!allowEmpty)
error();
case Token::Type_EOS:
return boost::shared_ptr<Node>();
if (!allowEmpty)
error();
default:
return boost::shared_ptr<Node>();
error();
}
default:
error();
}
}
return boost::shared_ptr<Node>();
@ -506,9 +514,10 @@ void CSMFilter::Parser::error()
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
mFilter.reset();
@ -516,23 +525,65 @@ bool CSMFilter::Parser::parse (const std::string& filter)
mInput = filter;
mIndex = 0;
boost::shared_ptr<Node> node = parseImp (true);
Token token;
if (mError)
return false;
if (allowPredefined)
token = getNextToken();
if (getNextToken()!=Token (Token::Type_EOS))
return false;
if (!allowPredefined || token==Token (Token::Type_OneShot))
{
boost::shared_ptr<Node> node = parseImp (true, token!=Token (Token::Type_OneShot));
if (node)
mFilter = node;
if (mError)
return false;
if (getNextToken()!=Token (Token::Type_EOS))
{
error();
return false;
}
if (node)
mFilter = node;
else
{
// Empty filter string equals to filter "true".
mFilter.reset (new BooleanNode (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
{
// Empty filter string equals to filter "true".
mFilter.reset (new BooleanNode (true));
error();
return false;
}
return true;
}
boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const

@ -5,6 +5,11 @@
#include "node.hpp"
namespace CSMWorld
{
class Data;
}
namespace CSMFilter
{
struct Token;
@ -15,6 +20,7 @@ namespace CSMFilter
std::string mInput;
int mIndex;
bool mError;
const CSMWorld::Data& mData;
Token getStringToken();
@ -25,7 +31,7 @@ namespace CSMFilter
Token checkKeywords (const Token& token);
///< 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.
boost::shared_ptr<Node> parseNAry (const Token& keyword);
@ -38,9 +44,9 @@ namespace CSMFilter
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
///
/// \return Success?

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

@ -1191,6 +1191,31 @@ namespace CSMWorld
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

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

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

@ -69,7 +69,9 @@ CSMWorld::RevertCommand::~RevertCommand()
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());
if (state==RecordBase::State_ModifiedOnly)
@ -102,7 +104,9 @@ CSMWorld::DeleteCommand::~DeleteCommand()
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());
if (state==RecordBase::State_ModifiedOnly)

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

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

@ -1,11 +1,27 @@
#include "editwidget.hpp"
CSVFilter::EditWidget::EditWidget (QWidget *parent)
: QLineEdit (parent)
#include <QAbstractItemModel>
#include "../../model/world/data.hpp"
CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent)
: QLineEdit (parent), mParser (data)
{
mPalette = palette();
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)
@ -23,4 +39,20 @@ void CSVFilter::EditWidget::textChanged (const QString& text)
/// \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());
}

@ -9,6 +9,13 @@
#include "../../model/filter/parser.hpp"
#include "../../model/filter/node.hpp"
class QModelIndex;
namespace CSMWorld
{
class Data;
}
namespace CSVFilter
{
class EditWidget : public QLineEdit
@ -20,7 +27,7 @@ namespace CSVFilter
public:
EditWidget (QWidget *parent = 0);
EditWidget (CSMWorld::Data& data, QWidget *parent = 0);
signals:
@ -29,6 +36,12 @@ namespace CSVFilter
private slots:
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);
};
}

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

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

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

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

@ -25,7 +25,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
layout->insertWidget (0, mTable =
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);

Loading…
Cancel
Save