forked from mirror/openmw-tes3mp
Merge branch 'replace'
commit
38ce074a62
@ -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", "<CR>");
|
||||
text.replace ('\t', ' ');
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
QString CSMTools::Search::flatten (const QString& text) const
|
||||
{
|
||||
QString flat (text);
|
||||
|
||||
flat.replace ("&", "&");
|
||||
flat.replace ("<", "<");
|
||||
|
||||
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
|
@ -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
|
Loading…
Reference in New Issue