2015-03-25 10:56:14 +00:00
|
|
|
|
|
|
|
#include "search.hpp"
|
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include "../../model/doc/messages.hpp"
|
|
|
|
|
|
|
|
#include "../../model/world/idtablebase.hpp"
|
|
|
|
#include "../../model/world/columnbase.hpp"
|
|
|
|
#include "../../model/world/universalid.hpp"
|
|
|
|
|
|
|
|
void CSMTools::Search::searchTextCell (const CSMWorld::IdTableBase *model,
|
2015-03-28 11:05:49 +00:00
|
|
|
const QModelIndex& index, const CSMWorld::UniversalId& id,
|
2015-03-25 10:56:14 +00:00
|
|
|
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;
|
2015-03-29 13:28:31 +00:00
|
|
|
hint
|
|
|
|
<< "r: "
|
|
|
|
<< model->getColumnId (index.column())
|
|
|
|
<< " " << pos
|
|
|
|
<< " " << search.length();
|
2015-03-25 10:56:14 +00:00
|
|
|
|
2015-03-29 16:16:43 +00:00
|
|
|
messages.add (id, formatDescription (text, pos, search.length()).toUtf8().data(), hint.str());
|
2015-03-25 10:56:14 +00:00
|
|
|
|
|
|
|
pos += search.length();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSMTools::Search::searchRegExCell (const CSMWorld::IdTableBase *model,
|
2015-03-28 11:05:49 +00:00
|
|
|
const QModelIndex& index, const CSMWorld::UniversalId& id,
|
2015-03-25 10:56:14 +00:00
|
|
|
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;
|
2015-03-29 16:16:43 +00:00
|
|
|
hint << "r: " << model->getColumnId (index.column()) << " " << pos << " " << length;
|
2015-03-25 10:56:14 +00:00
|
|
|
|
2015-03-29 16:16:43 +00:00
|
|
|
messages.add (id, formatDescription (text, pos, length).toUtf8().data(), hint.str());
|
2015-03-25 10:56:14 +00:00
|
|
|
|
|
|
|
pos += length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSMTools::Search::searchRecordStateCell (const CSMWorld::IdTableBase *model,
|
|
|
|
const QModelIndex& index, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) const
|
|
|
|
{
|
|
|
|
int data = model->data (index).toInt();
|
|
|
|
|
|
|
|
if (data==mValue)
|
|
|
|
{
|
|
|
|
std::vector<std::string> states =
|
|
|
|
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);
|
|
|
|
|
|
|
|
std::ostringstream message;
|
2015-03-29 16:16:43 +00:00
|
|
|
message << states.at (data);
|
2015-03-25 10:56:14 +00:00
|
|
|
|
|
|
|
std::ostringstream hint;
|
2015-03-29 16:16:43 +00:00
|
|
|
hint << "r: " << model->getColumnId (index.column());
|
2015-03-25 10:56:14 +00:00
|
|
|
|
|
|
|
messages.add (id, message.str(), hint.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-29 16:16:43 +00:00
|
|
|
QString CSMTools::Search::formatDescription (const QString& description, int pos, int length) const
|
2015-03-27 18:10:45 +00:00
|
|
|
{
|
2015-03-30 10:52:08 +00:00
|
|
|
QString text (description);
|
2015-03-29 16:16:43 +00:00
|
|
|
|
|
|
|
// compensate for Windows nonsense
|
|
|
|
text.remove ('\r');
|
2015-03-27 18:10:45 +00:00
|
|
|
|
2015-03-30 10:52:08 +00:00
|
|
|
// split
|
|
|
|
int padding = 10; ///< \todo make this configurable
|
|
|
|
|
|
|
|
QString highlight = flatten (text.mid (pos, length));
|
|
|
|
QString before = flatten (padding<=pos ? text.mid (0, pos) : text.mid (pos-padding, padding));
|
|
|
|
QString after = flatten (text.mid (pos+length, padding));
|
|
|
|
|
|
|
|
// join
|
|
|
|
text = before + "<b>" + highlight + "</b>" + after;
|
|
|
|
|
2015-03-29 16:16:43 +00:00
|
|
|
// improve layout for single line display
|
2015-03-30 10:52:08 +00:00
|
|
|
text.replace ("\n", "<CR>");
|
2015-03-29 16:16:43 +00:00
|
|
|
text.replace ('\t', ' ');
|
|
|
|
|
2015-03-30 10:52:08 +00:00
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString CSMTools::Search::flatten (const QString& text) const
|
|
|
|
{
|
|
|
|
QString flat (text);
|
|
|
|
|
|
|
|
flat.replace ("&", "&");
|
|
|
|
flat.replace ("<", "<");
|
|
|
|
|
|
|
|
return flat;
|
2015-03-27 18:10:45 +00:00
|
|
|
}
|
|
|
|
|
2015-03-25 10:56:14 +00:00
|
|
|
CSMTools::Search::Search() : mType (Type_None) {}
|
|
|
|
|
|
|
|
CSMTools::Search::Search (Type type, const std::string& value)
|
|
|
|
: mType (type), mText (value)
|
|
|
|
{
|
2015-03-28 10:54:32 +00:00
|
|
|
if (type!=Type_Text && type!=Type_Id)
|
2015-03-25 10:56:14 +00:00
|
|
|
throw std::logic_error ("Invalid search parameter (string)");
|
|
|
|
}
|
|
|
|
|
|
|
|
CSMTools::Search::Search (Type type, const QRegExp& value)
|
|
|
|
: mType (type), mRegExp (value)
|
|
|
|
{
|
2015-03-28 10:54:32 +00:00
|
|
|
if (type!=Type_TextRegEx && type!=Type_IdRegEx)
|
2015-03-25 10:56:14 +00:00
|
|
|
throw std::logic_error ("Invalid search parameter (RegExp)");
|
|
|
|
}
|
|
|
|
|
|
|
|
CSMTools::Search::Search (Type type, int value)
|
|
|
|
: mType (type), mValue (value)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2015-03-28 10:54:32 +00:00
|
|
|
case Type_Id:
|
|
|
|
case Type_IdRegEx:
|
2015-03-25 10:56:14 +00:00
|
|
|
|
2015-03-28 11:05:49 +00:00
|
|
|
if (CSMWorld::ColumnBase::isId (display) ||
|
|
|
|
CSMWorld::ColumnBase::isScript (display))
|
2015-03-25 10:56:14 +00:00
|
|
|
{
|
|
|
|
consider = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type_RecordState:
|
|
|
|
|
|
|
|
if (display==CSMWorld::ColumnBase::Display_RecordState)
|
|
|
|
consider = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type_None:
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (consider)
|
2015-03-28 11:05:49 +00:00
|
|
|
mColumns.insert (i);
|
2015-03-25 10:56:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2015-03-28 11:05:49 +00:00
|
|
|
for (std::set<int>::const_iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)
|
2015-03-25 10:56:14 +00:00
|
|
|
{
|
2015-03-28 11:05:49 +00:00
|
|
|
QModelIndex index = model->index (row, *iter);
|
2015-03-25 10:56:14 +00:00
|
|
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
switch (mType)
|
|
|
|
{
|
|
|
|
case Type_Text:
|
2015-03-28 10:54:32 +00:00
|
|
|
case Type_Id:
|
2015-03-25 10:56:14 +00:00
|
|
|
|
2015-03-28 11:05:49 +00:00
|
|
|
searchTextCell (model, index, id, messages);
|
2015-03-25 10:56:14 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Type_TextRegEx:
|
2015-03-28 10:54:32 +00:00
|
|
|
case Type_IdRegEx:
|
2015-03-25 10:56:14 +00:00
|
|
|
|
2015-03-28 11:05:49 +00:00
|
|
|
searchRegExCell (model, index, id, messages);
|
2015-03-25 10:56:14 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Type_RecordState:
|
|
|
|
|
|
|
|
searchRecordStateCell (model, index, id, messages);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Type_None:
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|