Merge branch 'master' of https://github.com/OpenMW/openmw into osg

Conflicts:
	apps/opencs/view/render/cell.cpp
c++11
scrawl 10 years ago
commit 1699759d12

@ -354,6 +354,14 @@ endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++98 -pedantic -Wno-long-long")
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION)
string(REGEX REPLACE ".*version ([0-9\\.]*).*" "\\1" CLANG_VERSION ${CLANG_VERSION})
if ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression")
endif ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6)
endif(CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)

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

@ -2,6 +2,7 @@
#include <cassert>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
@ -2377,6 +2378,17 @@ CSMWorld::UniversalId CSMDoc::Document::verify()
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)
{
if (type==State_Saving)

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

@ -13,7 +13,7 @@ namespace CSMDoc
State_Saving = 16,
State_Verifying = 32,
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
};
}

@ -191,6 +191,21 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
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:

@ -2,6 +2,29 @@
#include "reportmodel.hpp"
#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
{
@ -16,7 +39,7 @@ int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const
if (parent.isValid())
return 0;
return 3;
return mColumnDescription+1;
}
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)
return QVariant();
if (index.column()==0)
return static_cast<int> (mRows.at (index.row()).first.getType());
switch (index.column())
{
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)
return QString::fromUtf8 (mRows.at (index.row()).second.first.c_str());
char type, ignore;
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
@ -41,13 +100,19 @@ QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orienta
if (orientation==Qt::Vertical)
return QVariant();
if (section==0)
return "Type";
switch (section)
{
case Column_Type: return "Type";
case Column_Id: return "ID";
}
if (section==1)
if (section==mColumnDescription)
return "Description";
return "Hint";
if (section==mColumnField)
return "Field";
return "-";
}
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)
{
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();
}
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
{
return mRows.at (row).first;
return mRows.at (row).mId;
}
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
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:
ReportModel (bool fieldColumn = false);
virtual int rowCount (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 bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex());
void add (const CSMWorld::UniversalId& id, const std::string& message,
const std::string& hint = "");
void flagAsReplaced (int index);
const CSMWorld::UniversalId& getUniversalId (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 "referencecheck.hpp"
#include "startscriptcheck.hpp"
#include "searchoperation.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{
switch (type)
{
case CSMDoc::State_Verifying: return &mVerifier;
case CSMDoc::State_Searching: return &mSearch;
}
return 0;
@ -101,11 +103,18 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
}
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
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
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()
@ -116,6 +125,12 @@ CSMTools::Tools::~Tools()
delete mVerifierOperation;
}
if (mSearchOperation)
{
mSearch.abortAndWait();
delete mSearchOperation;
}
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
delete iter->second;
}
@ -130,6 +145,28 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier()
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)
{
if (CSMDoc::OperationHolder *operation = get (type))
@ -141,6 +178,7 @@ int CSMTools::Tools::getRunningOperations() const
static const int sOperations[] =
{
CSMDoc::State_Verifying,
CSMDoc::State_Searching,
-1
};
@ -157,9 +195,10 @@ int CSMTools::Tools::getRunningOperations() const
CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id)
{
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());
return mReports.at (id.getIndex());
}

@ -22,6 +22,8 @@ namespace CSMDoc
namespace CSMTools
{
class ReportModel;
class Search;
class SearchOperation;
class Tools : public QObject
{
@ -31,6 +33,8 @@ namespace CSMTools
CSMWorld::Data& mData;
CSMDoc::Operation *mVerifierOperation;
CSMDoc::OperationHolder mVerifier;
SearchOperation *mSearchOperation;
CSMDoc::OperationHolder mSearch;
std::map<int, ReportModel *> mReports;
int mNextReportNumber;
std::map<int, int> mActiveReports; // type, report number
@ -56,6 +60,11 @@ namespace CSMTools
CSMWorld::UniversalId runVerifier();
///< \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);
///< \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));
}
int CSMWorld::ColumnBase::getId() const
int CSMWorld::ColumnBase::getId() const
{
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_Video,
Display_Colour,
Display_ScriptFile,
Display_ScriptLines, // console context
Display_SoundGeneratorType,
Display_School
Display_School,
Display_Id
};
int mColumnId;
@ -122,6 +124,12 @@ namespace CSMWorld
virtual std::string getTitle() const;
virtual int getId() const;
static bool isId (Display display);
static bool isText (Display display);
static bool isScript (Display display);
};
template<typename ESXRecordT>

@ -43,7 +43,7 @@ namespace CSMWorld
struct StringIdColumn : public Column<ESXRecordT>
{
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)
{}
@ -818,7 +818,7 @@ namespace CSMWorld
ScriptColumn (Type type)
: 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)
{}

@ -82,7 +82,7 @@ namespace CSMWorld
/// Is \a id flagged as deleted?
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?
virtual bool isDeleted (const std::string& id) const = 0;
virtual int getColumnId (int column) const = 0;
unsigned int getFeatures() const;
};
}

@ -40,7 +40,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
{
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));
baseColumns.mId = &mColumns.back();
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();
if (role==ColumnBase::Role_Display)
return ColumnBase::Display_String;
return ColumnBase::Display_Id;
break;
@ -144,3 +144,14 @@ bool CSMWorld::ResourceTable::isDeleted (const std::string& id) const
{
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?
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_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
};
}
@ -347,6 +348,25 @@ std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listReferenceabl
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)
{
for (int i=0; sIdArg[i].mType; ++i)

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

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

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

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

@ -9,6 +9,7 @@
#include "../../model/world/idtable.hpp"
#include "../../model/world/columns.hpp"
#include "../../model/world/data.hpp"
#include "../../model/world/refcollection.hpp"
#include "elements.hpp"
#include "terrainstorage.hpp"
@ -28,26 +29,19 @@ bool CSVRender::Cell::removeObject (const std::string& id)
bool CSVRender::Cell::addObjects (int start, int end)
{
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);
int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);
int stateColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification);
bool modified = false;
const CSMWorld::RefCollection& collection = mData.getReferences();
for (int i=start; i<=end; ++i)
{
std::string cell = Misc::StringUtils::lowerCase (references.data (
references.index (i, cellColumn)).toString().toUtf8().constData());
std::string cell = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mCell);
int state = references.data (references.index (i, stateColumn)).toInt();
CSMWorld::RecordBase::State state = collection.getRecord (i).mState;
if (cell==mId && state!=CSMWorld::RecordBase::State_Deleted)
{
std::string id = Misc::StringUtils::lowerCase (references.data (
references.index (i, idColumn)).toString().toUtf8().constData());
std::string id = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mId);
mObjects.insert (std::make_pair (id, new Object (mData, mCellNode, id, false)));
modified = true;

@ -1,6 +1,8 @@
#ifndef OPENCS_VIEW_OBJECT_H
#define OPENCS_VIEW_OBJECT_H
#include <string>
#include <boost/shared_ptr.hpp>
#include <osg/ref_ptr>

@ -6,7 +6,7 @@
CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: 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&)),
SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)));

@ -6,11 +6,45 @@
#include <QHeaderView>
#include <QAction>
#include <QMenu>
#include <QStyledItemDelegate>
#include <QTextDocument>
#include <QPainter>
#include "../../model/tools/reportmodel.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)
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
@ -22,8 +56,25 @@ void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)
{
menu.addAction (mShowAction);
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());
}
@ -67,10 +118,11 @@ void CSVTools::ReportTable::mouseDoubleClickEvent (QMouseEvent *event)
}
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))
{
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
horizontalHeader()->setStretchLastSection (true);
verticalHeader()->hide();
setSortingEnabled (true);
setSelectionBehavior (QAbstractItemView::SelectRows);
@ -84,6 +136,9 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
setItemDelegateForColumn (0, mIdTypeDelegate);
if (richTextDescription)
setItemDelegateForColumn (mModel->columnCount()-1, new RichTextDelegate (this));
mShowAction = new QAction (tr ("Show"), this);
connect (mShowAction, SIGNAL (triggered()), this, SLOT (showSelection()));
addAction (mShowAction);
@ -91,6 +146,10 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
mRemoveAction = new QAction (tr ("Remove from list"), this);
connect (mRemoveAction, SIGNAL (triggered()), this, SLOT (removeSelection()));
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
@ -113,6 +172,42 @@ void CSVTools::ReportTable::updateUserSetting (const QString& name, const QStrin
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()
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
@ -134,3 +229,8 @@ void CSVTools::ReportTable::removeSelection()
selectionModel()->clear();
}
void CSVTools::ReportTable::clear()
{
mModel->clear();
}

@ -25,6 +25,7 @@ namespace CSVTools
CSVWorld::CommandDelegate *mIdTypeDelegate;
QAction *mShowAction;
QAction *mRemoveAction;
QAction *mReplaceAction;
private:
@ -36,13 +37,23 @@ namespace CSVTools
public:
/// \param richTextDescription Use rich text in the description column.
ReportTable (CSMDoc::Document& document, const CSMWorld::UniversalId& id,
QWidget *parent = 0);
bool richTextDescription, QWidget *parent = 0);
virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const;
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:
void showSelection();
@ -52,6 +63,8 @@ namespace CSVTools
signals:
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 "reportsubview.hpp"
#include "searchsubview.hpp"
void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{
@ -11,4 +12,6 @@ void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
new CSVDoc::SubViewFactory<ReportSubView>);
manager.add (CSMWorld::UniversalId::Type_LoadErrorLog,
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)
if (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display)==
CSMWorld::ColumnBase::Display_Script)
CSMWorld::ColumnBase::Display_ScriptFile)
{
mColumn = i;
break;

@ -548,7 +548,6 @@ namespace MWGui
WindowModal::open();
mMagicList->setModel(new SpellModel(MWBase::Environment::get().getWorld()->getPlayerPtr()));
mMagicList->update();
}
void MagicSelectionDialog::onModelIndexSelected(SpellModel::ModelIndex index)

@ -10,6 +10,16 @@
namespace MWGui
{
const char* SpellView::sSpellModelIndex = "SpellModelIndex";
SpellView::LineInfo::LineInfo(MyGUI::Widget* leftWidget, MyGUI::Widget* rightWidget, SpellModel::ModelIndex spellIndex)
: mLeftWidget(leftWidget)
, mRightWidget(rightWidget)
, mSpellIndex(spellIndex)
{
}
SpellView::SpellView()
: mShowCostColumn(true)
, mHighlightSelected(true)
@ -113,10 +123,10 @@ namespace MWGui
group.push_back(costChance);
Gui::SharedStateButton::createButtonGroup(group);
mLines.push_back(std::make_pair(t, costChance));
mLines.push_back(LineInfo(t, costChance, i));
}
else
mLines.push_back(std::make_pair(t, (MyGUI::Widget*)NULL));
mLines.push_back(LineInfo(t, (MyGUI::Widget*)NULL, i));
t->setStateSelected(spell.mSelected);
}
@ -124,30 +134,85 @@ namespace MWGui
layoutWidgets();
}
void SpellView::incrementalUpdate()
{
if (!mModel.get())
{
return;
}
mModel->update();
bool fullUpdateRequired = false;
SpellModel::ModelIndex maxSpellIndexFound = -1;
for (std::vector< LineInfo >::iterator it = mLines.begin(); it != mLines.end(); ++it)
{
// only update the lines that are "updateable"
SpellModel::ModelIndex spellIndex(it->mSpellIndex);
if (spellIndex != NoSpellIndex)
{
Gui::SharedStateButton* nameButton = reinterpret_cast<Gui::SharedStateButton*>(it->mLeftWidget);
// match model against line
// if don't match, then major change has happened, so do a full update
if (mModel->getItemCount() <= static_cast<unsigned>(spellIndex))
{
fullUpdateRequired = true;
break;
}
// more checking for major change.
const Spell& spell = mModel->getItem(spellIndex);
if (nameButton->getCaption() != spell.mName)
{
fullUpdateRequired = true;
break;
}
else
{
maxSpellIndexFound = spellIndex;
Gui::SharedStateButton* costButton = reinterpret_cast<Gui::SharedStateButton*>(it->mRightWidget);
if ((costButton != NULL) && (costButton->getCaption() != spell.mCostColumn))
{
costButton->setCaption(spell.mCostColumn);
}
}
}
}
// special case, look for spells added to model that are beyond last updatable item
SpellModel::ModelIndex topSpellIndex = mModel->getItemCount() - 1;
if (fullUpdateRequired ||
((0 <= topSpellIndex) && (maxSpellIndexFound < topSpellIndex)))
{
update();
}
}
void SpellView::layoutWidgets()
{
int height = 0;
for (std::vector< std::pair<MyGUI::Widget*, MyGUI::Widget*> >::iterator it = mLines.begin();
for (std::vector< LineInfo >::iterator it = mLines.begin();
it != mLines.end(); ++it)
{
height += (it->first)->getHeight();
height += (it->mLeftWidget)->getHeight();
}
bool scrollVisible = height > mScrollView->getHeight();
int width = mScrollView->getWidth() - (scrollVisible ? 18 : 0);
height = 0;
for (std::vector< std::pair<MyGUI::Widget*, MyGUI::Widget*> >::iterator it = mLines.begin();
for (std::vector< LineInfo >::iterator it = mLines.begin();
it != mLines.end(); ++it)
{
int lineHeight = (it->first)->getHeight();
(it->first)->setCoord(4, height, width-8, lineHeight);
if (it->second)
int lineHeight = (it->mLeftWidget)->getHeight();
(it->mLeftWidget)->setCoord(4, height, width - 8, lineHeight);
if (it->mRightWidget)
{
(it->second)->setCoord(4, height, width-8, lineHeight);
MyGUI::TextBox* second = (it->second)->castType<MyGUI::TextBox>(false);
(it->mRightWidget)->setCoord(4, height, width - 8, lineHeight);
MyGUI::TextBox* second = (it->mRightWidget)->castType<MyGUI::TextBox>(false);
if (second)
(it->first)->setSize(width-8-second->getTextSize().width, lineHeight);
(it->mLeftWidget)->setSize(width - 8 - second->getTextSize().width, lineHeight);
}
height += lineHeight;
@ -167,7 +232,7 @@ namespace MWGui
MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18),
MyGUI::Align::Left | MyGUI::Align::Top);
separator->setNeedMouseFocus(false);
mLines.push_back(std::make_pair(separator, (MyGUI::Widget*)NULL));
mLines.push_back(LineInfo(separator, (MyGUI::Widget*)NULL, NoSpellIndex));
}
MyGUI::TextBox* groupWidget = mScrollView->createWidget<MyGUI::TextBox>("SandBrightText",
@ -186,10 +251,10 @@ namespace MWGui
groupWidget2->setTextAlign(MyGUI::Align::Right);
groupWidget2->setNeedMouseFocus(false);
mLines.push_back(std::make_pair(groupWidget, groupWidget2));
mLines.push_back(LineInfo(groupWidget, groupWidget2, NoSpellIndex));
}
else
mLines.push_back(std::make_pair(groupWidget, (MyGUI::Widget*)NULL));
mLines.push_back(LineInfo(groupWidget, (MyGUI::Widget*)NULL, NoSpellIndex));
}
@ -222,16 +287,20 @@ namespace MWGui
widget->setUserString("Spell", spell.mId);
}
widget->setUserString("SpellModelIndex", MyGUI::utility::toString(index));
widget->setUserString(sSpellModelIndex, MyGUI::utility::toString(index));
widget->eventMouseWheel += MyGUI::newDelegate(this, &SpellView::onMouseWheel);
widget->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellView::onSpellSelected);
}
SpellModel::ModelIndex SpellView::getSpellModelIndex(MyGUI::Widget* widget)
{
return MyGUI::utility::parseInt(widget->getUserString(sSpellModelIndex));
}
void SpellView::onSpellSelected(MyGUI::Widget* _sender)
{
SpellModel::ModelIndex i = MyGUI::utility::parseInt(_sender->getUserString("SpellModelIndex"));
eventSpellClicked(i);
eventSpellClicked(getSpellModelIndex(_sender));
}
void SpellView::onMouseWheel(MyGUI::Widget* _sender, int _rel)

@ -1,6 +1,8 @@
#ifndef OPENMW_GUI_SPELLVIEW_H
#define OPENMW_GUI_SPELLVIEW_H
#include <boost/tuple/tuple.hpp>
#include <MyGUI_Widget.h>
#include "spellmodel.hpp"
@ -37,6 +39,9 @@ namespace MWGui
void update();
/// simplified update called each frame
void incrementalUpdate();
typedef MyGUI::delegates::CMultiDelegate1<SpellModel::ModelIndex> EventHandle_ModelIndex;
/// Fired when a spell was clicked
EventHandle_ModelIndex eventSpellClicked;
@ -51,7 +56,25 @@ namespace MWGui
std::auto_ptr<SpellModel> mModel;
std::vector< std::pair<MyGUI::Widget*, MyGUI::Widget*> > mLines;
/// tracks a row in the spell view
struct LineInfo
{
/// the widget on the left side of the row
MyGUI::Widget* mLeftWidget;
/// the widget on the left side of the row (if there is one)
MyGUI::Widget* mRightWidget;
/// index to item in mModel that row is showing information for
SpellModel::ModelIndex mSpellIndex;
LineInfo(MyGUI::Widget* leftWidget, MyGUI::Widget* rightWidget, SpellModel::ModelIndex spellIndex);
};
/// magic number indicating LineInfo does not correspond to an item in mModel
enum { NoSpellIndex = -1 };
std::vector< LineInfo > mLines;
bool mShowCostColumn;
bool mHighlightSelected;
@ -62,6 +85,10 @@ namespace MWGui
void onSpellSelected(MyGUI::Widget* _sender);
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
SpellModel::ModelIndex getSpellModelIndex(MyGUI::Widget* _sender);
static const char* sSpellModelIndex;
};
}

@ -28,6 +28,7 @@ namespace MWGui
: WindowPinnableBase("openmw_spell_window.layout")
, NoDrop(drag, mMainWidget)
, mSpellView(NULL)
, mUpdateTimer(0.0f)
{
mSpellIcons = new SpellIcons();
@ -60,12 +61,25 @@ namespace MWGui
updateSpells();
}
void SpellWindow::onFrame(float dt)
{
if (mMainWidget->getVisible())
{
NoDrop::onFrame(dt);
mUpdateTimer += dt;
if (0.5f < mUpdateTimer)
{
mUpdateTimer = 0;
mSpellView->incrementalUpdate();
}
}
}
void SpellWindow::updateSpells()
{
mSpellIcons->updateWidgets(mEffectBox, false);
mSpellView->setModel(new SpellModel(MWBase::Environment::get().getWorld()->getPlayerPtr()));
mSpellView->update();
}
void SpellWindow::onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped)
@ -170,7 +184,6 @@ namespace MWGui
void SpellWindow::cycle(bool next)
{
mSpellView->setModel(new SpellModel(MWBase::Environment::get().getWorld()->getPlayerPtr()));
mSpellView->getModel()->update();
SpellModel::ModelIndex selected = 0;
for (SpellModel::ModelIndex i = 0; i<int(mSpellView->getModel()->getItemCount()); ++i)

@ -19,7 +19,7 @@ namespace MWGui
void updateSpells();
void onFrame(float dt) { NoDrop::onFrame(dt); }
void onFrame(float dt);
/// Cycle to next/previous spell
void cycle(bool next);
@ -41,6 +41,9 @@ namespace MWGui
SpellView* mSpellView;
SpellIcons* mSpellIcons;
private:
float mUpdateTimer;
};
}

@ -686,7 +686,8 @@ namespace MWMechanics
// If there is no path this actor doesn't go anywhere. See:
// https://forum.openmw.org/viewtopic.php?t=1556
// http://www.fliggerty.com/phpBB3/viewtopic.php?f=30&t=5833
if(!pathgrid || pathgrid->mPoints.empty())
// Note: In order to wander, need at least two points.
if(!pathgrid || (pathgrid->mPoints.size() < 2))
mDistance = 0;
// A distance value passed into the constructor indicates how far the
@ -730,12 +731,37 @@ namespace MWMechanics
}
mCurrentNode = mAllowedNodes[index];
mAllowedNodes.erase(mAllowedNodes.begin() + index);
}
// In vanilla Morrowind, sometimes distance is too small to include at least two points,
// in which case, we will take the two closest points regardless of the wander distance
// This is a backup option, as std::sort is potentially O(n^2) in time.
if (mAllowedNodes.empty())
{
// Start with list of PathGrid nodes, sorted by distance from actor
std::vector<PathDistance> nodeDistances;
for (unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)
{
float distance = npcPos.squaredDistance(PathFinder::MakeOgreVector3(pathgrid->mPoints[counter]));
nodeDistances.push_back(std::make_pair(distance, &pathgrid->mPoints.at(counter)));
}
std::sort(nodeDistances.begin(), nodeDistances.end(), sortByDistance);
mStoredAvailableNodes = true; // set only if successful in finding allowed nodes
// make closest node the current node
mCurrentNode = *nodeDistances[0].second;
// give Actor a 2nd node to walk to
mAllowedNodes.push_back(*nodeDistances[1].second);
}
mStoredAvailableNodes = true; // set only if successful in finding allowed nodes
}
}
bool AiWander::sortByDistance(const PathDistance& left, const PathDistance& right)
{
return left.first < right.first;
}
void AiWander::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiWander> wander(new ESM::AiSequence::AiWander());

@ -122,6 +122,13 @@ namespace MWMechanics
/// lookup table for converting idleSelect value to groupName
static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1];
/// record distances of pathgrid point nodes to actor
/// first value is distance between actor and node, second value is PathGrid node
typedef std::pair<float, const ESM::Pathgrid::Point*> PathDistance;
/// used to sort array of PathDistance objects into ascending order
static bool sortByDistance(const PathDistance& left, const PathDistance& right);
};

@ -10,6 +10,9 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwgui/inventorywindow.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/spellcasting.hpp"
@ -175,7 +178,7 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite
flagAsModified();
fireEquipmentChangedEvent();
fireEquipmentChangedEvent(actor);
updateMagicEffects(actor);
}
@ -188,7 +191,7 @@ void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor)
mUpdatesEnabled = true;
fireEquipmentChangedEvent();
fireEquipmentChangedEvent(actor);
updateMagicEffects(actor);
}
@ -318,7 +321,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
if (changed)
{
mSlots.swap (slots_);
fireEquipmentChangedEvent();
fireEquipmentChangedEvent(actor);
updateMagicEffects(actor);
flagAsModified();
}
@ -549,7 +552,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c
}
}
fireEquipmentChangedEvent();
fireEquipmentChangedEvent(actor);
updateMagicEffects(actor);
return retval;
@ -576,12 +579,18 @@ void MWWorld::InventoryStore::setListener(InventoryStoreListener *listener, cons
updateMagicEffects(actor);
}
void MWWorld::InventoryStore::fireEquipmentChangedEvent()
void MWWorld::InventoryStore::fireEquipmentChangedEvent(const Ptr& actor)
{
if (!mUpdatesEnabled)
return;
if (mListener)
mListener->equipmentChanged();
// if player, update inventory window
if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr())
{
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
}
}
void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisitor &visitor)

@ -111,7 +111,7 @@ namespace MWWorld
void updateMagicEffects(const Ptr& actor);
void updateRechargingItems();
void fireEquipmentChangedEvent();
void fireEquipmentChangedEvent(const Ptr& actor);
virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory);

@ -6,9 +6,10 @@
int ContentSelectorModel::EsmFile::sPropertyCount = 7;
QString ContentSelectorModel::EsmFile::sToolTip = QString("<b>Author:</b> %1<br/> \
<b>Version:</b> %2<br/> \
<b>Path:</b><br/>%3<br/> \
<br/><b>Description:</b><br/>%4<br/> \
<br/><b>Dependencies: </b>%5<br/>");
<b>Modified:</b> %3<br/> \
<b>Path:</b><br/>%4<br/> \
<br/><b>Description:</b><br/>%5<br/> \
<br/><b>Dependencies: </b>%6<br/>");
ContentSelectorModel::EsmFile::EsmFile(QString fileName, ModelItem *parent)

@ -59,6 +59,7 @@ namespace ContentSelectorModel
inline QString description() const { return mDescription; }
inline QString toolTip() const { return sToolTip.arg(mAuthor)
.arg(mFormat)
.arg(mModified.toString(Qt::ISODate))
.arg(mPath)
.arg(mDescription)
.arg(mGameFiles.join(", "));

@ -26,4 +26,4 @@ namespace Misc {
}
}
}
}

Loading…
Cancel
Save