Merge branch 'replace'

test
Marc Zinnschlag 10 years ago
commit 38ce074a62

@ -40,7 +40,7 @@ opencs_units (model/tools
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck startscriptcheck search searchoperation searchstage
) )
@ -91,7 +91,7 @@ opencs_hdrs_noqt (view/render
opencs_units (view/tools opencs_units (view/tools
reportsubview reporttable reportsubview reporttable searchsubview searchbox
) )
opencs_units_noqt (view/tools opencs_units_noqt (view/tools

@ -2374,6 +2374,17 @@ CSMWorld::UniversalId CSMDoc::Document::verify()
return id; return id;
} }
CSMWorld::UniversalId CSMDoc::Document::newSearch()
{
return mTools.newSearch();
}
void CSMDoc::Document::runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search)
{
return mTools.runSearch (searchId, search);
}
void CSMDoc::Document::abortOperation (int type) void CSMDoc::Document::abortOperation (int type)
{ {
if (type==State_Saving) if (type==State_Saving)

@ -120,6 +120,10 @@ namespace CSMDoc
CSMWorld::UniversalId verify(); CSMWorld::UniversalId verify();
CSMWorld::UniversalId newSearch();
void runSearch (const CSMWorld::UniversalId& searchId, const CSMTools::Search& search);
void abortOperation (int type); void abortOperation (int type);
const CSMWorld::Data& getData() const; const CSMWorld::Data& getData() const;

@ -13,7 +13,7 @@ namespace CSMDoc
State_Saving = 16, State_Saving = 16,
State_Verifying = 32, State_Verifying = 32,
State_Compiling = 64, // not implemented yet State_Compiling = 64, // not implemented yet
State_Searching = 128, // not implemented yet State_Searching = 128,
State_Loading = 256 // pseudo-state; can not be encountered in a loaded document State_Loading = 256 // pseudo-state; can not be encountered in a loaded document
}; };
} }

@ -208,6 +208,21 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in table:<p>" + toolTip); shiftCtrlDoubleClick->setToolTip ("Action on shift control double click in table:<p>" + toolTip);
} }
declareSection ("search", "Search & Replace");
{
Setting *before = createSetting (Type_SpinBox, "char-before",
"Characters before search string");
before->setDefaultValue (10);
before->setRange (0, 1000);
before->setToolTip ("Maximum number of character to display in search result before the searched text");
Setting *after = createSetting (Type_SpinBox, "char-after",
"Characters after search string");
after->setDefaultValue (10);
after->setRange (0, 1000);
after->setToolTip ("Maximum number of character to display in search result after the searched text");
}
{ {
/****************************************************************** /******************************************************************
* There are three types of values: * There are three types of values:

@ -2,6 +2,29 @@
#include "reportmodel.hpp" #include "reportmodel.hpp"
#include <stdexcept> #include <stdexcept>
#include <sstream>
#include "../world/columns.hpp"
CSMTools::ReportModel::Line::Line (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint)
: mId (id), mMessage (message), mHint (hint)
{}
CSMTools::ReportModel::ReportModel (bool fieldColumn)
{
if (fieldColumn)
{
mColumnField = 3;
mColumnDescription = 4;
}
else
{
mColumnDescription = 3;
mColumnField = -1;
}
}
int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const
{ {
@ -16,7 +39,7 @@ int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const
if (parent.isValid()) if (parent.isValid())
return 0; return 0;
return 3; return mColumnDescription+1;
} }
QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
@ -24,13 +47,49 @@ QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
if (role!=Qt::DisplayRole) if (role!=Qt::DisplayRole)
return QVariant(); return QVariant();
if (index.column()==0) switch (index.column())
return static_cast<int> (mRows.at (index.row()).first.getType()); {
case Column_Type:
return static_cast<int> (mRows.at (index.row()).mId.getType());
case Column_Id:
{
CSMWorld::UniversalId id = mRows.at (index.row()).mId;
if (id.getArgumentType()==CSMWorld::UniversalId::ArgumentType_Id)
return QString::fromUtf8 (id.getId().c_str());
return QString ("-");
}
case Column_Hint:
return QString::fromUtf8 (mRows.at (index.row()).mHint.c_str());
}
if (index.column()==mColumnDescription)
return QString::fromUtf8 (mRows.at (index.row()).mMessage.c_str());
if (index.column()==mColumnField)
{
std::string field;
std::istringstream stream (mRows.at (index.row()).mHint);
if (index.column()==1) char type, ignore;
return QString::fromUtf8 (mRows.at (index.row()).second.first.c_str()); int fieldIndex;
return QString::fromUtf8 (mRows.at (index.row()).second.second.c_str()); if ((stream >> type >> ignore >> fieldIndex) && (type=='r' || type=='R'))
{
field = CSMWorld::Columns::getName (
static_cast<CSMWorld::Columns::ColumnId> (fieldIndex));
}
return QString::fromUtf8 (field.c_str());
}
return QVariant();
} }
QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orientation, int role) const QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orientation, int role) const
@ -41,13 +100,19 @@ QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orienta
if (orientation==Qt::Vertical) if (orientation==Qt::Vertical)
return QVariant(); return QVariant();
if (section==0) switch (section)
return "Type"; {
case Column_Type: return "Type";
case Column_Id: return "ID";
}
if (section==1) if (section==mColumnDescription)
return "Description"; return "Description";
return "Hint"; if (section==mColumnField)
return "Field";
return "-";
} }
bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& parent) bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& parent)
@ -64,18 +129,43 @@ void CSMTools::ReportModel::add (const CSMWorld::UniversalId& id, const std::str
const std::string& hint) const std::string& hint)
{ {
beginInsertRows (QModelIndex(), mRows.size(), mRows.size()); beginInsertRows (QModelIndex(), mRows.size(), mRows.size());
mRows.push_back (std::make_pair (id, std::make_pair (message, hint))); mRows.push_back (Line (id, message, hint));
endInsertRows(); endInsertRows();
} }
void CSMTools::ReportModel::flagAsReplaced (int index)
{
Line& line = mRows.at (index);
std::string hint = line.mHint;
if (hint.empty() || hint[0]!='R')
throw std::logic_error ("trying to flag message as replaced that is not replaceable");
hint[0] = 'r';
line.mHint = hint;
emit dataChanged (this->index (index, 0), this->index (index, columnCount()));
}
const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const
{ {
return mRows.at (row).first; return mRows.at (row).mId;
} }
std::string CSMTools::ReportModel::getHint (int row) const std::string CSMTools::ReportModel::getHint (int row) const
{ {
return mRows.at (row).second.second; return mRows.at (row).mHint;
}
void CSMTools::ReportModel::clear()
{
if (!mRows.empty())
{
beginRemoveRows (QModelIndex(), 0, mRows.size()-1);
mRows.clear();
endRemoveRows();
}
} }

@ -14,10 +14,32 @@ namespace CSMTools
{ {
Q_OBJECT Q_OBJECT
std::vector<std::pair<CSMWorld::UniversalId, std::pair<std::string, std::string> > > mRows; struct Line
{
Line (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint);
CSMWorld::UniversalId mId;
std::string mMessage;
std::string mHint;
};
std::vector<Line> mRows;
// Fixed columns
enum Columns
{
Column_Type = 0, Column_Id = 1, Column_Hint = 2
};
// Configurable columns
int mColumnDescription;
int mColumnField;
public: public:
ReportModel (bool fieldColumn = false);
virtual int rowCount (const QModelIndex & parent = QModelIndex()) const; virtual int rowCount (const QModelIndex & parent = QModelIndex()) const;
virtual int columnCount (const QModelIndex & parent = QModelIndex()) const; virtual int columnCount (const QModelIndex & parent = QModelIndex()) const;
@ -27,13 +49,17 @@ namespace CSMTools
virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex());
void add (const CSMWorld::UniversalId& id, const std::string& message, void add (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint = ""); const std::string& hint = "");
void flagAsReplaced (int index);
const CSMWorld::UniversalId& getUniversalId (int row) const; const CSMWorld::UniversalId& getUniversalId (int row) const;
std::string getHint (int row) const; std::string getHint (int row) const;
void clear();
}; };
} }

@ -0,0 +1,279 @@
#include "search.hpp"
#include <stdexcept>
#include <sstream>
#include "../doc/messages.hpp"
#include "../doc/document.hpp"
#include "../world/idtablebase.hpp"
#include "../world/columnbase.hpp"
#include "../world/universalid.hpp"
#include "../world/commands.hpp"
void CSMTools::Search::searchTextCell (const CSMWorld::IdTableBase *model,
const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable,
CSMDoc::Messages& messages) const
{
// using QString here for easier handling of case folding.
QString search = QString::fromUtf8 (mText.c_str());
QString text = model->data (index).toString();
int pos = 0;
while ((pos = text.indexOf (search, pos, Qt::CaseInsensitive))!=-1)
{
std::ostringstream hint;
hint
<< (writable ? 'R' : 'r')
<<": "
<< model->getColumnId (index.column())
<< " " << pos
<< " " << search.length();
messages.add (id, formatDescription (text, pos, search.length()).toUtf8().data(), hint.str());
pos += search.length();
}
}
void CSMTools::Search::searchRegExCell (const CSMWorld::IdTableBase *model,
const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable,
CSMDoc::Messages& messages) const
{
QString text = model->data (index).toString();
int pos = 0;
while ((pos = mRegExp.indexIn (text, pos))!=-1)
{
int length = mRegExp.matchedLength();
std::ostringstream hint;
hint
<< (writable ? 'R' : 'r')
<<": "
<< model->getColumnId (index.column())
<< " " << pos
<< " " << length;
messages.add (id, formatDescription (text, pos, length).toUtf8().data(), hint.str());
pos += length;
}
}
void CSMTools::Search::searchRecordStateCell (const CSMWorld::IdTableBase *model,
const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const
{
if (writable)
throw std::logic_error ("Record state can not be modified by search and replace");
int data = model->data (index).toInt();
if (data==mValue)
{
std::vector<std::string> states =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);
std::ostringstream message;
message << states.at (data);
std::ostringstream hint;
hint << "r: " << model->getColumnId (index.column());
messages.add (id, message.str(), hint.str());
}
}
QString CSMTools::Search::formatDescription (const QString& description, int pos, int length) const
{
QString text (description);
// split
QString highlight = flatten (text.mid (pos, length));
QString before = flatten (mPaddingBefore>=pos ?
text.mid (0, pos) : text.mid (pos-mPaddingBefore, mPaddingBefore));
QString after = flatten (text.mid (pos+length, mPaddingAfter));
// compensate for Windows nonsense
text.remove ('\r');
// join
text = before + "<b>" + highlight + "</b>" + after;
// improve layout for single line display
text.replace ("\n", "&lt;CR>");
text.replace ('\t', ' ');
return text;
}
QString CSMTools::Search::flatten (const QString& text) const
{
QString flat (text);
flat.replace ("&", "&amp;");
flat.replace ("<", "&lt;");
return flat;
}
CSMTools::Search::Search() : mType (Type_None), mPaddingBefore (10), mPaddingAfter (10) {}
CSMTools::Search::Search (Type type, const std::string& value)
: mType (type), mText (value), mPaddingBefore (10), mPaddingAfter (10)
{
if (type!=Type_Text && type!=Type_Id)
throw std::logic_error ("Invalid search parameter (string)");
}
CSMTools::Search::Search (Type type, const QRegExp& value)
: mType (type), mRegExp (value), mPaddingBefore (10), mPaddingAfter (10)
{
if (type!=Type_TextRegEx && type!=Type_IdRegEx)
throw std::logic_error ("Invalid search parameter (RegExp)");
}
CSMTools::Search::Search (Type type, int value)
: mType (type), mValue (value), mPaddingBefore (10), mPaddingAfter (10)
{
if (type!=Type_RecordState)
throw std::logic_error ("invalid search parameter (int)");
}
void CSMTools::Search::configure (const CSMWorld::IdTableBase *model)
{
mColumns.clear();
int columns = model->columnCount();
for (int i=0; i<columns; ++i)
{
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> (
model->headerData (
i, Qt::Horizontal, static_cast<int> (CSMWorld::ColumnBase::Role_Display)).toInt());
bool consider = false;
switch (mType)
{
case Type_Text:
case Type_TextRegEx:
if (CSMWorld::ColumnBase::isText (display) ||
CSMWorld::ColumnBase::isScript (display))
{
consider = true;
}
break;
case Type_Id:
case Type_IdRegEx:
if (CSMWorld::ColumnBase::isId (display) ||
CSMWorld::ColumnBase::isScript (display))
{
consider = true;
}
break;
case Type_RecordState:
if (display==CSMWorld::ColumnBase::Display_RecordState)
consider = true;
break;
case Type_None:
break;
}
if (consider)
mColumns.insert (i);
}
mIdColumn = model->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
mTypeColumn = model->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);
}
void CSMTools::Search::searchRow (const CSMWorld::IdTableBase *model, int row,
CSMDoc::Messages& messages) const
{
for (std::set<int>::const_iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)
{
QModelIndex index = model->index (row, *iter);
CSMWorld::UniversalId::Type type = static_cast<CSMWorld::UniversalId::Type> (
model->data (model->index (row, mTypeColumn)).toInt());
CSMWorld::UniversalId id (
type, model->data (model->index (row, mIdColumn)).toString().toUtf8().data());
bool writable = model->flags (index) & Qt::ItemIsEditable;
switch (mType)
{
case Type_Text:
case Type_Id:
searchTextCell (model, index, id, writable, messages);
break;
case Type_TextRegEx:
case Type_IdRegEx:
searchRegExCell (model, index, id, writable, messages);
break;
case Type_RecordState:
searchRecordStateCell (model, index, id, writable, messages);
break;
case Type_None:
break;
}
}
}
void CSMTools::Search::setPadding (int before, int after)
{
mPaddingBefore = before;
mPaddingAfter = after;
}
void CSMTools::Search::replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model,
const CSMWorld::UniversalId& id, const std::string& messageHint,
const std::string& replaceText) const
{
std::istringstream stream (messageHint.c_str());
char hint, ignore;
int columnId, pos, length;
if (stream >> hint >> ignore >> columnId >> pos >> length)
{
int column =
model->findColumnIndex (static_cast<CSMWorld::Columns::ColumnId> (columnId));
QModelIndex index = model->getModelIndex (id.getId(), column);
std::string text = model->data (index).toString().toUtf8().constData();
std::string before = text.substr (0, pos);
std::string after = text.substr (pos+length);
std::string newText = before + replaceText + after;
document.getUndoStack().push (
new CSMWorld::ModifyCommand (*model, index, QString::fromUtf8 (newText.c_str())));
}
}

@ -0,0 +1,96 @@
#ifndef CSM_TOOLS_SEARCH_H
#define CSM_TOOLS_SEARCH_H
#include <string>
#include <set>
#include <QRegExp>
#include <QMetaType>
class QModelIndex;
namespace CSMDoc
{
class Messages;
class Document;
}
namespace CSMWorld
{
class IdTableBase;
class UniversalId;
}
namespace CSMTools
{
class Search
{
public:
enum Type
{
Type_Text = 0,
Type_TextRegEx = 1,
Type_Id = 2,
Type_IdRegEx = 3,
Type_RecordState = 4,
Type_None
};
private:
Type mType;
std::string mText;
QRegExp mRegExp;
int mValue;
std::set<int> mColumns;
int mIdColumn;
int mTypeColumn;
int mPaddingBefore;
int mPaddingAfter;
void searchTextCell (const CSMWorld::IdTableBase *model, const QModelIndex& index,
const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const;
void searchRegExCell (const CSMWorld::IdTableBase *model, const QModelIndex& index,
const CSMWorld::UniversalId& id, bool writable, CSMDoc::Messages& messages) const;
void searchRecordStateCell (const CSMWorld::IdTableBase *model,
const QModelIndex& index, const CSMWorld::UniversalId& id, bool writable,
CSMDoc::Messages& messages) const;
QString formatDescription (const QString& description, int pos, int length) const;
QString flatten (const QString& text) const;
public:
Search();
Search (Type type, const std::string& value);
Search (Type type, const QRegExp& value);
Search (Type type, int value);
// Configure search for the specified model.
void configure (const CSMWorld::IdTableBase *model);
// Search row in \a model and store results in \a messages.
//
// \attention *this needs to be configured for \a model.
void searchRow (const CSMWorld::IdTableBase *model, int row,
CSMDoc::Messages& messages) const;
void setPadding (int before, int after);
// Configuring *this for the model is not necessary when calling this function.
void replace (CSMDoc::Document& document, CSMWorld::IdTableBase *model,
const CSMWorld::UniversalId& id, const std::string& messageHint,
const std::string& replaceText) const;
};
}
Q_DECLARE_METATYPE (CSMTools::Search)
#endif

@ -0,0 +1,40 @@
#include "searchoperation.hpp"
#include "../doc/state.hpp"
#include "../doc/document.hpp"
#include "../world/data.hpp"
#include "../world/idtablebase.hpp"
#include "searchstage.hpp"
CSMTools::SearchOperation::SearchOperation (CSMDoc::Document& document)
: CSMDoc::Operation (CSMDoc::State_Searching, false)
{
std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listTypes (
CSMWorld::UniversalId::Class_RecordList |
CSMWorld::UniversalId::Class_ResourceList
);
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
iter!=types.end(); ++iter)
appendStage (new SearchStage (&dynamic_cast<CSMWorld::IdTableBase&> (
*document.getData().getTableModel (*iter))));
}
void CSMTools::SearchOperation::configure (const Search& search)
{
mSearch = search;
}
void CSMTools::SearchOperation::appendStage (SearchStage *stage)
{
CSMDoc::Operation::appendStage (stage);
stage->setOperation (this);
}
const CSMTools::Search& CSMTools::SearchOperation::getSearch() const
{
return mSearch;
}

@ -0,0 +1,38 @@
#ifndef CSM_TOOLS_SEARCHOPERATION_H
#define CSM_TOOLS_SEARCHOPERATION_H
#include "../doc/operation.hpp"
#include "search.hpp"
namespace CSMDoc
{
class Document;
}
namespace CSMTools
{
class SearchStage;
class SearchOperation : public CSMDoc::Operation
{
Search mSearch;
public:
SearchOperation (CSMDoc::Document& document);
/// \attention Do not call this function while a search is running.
void configure (const Search& search);
void appendStage (SearchStage *stage);
///< The ownership of \a stage is transferred to *this.
///
/// \attention Do no call this function while this Operation is running.
const Search& getSearch() const;
};
}
#endif

@ -0,0 +1,30 @@
#include "searchstage.hpp"
#include "../world/idtablebase.hpp"
#include "searchoperation.hpp"
CSMTools::SearchStage::SearchStage (const CSMWorld::IdTableBase *model)
: mModel (model), mOperation (0)
{}
int CSMTools::SearchStage::setup()
{
if (mOperation)
mSearch = mOperation->getSearch();
mSearch.configure (mModel);
return mModel->rowCount();
}
void CSMTools::SearchStage::perform (int stage, CSMDoc::Messages& messages)
{
mSearch.searchRow (mModel, stage, messages);
}
void CSMTools::SearchStage::setOperation (const SearchOperation *operation)
{
mOperation = operation;
}

@ -0,0 +1,37 @@
#ifndef CSM_TOOLS_SEARCHSTAGE_H
#define CSM_TOOLS_SEARCHSTAGE_H
#include "../doc/stage.hpp"
#include "search.hpp"
namespace CSMWorld
{
class IdTableBase;
}
namespace CSMTools
{
class SearchOperation;
class SearchStage : public CSMDoc::Stage
{
const CSMWorld::IdTableBase *mModel;
Search mSearch;
const SearchOperation *mOperation;
public:
SearchStage (const CSMWorld::IdTableBase *model);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages.
void setOperation (const SearchOperation *operation);
};
}
#endif

@ -25,12 +25,14 @@
#include "bodypartcheck.hpp" #include "bodypartcheck.hpp"
#include "referencecheck.hpp" #include "referencecheck.hpp"
#include "startscriptcheck.hpp" #include "startscriptcheck.hpp"
#include "searchoperation.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type) CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{ {
switch (type) switch (type)
{ {
case CSMDoc::State_Verifying: return &mVerifier; case CSMDoc::State_Verifying: return &mVerifier;
case CSMDoc::State_Searching: return &mSearch;
} }
return 0; return 0;
@ -101,11 +103,18 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
} }
CSMTools::Tools::Tools (CSMDoc::Document& document) CSMTools::Tools::Tools (CSMDoc::Document& document)
: mDocument (document), mData (document.getData()), mVerifierOperation (0), mNextReportNumber (0) : mDocument (document), mData (document.getData()), mVerifierOperation (0), mNextReportNumber (0),
mSearchOperation (0)
{ {
// index 0: load error log // index 0: load error log
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel)); mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
mActiveReports.insert (std::make_pair (CSMDoc::State_Loading, 0)); mActiveReports.insert (std::make_pair (CSMDoc::State_Loading, 0));
connect (&mSearch, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
connect (&mSearch, SIGNAL (done (int, bool)), this, SIGNAL (done (int, bool)));
connect (&mSearch,
SIGNAL (reportMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)),
this, SLOT (verifierMessage (const CSMWorld::UniversalId&, const std::string&, const std::string&, int)));
} }
CSMTools::Tools::~Tools() CSMTools::Tools::~Tools()
@ -116,6 +125,12 @@ CSMTools::Tools::~Tools()
delete mVerifierOperation; delete mVerifierOperation;
} }
if (mSearchOperation)
{
mSearch.abortAndWait();
delete mSearchOperation;
}
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter) for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
delete iter->second; delete iter->second;
} }
@ -130,6 +145,28 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier()
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber-1); return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber-1);
} }
CSMWorld::UniversalId CSMTools::Tools::newSearch()
{
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel (true)));
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Search, mNextReportNumber-1);
}
void CSMTools::Tools::runSearch (const CSMWorld::UniversalId& searchId, const Search& search)
{
mActiveReports[CSMDoc::State_Searching] = searchId.getIndex();
if (!mSearchOperation)
{
mSearchOperation = new SearchOperation (mDocument);
mSearch.setOperation (mSearchOperation);
}
mSearchOperation->configure (search);
mSearch.start();
}
void CSMTools::Tools::abortOperation (int type) void CSMTools::Tools::abortOperation (int type)
{ {
if (CSMDoc::OperationHolder *operation = get (type)) if (CSMDoc::OperationHolder *operation = get (type))
@ -141,6 +178,7 @@ int CSMTools::Tools::getRunningOperations() const
static const int sOperations[] = static const int sOperations[] =
{ {
CSMDoc::State_Verifying, CSMDoc::State_Verifying,
CSMDoc::State_Searching,
-1 -1
}; };
@ -157,9 +195,10 @@ int CSMTools::Tools::getRunningOperations() const
CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id) CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id)
{ {
if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults && if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults &&
id.getType()!=CSMWorld::UniversalId::Type_LoadErrorLog) id.getType()!=CSMWorld::UniversalId::Type_LoadErrorLog &&
id.getType()!=CSMWorld::UniversalId::Type_Search)
throw std::logic_error ("invalid request for report model: " + id.toString()); throw std::logic_error ("invalid request for report model: " + id.toString());
return mReports.at (id.getIndex()); return mReports.at (id.getIndex());
} }

@ -22,6 +22,8 @@ namespace CSMDoc
namespace CSMTools namespace CSMTools
{ {
class ReportModel; class ReportModel;
class Search;
class SearchOperation;
class Tools : public QObject class Tools : public QObject
{ {
@ -31,6 +33,8 @@ namespace CSMTools
CSMWorld::Data& mData; CSMWorld::Data& mData;
CSMDoc::Operation *mVerifierOperation; CSMDoc::Operation *mVerifierOperation;
CSMDoc::OperationHolder mVerifier; CSMDoc::OperationHolder mVerifier;
SearchOperation *mSearchOperation;
CSMDoc::OperationHolder mSearch;
std::map<int, ReportModel *> mReports; std::map<int, ReportModel *> mReports;
int mNextReportNumber; int mNextReportNumber;
std::map<int, int> mActiveReports; // type, report number std::map<int, int> mActiveReports; // type, report number
@ -56,6 +60,11 @@ namespace CSMTools
CSMWorld::UniversalId runVerifier(); CSMWorld::UniversalId runVerifier();
///< \return ID of the report for this verification run ///< \return ID of the report for this verification run
/// Return ID of the report for this search.
CSMWorld::UniversalId newSearch();
void runSearch (const CSMWorld::UniversalId& searchId, const Search& search);
void abortOperation (int type); void abortOperation (int type);
///< \attention The operation is not aborted immediately. ///< \attention The operation is not aborted immediately.

@ -19,7 +19,80 @@ std::string CSMWorld::ColumnBase::getTitle() const
return Columns::getName (static_cast<Columns::ColumnId> (mColumnId)); return Columns::getName (static_cast<Columns::ColumnId> (mColumnId));
} }
int CSMWorld::ColumnBase::getId() const int CSMWorld::ColumnBase::getId() const
{ {
return mColumnId; return mColumnId;
} }
bool CSMWorld::ColumnBase::isId (Display display)
{
static const Display ids[] =
{
Display_Skill,
Display_Class,
Display_Faction,
Display_Race,
Display_Sound,
Display_Region,
Display_Birthsign,
Display_Spell,
Display_Cell,
Display_Referenceable,
Display_Activator,
Display_Potion,
Display_Apparatus,
Display_Armor,
Display_Book,
Display_Clothing,
Display_Container,
Display_Creature,
Display_Door,
Display_Ingredient,
Display_CreatureLevelledList,
Display_ItemLevelledList,
Display_Light,
Display_Lockpick,
Display_Miscellaneous,
Display_Npc,
Display_Probe,
Display_Repair,
Display_Static,
Display_Weapon,
Display_Reference,
Display_Filter,
Display_Topic,
Display_Journal,
Display_TopicInfo,
Display_JournalInfo,
Display_Scene,
Display_GlobalVariable,
Display_Script,
Display_Mesh,
Display_Icon,
Display_Music,
Display_SoundRes,
Display_Texture,
Display_Video,
Display_Id,
Display_None
};
for (int i=0; ids[i]!=Display_None; ++i)
if (ids[i]==display)
return true;
return false;
}
bool CSMWorld::ColumnBase::isText (Display display)
{
return display==Display_String || display==Display_LongString;
}
bool CSMWorld::ColumnBase::isScript (Display display)
{
return display==Display_ScriptFile || display==Display_ScriptLines;
}

@ -101,9 +101,11 @@ namespace CSMWorld
Display_Texture, Display_Texture,
Display_Video, Display_Video,
Display_Colour, Display_Colour,
Display_ScriptFile,
Display_ScriptLines, // console context Display_ScriptLines, // console context
Display_SoundGeneratorType, Display_SoundGeneratorType,
Display_School Display_School,
Display_Id
}; };
int mColumnId; int mColumnId;
@ -122,6 +124,12 @@ namespace CSMWorld
virtual std::string getTitle() const; virtual std::string getTitle() const;
virtual int getId() const; virtual int getId() const;
static bool isId (Display display);
static bool isText (Display display);
static bool isScript (Display display);
}; };
template<typename ESXRecordT> template<typename ESXRecordT>

@ -43,7 +43,7 @@ namespace CSMWorld
struct StringIdColumn : public Column<ESXRecordT> struct StringIdColumn : public Column<ESXRecordT>
{ {
StringIdColumn (bool hidden = false) StringIdColumn (bool hidden = false)
: Column<ESXRecordT> (Columns::ColumnId_Id, ColumnBase::Display_String, : Column<ESXRecordT> (Columns::ColumnId_Id, ColumnBase::Display_Id,
hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue) hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue)
{} {}
@ -818,7 +818,7 @@ namespace CSMWorld
ScriptColumn (Type type) ScriptColumn (Type type)
: Column<ESXRecordT> (Columns::ColumnId_ScriptText, : Column<ESXRecordT> (Columns::ColumnId_ScriptText,
type==Type_File ? ColumnBase::Display_Script : ColumnBase::Display_ScriptLines, type==Type_File ? ColumnBase::Display_ScriptFile : ColumnBase::Display_ScriptLines,
type==Type_File ? 0 : ColumnBase::Flag_Dialogue) type==Type_File ? 0 : ColumnBase::Flag_Dialogue)
{} {}

@ -82,7 +82,7 @@ namespace CSMWorld
/// Is \a id flagged as deleted? /// Is \a id flagged as deleted?
virtual bool isDeleted (const std::string& id) const; virtual bool isDeleted (const std::string& id) const;
int getColumnId(int column) const; virtual int getColumnId(int column) const;
}; };
} }

@ -60,6 +60,8 @@ namespace CSMWorld
/// Is \a id flagged as deleted? /// Is \a id flagged as deleted?
virtual bool isDeleted (const std::string& id) const = 0; virtual bool isDeleted (const std::string& id) const = 0;
virtual int getColumnId (int column) const = 0;
unsigned int getFeatures() const; unsigned int getFeatures() const;
}; };
} }

@ -40,7 +40,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
{ {
BaseColumns baseColumns; BaseColumns baseColumns;
mColumns.push_back (RefIdColumn (Columns::ColumnId_Id, ColumnBase::Display_String, mColumns.push_back (RefIdColumn (Columns::ColumnId_Id, ColumnBase::Display_Id,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false));
baseColumns.mId = &mColumns.back(); baseColumns.mId = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Modification, ColumnBase::Display_RecordState, mColumns.push_back (RefIdColumn (Columns::ColumnId_Modification, ColumnBase::Display_RecordState,

@ -60,7 +60,7 @@ QVariant CSMWorld::ResourceTable::headerData (int section, Qt::Orientation orien
return Columns::getName (Columns::ColumnId_Id).c_str(); return Columns::getName (Columns::ColumnId_Id).c_str();
if (role==ColumnBase::Role_Display) if (role==ColumnBase::Role_Display)
return ColumnBase::Display_String; return ColumnBase::Display_Id;
break; break;
@ -144,3 +144,14 @@ bool CSMWorld::ResourceTable::isDeleted (const std::string& id) const
{ {
return false; return false;
} }
int CSMWorld::ResourceTable::getColumnId (int column) const
{
switch (column)
{
case 0: return Columns::ColumnId_Id;
case 1: return Columns::ColumnId_RecordType;
}
return -1;
}

@ -51,6 +51,8 @@ namespace CSMWorld
/// Is \a id flagged as deleted? /// Is \a id flagged as deleted?
virtual bool isDeleted (const std::string& id) const; virtual bool isDeleted (const std::string& id) const;
virtual int getColumnId (int column) const;
}; };
} }

@ -128,6 +128,7 @@ namespace
{ {
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", 0 }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", 0 },
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", 0 },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
}; };
} }
@ -347,6 +348,25 @@ std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listReferenceabl
return list; return list;
} }
std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listTypes (int classes)
{
std::vector<CSMWorld::UniversalId::Type> list;
for (int i=0; sNoArg[i].mName; ++i)
if (sNoArg[i].mClass & classes)
list.push_back (sNoArg[i].mType);
for (int i=0; sIdArg[i].mName; ++i)
if (sIdArg[i].mClass & classes)
list.push_back (sIdArg[i].mType);
for (int i=0; sIndexArg[i].mName; ++i)
if (sIndexArg[i].mClass & classes)
list.push_back (sIndexArg[i].mType);
return list;
}
CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type) CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type)
{ {
for (int i=0; sIdArg[i].mType; ++i) for (int i=0; sIdArg[i].mType; ++i)

@ -16,16 +16,16 @@ namespace CSMWorld
enum Class enum Class
{ {
Class_None = 0, Class_None = 0,
Class_Record, Class_Record = 1,
Class_RefRecord, // referenceable record Class_RefRecord = 2, // referenceable record
Class_SubRecord, Class_SubRecord = 4,
Class_RecordList, Class_RecordList = 8,
Class_Collection, // multiple types of records combined Class_Collection = 16, // multiple types of records combined
Class_Transient, // not part of the world data or the project data Class_Transient = 32, // not part of the world data or the project data
Class_NonRecord, // record like data that is not part of the world Class_NonRecord = 64, // record like data that is not part of the world
Class_Resource, ///< \attention Resource IDs are unique only within the Class_Resource = 128, ///< \attention Resource IDs are unique only within the
/// respective collection /// respective collection
Class_ResourceList Class_ResourceList = 256
}; };
enum ArgumentType enum ArgumentType
@ -130,6 +130,7 @@ namespace CSMWorld
Type_Pathgrid, Type_Pathgrid,
Type_StartScripts, Type_StartScripts,
Type_StartScript, Type_StartScript,
Type_Search,
Type_RunLog Type_RunLog
}; };
@ -180,6 +181,8 @@ namespace CSMWorld
static std::vector<Type> listReferenceableTypes(); static std::vector<Type> listReferenceableTypes();
static std::vector<Type> listTypes (int classes);
/// If \a type is a SubRecord, RefRecord or Record type return the type of the table /// If \a type is a SubRecord, RefRecord or Record type return the type of the table
/// that contains records of type \a type. /// that contains records of type \a type.
/// Otherwise return Type_None. /// Otherwise return Type_None.

@ -18,6 +18,7 @@ void CSVDoc::Operation::updateLabel (int threads)
{ {
case CSMDoc::State_Saving: name = "saving"; break; case CSMDoc::State_Saving: name = "saving"; break;
case CSMDoc::State_Verifying: name = "verifying"; break; case CSMDoc::State_Verifying: name = "verifying"; break;
case CSMDoc::State_Searching: name = "searching"; break;
} }
std::ostringstream stream; std::ostringstream stream;

@ -91,6 +91,10 @@ void CSVDoc::View::setupEditMenu()
QAction *userSettings = new QAction (tr ("&Preferences"), this); QAction *userSettings = new QAction (tr ("&Preferences"), this);
connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest())); connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest()));
edit->addAction (userSettings); edit->addAction (userSettings);
QAction *search = new QAction (tr ("Search"), this);
connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView()));
edit->addAction (search);
} }
void CSVDoc::View::setupViewMenu() void CSVDoc::View::setupViewMenu()
@ -443,7 +447,7 @@ void CSVDoc::View::updateDocumentState()
static const int operations[] = static const int operations[] =
{ {
CSMDoc::State_Saving, CSMDoc::State_Verifying, CSMDoc::State_Saving, CSMDoc::State_Verifying, CSMDoc::State_Searching,
-1 // end marker -1 // end marker
}; };
@ -728,6 +732,11 @@ void CSVDoc::View::addStartScriptsSubView()
addSubView (CSMWorld::UniversalId::Type_StartScripts); addSubView (CSMWorld::UniversalId::Type_StartScripts);
} }
void CSVDoc::View::addSearchSubView()
{
addSubView (mDocument->newSearch());
}
void CSVDoc::View::abortOperation (int type) void CSVDoc::View::abortOperation (int type)
{ {
mDocument->abortOperation (type); mDocument->abortOperation (type);

@ -217,6 +217,8 @@ namespace CSVDoc
void addStartScriptsSubView(); void addStartScriptsSubView();
void addSearchSubView();
void toggleShowStatusBar (bool show); void toggleShowStatusBar (bool show);
void loadErrorLog(); void loadErrorLog();

@ -6,7 +6,7 @@
CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: CSVDoc::SubView (id) : CSVDoc::SubView (id)
{ {
setWidget (mTable = new ReportTable (document, id, this)); setWidget (mTable = new ReportTable (document, id, false, this));
connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)), connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)),
SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&))); SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)));

@ -6,11 +6,45 @@
#include <QHeaderView> #include <QHeaderView>
#include <QAction> #include <QAction>
#include <QMenu> #include <QMenu>
#include <QStyledItemDelegate>
#include <QTextDocument>
#include <QPainter>
#include "../../model/tools/reportmodel.hpp" #include "../../model/tools/reportmodel.hpp"
#include "../../view/world/idtypedelegate.hpp" #include "../../view/world/idtypedelegate.hpp"
namespace CSVTools
{
class RichTextDelegate : public QStyledItemDelegate
{
public:
RichTextDelegate (QObject *parent = 0);
virtual void paint(QPainter *painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const;
};
}
CSVTools::RichTextDelegate::RichTextDelegate (QObject *parent) : QStyledItemDelegate (parent)
{}
void CSVTools::RichTextDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
QTextDocument document;
QVariant value = index.data (Qt::DisplayRole);
if (value.isValid() && !value.isNull())
{
document.setHtml (value.toString());
painter->translate (option.rect.topLeft());
document.drawContents (painter);
painter->translate (-option.rect.topLeft());
}
}
void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event) void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
@ -22,8 +56,25 @@ void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)
{ {
menu.addAction (mShowAction); menu.addAction (mShowAction);
menu.addAction (mRemoveAction); menu.addAction (mRemoveAction);
}
bool found = false;
for (QModelIndexList::const_iterator iter (selectedRows.begin());
iter!=selectedRows.end(); ++iter)
{
QString hint = mModel->data (mModel->index (iter->row(), 2)).toString();
if (!hint.isEmpty() && hint[0]=='R')
{
found = true;
break;
}
}
if (found)
menu.addAction (mReplaceAction);
}
menu.exec (event->globalPos()); menu.exec (event->globalPos());
} }
@ -67,10 +118,11 @@ void CSVTools::ReportTable::mouseDoubleClickEvent (QMouseEvent *event)
} }
CSVTools::ReportTable::ReportTable (CSMDoc::Document& document, CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
const CSMWorld::UniversalId& id, QWidget *parent) const CSMWorld::UniversalId& id, bool richTextDescription, QWidget *parent)
: CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id)) : CSVWorld::DragRecordTable (document, parent), mModel (document.getReport (id))
{ {
horizontalHeader()->setResizeMode (QHeaderView::Interactive); horizontalHeader()->setResizeMode (QHeaderView::Interactive);
horizontalHeader()->setStretchLastSection (true);
verticalHeader()->hide(); verticalHeader()->hide();
setSortingEnabled (true); setSortingEnabled (true);
setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionBehavior (QAbstractItemView::SelectRows);
@ -84,6 +136,9 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
setItemDelegateForColumn (0, mIdTypeDelegate); setItemDelegateForColumn (0, mIdTypeDelegate);
if (richTextDescription)
setItemDelegateForColumn (mModel->columnCount()-1, new RichTextDelegate (this));
mShowAction = new QAction (tr ("Show"), this); mShowAction = new QAction (tr ("Show"), this);
connect (mShowAction, SIGNAL (triggered()), this, SLOT (showSelection())); connect (mShowAction, SIGNAL (triggered()), this, SLOT (showSelection()));
addAction (mShowAction); addAction (mShowAction);
@ -91,6 +146,10 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
mRemoveAction = new QAction (tr ("Remove from list"), this); mRemoveAction = new QAction (tr ("Remove from list"), this);
connect (mRemoveAction, SIGNAL (triggered()), this, SLOT (removeSelection())); connect (mRemoveAction, SIGNAL (triggered()), this, SLOT (removeSelection()));
addAction (mRemoveAction); addAction (mRemoveAction);
mReplaceAction = new QAction (tr ("Replace"), this);
connect (mReplaceAction, SIGNAL (triggered()), this, SIGNAL (replaceRequest()));
addAction (mReplaceAction);
} }
std::vector<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() const std::vector<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() const
@ -113,6 +172,42 @@ void CSVTools::ReportTable::updateUserSetting (const QString& name, const QStrin
mIdTypeDelegate->updateUserSetting (name, list); mIdTypeDelegate->updateUserSetting (name, list);
} }
std::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const
{
std::vector<int> indices;
if (selection)
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
QString hint = mModel->data (mModel->index (iter->row(), 2)).toString();
if (!hint.isEmpty() && hint[0]=='R')
indices.push_back (iter->row());
}
}
else
{
for (int i=0; i<mModel->rowCount(); ++i)
{
QString hint = mModel->data (mModel->index (i, 2)).toString();
if (!hint.isEmpty() && hint[0]=='R')
indices.push_back (i);
}
}
return indices;
}
void CSVTools::ReportTable::flagAsReplaced (int index)
{
mModel->flagAsReplaced (index);
}
void CSVTools::ReportTable::showSelection() void CSVTools::ReportTable::showSelection()
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
@ -134,3 +229,8 @@ void CSVTools::ReportTable::removeSelection()
selectionModel()->clear(); selectionModel()->clear();
} }
void CSVTools::ReportTable::clear()
{
mModel->clear();
}

@ -25,6 +25,7 @@ namespace CSVTools
CSVWorld::CommandDelegate *mIdTypeDelegate; CSVWorld::CommandDelegate *mIdTypeDelegate;
QAction *mShowAction; QAction *mShowAction;
QAction *mRemoveAction; QAction *mRemoveAction;
QAction *mReplaceAction;
private: private:
@ -36,13 +37,23 @@ namespace CSVTools
public: public:
/// \param richTextDescription Use rich text in the description column.
ReportTable (CSMDoc::Document& document, const CSMWorld::UniversalId& id, ReportTable (CSMDoc::Document& document, const CSMWorld::UniversalId& id,
QWidget *parent = 0); bool richTextDescription, QWidget *parent = 0);
virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const; virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const;
void updateUserSetting (const QString& name, const QStringList& list); void updateUserSetting (const QString& name, const QStringList& list);
void clear();
// Return indices of rows that are suitable for replacement.
//
// \param selection Only list selected rows.
std::vector<int> getReplaceIndices (bool selection) const;
void flagAsReplaced (int index);
private slots: private slots:
void showSelection(); void showSelection();
@ -52,6 +63,8 @@ namespace CSVTools
signals: signals:
void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);
void replaceRequest();
}; };
} }

@ -0,0 +1,192 @@
#include "searchbox.hpp"
#include <stdexcept>
#include <QGridLayout>
#include <QComboBox>
#include <QPushButton>
#include "../../model/world/columns.hpp"
#include "../../model/tools/search.hpp"
void CSVTools::SearchBox::updateSearchButton()
{
if (!mSearchEnabled)
mSearch.setEnabled (false);
else
{
switch (mMode.currentIndex())
{
case 0:
case 1:
case 2:
case 3:
mSearch.setEnabled (!mText.text().isEmpty());
break;
case 4:
mSearch.setEnabled (true);
break;
}
}
}
CSVTools::SearchBox::SearchBox (QWidget *parent)
: QWidget (parent), mSearch ("Search"), mSearchEnabled (false), mReplace ("Replace All")
{
mLayout = new QGridLayout (this);
// search panel
std::vector<std::string> states =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);
states.resize (states.size()-1); // ignore erased state
for (std::vector<std::string>::const_iterator iter (states.begin()); iter!=states.end();
++iter)
mRecordState.addItem (QString::fromUtf8 (iter->c_str()));
mMode.addItem ("Text");
mMode.addItem ("Text (RegEx)");
mMode.addItem ("ID");
mMode.addItem ("ID (RegEx)");
mMode.addItem ("Record State");
mLayout->addWidget (&mMode, 0, 0);
mLayout->addWidget (&mSearch, 0, 3);
mInput.insertWidget (0, &mText);
mInput.insertWidget (1, &mRecordState);
mLayout->addWidget (&mInput, 0, 1);
connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int)));
connect (&mText, SIGNAL (textChanged (const QString&)),
this, SLOT (textChanged (const QString&)));
connect (&mSearch, SIGNAL (clicked (bool)), this, SLOT (startSearch (bool)));
connect (&mText, SIGNAL (returnPressed()), this, SLOT (startSearch()));
// replace panel
mReplaceInput.insertWidget (0, &mReplaceText);
mReplaceInput.insertWidget (1, &mReplacePlaceholder);
mLayout->addWidget (&mReplaceInput, 1, 1);
mLayout->addWidget (&mReplace, 1, 3);
// layout adjustments
mLayout->setColumnMinimumWidth (2, 50);
mLayout->setColumnStretch (1, 1);
mLayout->setContentsMargins (0, 0, 0, 0);
connect (&mReplace, (SIGNAL (clicked (bool))), this, SLOT (replaceAll (bool)));
// update
modeSelected (0);
updateSearchButton();
}
void CSVTools::SearchBox::setSearchMode (bool enabled)
{
mSearchEnabled = enabled;
updateSearchButton();
}
CSMTools::Search CSVTools::SearchBox::getSearch() const
{
CSMTools::Search::Type type = static_cast<CSMTools::Search::Type> (mMode.currentIndex());
switch (type)
{
case CSMTools::Search::Type_Text:
case CSMTools::Search::Type_Id:
return CSMTools::Search (type, std::string (mText.text().toUtf8().data()));
case CSMTools::Search::Type_TextRegEx:
case CSMTools::Search::Type_IdRegEx:
return CSMTools::Search (type, QRegExp (mText.text().toUtf8().data(), Qt::CaseInsensitive));
case CSMTools::Search::Type_RecordState:
return CSMTools::Search (type, mRecordState.currentIndex());
case CSMTools::Search::Type_None:
break;
}
throw std::logic_error ("invalid search mode index");
}
std::string CSVTools::SearchBox::getReplaceText() const
{
CSMTools::Search::Type type = static_cast<CSMTools::Search::Type> (mMode.currentIndex());
switch (type)
{
case CSMTools::Search::Type_Text:
case CSMTools::Search::Type_TextRegEx:
case CSMTools::Search::Type_Id:
case CSMTools::Search::Type_IdRegEx:
return mReplaceText.text().toUtf8().data();
default:
throw std::logic_error ("Invalid search mode for replace");
}
}
void CSVTools::SearchBox::setEditLock (bool locked)
{
mReplace.setEnabled (!locked);
}
void CSVTools::SearchBox::modeSelected (int index)
{
switch (index)
{
case CSMTools::Search::Type_Text:
case CSMTools::Search::Type_TextRegEx:
case CSMTools::Search::Type_Id:
case CSMTools::Search::Type_IdRegEx:
mInput.setCurrentIndex (0);
mReplaceInput.setCurrentIndex (0);
break;
case CSMTools::Search::Type_RecordState:
mInput.setCurrentIndex (1);
mReplaceInput.setCurrentIndex (1);
break;
}
updateSearchButton();
}
void CSVTools::SearchBox::textChanged (const QString& text)
{
updateSearchButton();
}
void CSVTools::SearchBox::startSearch (bool checked)
{
if (mSearch.isEnabled())
emit startSearch (getSearch());
}
void CSVTools::SearchBox::replaceAll (bool checked)
{
emit replaceAll();
}

@ -0,0 +1,70 @@
#ifndef CSV_TOOLS_SEARCHBOX_H
#define CSV_TOOLS_SEARCHBOX_H
#include <QWidget>
#include <QLineEdit>
#include <QComboBox>
#include <QStackedWidget>
#include <QPushButton>
#include <QLabel>
class QGridLayout;
namespace CSMTools
{
class Search;
}
namespace CSVTools
{
class SearchBox : public QWidget
{
Q_OBJECT
QStackedWidget mInput;
QLineEdit mText;
QComboBox mRecordState;
QPushButton mSearch;
QGridLayout *mLayout;
QComboBox mMode;
bool mSearchEnabled;
QStackedWidget mReplaceInput;
QLineEdit mReplaceText;
QLabel mReplacePlaceholder;
QPushButton mReplace;
private:
void updateSearchButton();
public:
SearchBox (QWidget *parent = 0);
void setSearchMode (bool enabled);
CSMTools::Search getSearch() const;
std::string getReplaceText() const;
void setEditLock (bool locked);
private slots:
void modeSelected (int index);
void textChanged (const QString& text);
void startSearch (bool checked = true);
void replaceAll (bool checked);
signals:
void startSearch (const CSMTools::Search& search);
void replaceAll();
};
}
#endif

@ -0,0 +1,119 @@
#include "searchsubview.hpp"
#include <QVBoxLayout>
#include "../../model/doc/document.hpp"
#include "../../model/tools/search.hpp"
#include "../../model/tools/reportmodel.hpp"
#include "../../model/world/idtablebase.hpp"
#include "reporttable.hpp"
#include "searchbox.hpp"
void CSVTools::SearchSubView::replace (bool selection)
{
if (mLocked)
return;
std::vector<int> indices = mTable->getReplaceIndices (selection);
std::string replace = mSearchBox.getReplaceText();
const CSMTools::ReportModel& model =
dynamic_cast<const CSMTools::ReportModel&> (*mTable->model());
// We are running through the indices in reverse order to avoid messing up multiple results
// in a single string.
for (std::vector<int>::const_reverse_iterator iter (indices.rbegin()); iter!=indices.rend(); ++iter)
{
CSMWorld::UniversalId id = model.getUniversalId (*iter);
CSMWorld::UniversalId::Type type = CSMWorld::UniversalId::getParentType (id.getType());
CSMWorld::IdTableBase *table = &dynamic_cast<CSMWorld::IdTableBase&> (
*mDocument.getData().getTableModel (type));
std::string hint = model.getHint (*iter);
mSearch.replace (mDocument, table, id, hint, replace);
mTable->flagAsReplaced (*iter);
}
}
CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: CSVDoc::SubView (id), mDocument (document), mPaddingBefore (10), mPaddingAfter (10),
mLocked (false)
{
QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins (QMargins (0, 0, 0, 0));
layout->addWidget (&mSearchBox);
layout->addWidget (mTable = new ReportTable (document, id, true), 2);
QWidget *widget = new QWidget;
widget->setLayout (layout);
setWidget (widget);
stateChanged (document.getState(), &document);
connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)),
SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)));
connect (mTable, SIGNAL (replaceRequest()), this, SLOT (replaceRequest()));
connect (&document, SIGNAL (stateChanged (int, CSMDoc::Document *)),
this, SLOT (stateChanged (int, CSMDoc::Document *)));
connect (&mSearchBox, SIGNAL (startSearch (const CSMTools::Search&)),
this, SLOT (startSearch (const CSMTools::Search&)));
connect (&mSearchBox, SIGNAL (replaceAll()), this, SLOT (replaceAllRequest()));
}
void CSVTools::SearchSubView::setEditLock (bool locked)
{
mLocked = locked;
mSearchBox.setEditLock (locked);
}
void CSVTools::SearchSubView::updateUserSetting (const QString &name, const QStringList &list)
{
mTable->updateUserSetting (name, list);
if (!list.empty())
{
if (name=="search/char-before")
mPaddingBefore = list.at (0).toInt();
else if (name=="search/char-after")
mPaddingAfter = list.at (0).toInt();
}
}
void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *document)
{
mSearchBox.setSearchMode (!(state & CSMDoc::State_Searching));
}
void CSVTools::SearchSubView::startSearch (const CSMTools::Search& search)
{
mSearch = search;
mSearch.setPadding (mPaddingBefore, mPaddingAfter);
mTable->clear();
mDocument.runSearch (getUniversalId(), mSearch);
}
void CSVTools::SearchSubView::replaceRequest()
{
replace (true);
}
void CSVTools::SearchSubView::replaceAllRequest()
{
replace (false);
}

@ -0,0 +1,58 @@
#ifndef CSV_TOOLS_SEARCHSUBVIEW_H
#define CSV_TOOLS_SEARCHSUBVIEW_H
#include "../../model/tools/search.hpp"
#include "../doc/subview.hpp"
#include "searchbox.hpp"
class QTableView;
class QModelIndex;
namespace CSMDoc
{
class Document;
}
namespace CSVTools
{
class ReportTable;
class SearchSubView : public CSVDoc::SubView
{
Q_OBJECT
ReportTable *mTable;
SearchBox mSearchBox;
CSMDoc::Document& mDocument;
int mPaddingBefore;
int mPaddingAfter;
CSMTools::Search mSearch;
bool mLocked;
private:
void replace (bool selection);
public:
SearchSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
virtual void setEditLock (bool locked);
virtual void updateUserSetting (const QString &, const QStringList &);
private slots:
void stateChanged (int state, CSMDoc::Document *document);
void startSearch (const CSMTools::Search& search);
void replaceRequest();
void replaceAllRequest();
};
}
#endif

@ -4,6 +4,7 @@
#include "../doc/subviewfactoryimp.hpp" #include "../doc/subviewfactoryimp.hpp"
#include "reportsubview.hpp" #include "reportsubview.hpp"
#include "searchsubview.hpp"
void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{ {
@ -11,4 +12,6 @@ void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
new CSVDoc::SubViewFactory<ReportSubView>); new CSVDoc::SubViewFactory<ReportSubView>);
manager.add (CSMWorld::UniversalId::Type_LoadErrorLog, manager.add (CSMWorld::UniversalId::Type_LoadErrorLog,
new CSVDoc::SubViewFactory<ReportSubView>); new CSVDoc::SubViewFactory<ReportSubView>);
manager.add (CSMWorld::UniversalId::Type_Search,
new CSVDoc::SubViewFactory<SearchSubView>);
} }

@ -22,7 +22,7 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc:
for (int i=0; i<mModel->columnCount(); ++i) for (int i=0; i<mModel->columnCount(); ++i)
if (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display)== if (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display)==
CSMWorld::ColumnBase::Display_Script) CSMWorld::ColumnBase::Display_ScriptFile)
{ {
mColumn = i; mColumn = i;
break; break;

Loading…
Cancel
Save