Arrange for plaintorich to keep track of the link between match area and search terms. Use this in preview to allow walking the matches for a given search term group
This commit is contained in:
parent
35ed2b3ca9
commit
1ed0f4ddf8
8 changed files with 291 additions and 156 deletions
|
@ -204,24 +204,32 @@ void RecollProtocol::queryDetails()
|
||||||
class PlainToRichKio : public PlainToRich {
|
class PlainToRichKio : public PlainToRich {
|
||||||
public:
|
public:
|
||||||
PlainToRichKio(const string& nm)
|
PlainToRichKio(const string& nm)
|
||||||
: PlainToRich() , m_name(nm)
|
: m_name(nm)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
virtual ~PlainToRichKio() {}
|
|
||||||
virtual string header() {
|
virtual string header() {
|
||||||
if (m_inputhtml) {
|
if (m_inputhtml) {
|
||||||
return snull;
|
return cstr_null;
|
||||||
} else {
|
} else {
|
||||||
return
|
return string("<html><head>"
|
||||||
string("<html><head>"
|
"<META http-equiv=\"Content-Type\""
|
||||||
"<META http-equiv=\"Content-Type\""
|
"content=\"text/html;charset=UTF-8\"><title>").
|
||||||
"content=\"text/html;charset=UTF-8\"><title>") +
|
append(m_name).
|
||||||
m_name +
|
append("</title></head><body><pre>");
|
||||||
string("</title></head><body><pre>");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual string startMatch() {return string("<font color=\"blue\">");}
|
|
||||||
virtual string endMatch() {return string("</font>");}
|
virtual string startMatch(unsigned int)
|
||||||
|
{
|
||||||
|
return string("<font color=\"blue\">");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual string endMatch()
|
||||||
|
{
|
||||||
|
return string("</font>");
|
||||||
|
}
|
||||||
|
|
||||||
const string &m_name;
|
const string &m_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -68,54 +68,130 @@ using std::pair;
|
||||||
// Subclass plainToRich to add <termtag>s and anchors to the preview text
|
// Subclass plainToRich to add <termtag>s and anchors to the preview text
|
||||||
class PlainToRichQtPreview : public PlainToRich {
|
class PlainToRichQtPreview : public PlainToRich {
|
||||||
public:
|
public:
|
||||||
int lastanchor;
|
|
||||||
PlainToRichQtPreview()
|
PlainToRichQtPreview()
|
||||||
|
: m_curanchor(1), m_lastanchor(0)
|
||||||
{
|
{
|
||||||
lastanchor = 0;
|
|
||||||
}
|
}
|
||||||
virtual ~PlainToRichQtPreview() {}
|
|
||||||
virtual string header() {
|
bool haveAnchors()
|
||||||
|
{
|
||||||
|
return m_lastanchor != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual string header()
|
||||||
|
{
|
||||||
if (m_inputhtml) {
|
if (m_inputhtml) {
|
||||||
return snull;
|
return cstr_null;
|
||||||
} else {
|
} else {
|
||||||
if (prefs.previewPlainPre) {
|
if (prefs.previewPlainPre) {
|
||||||
m_eolbr = false;
|
m_eolbr = false;
|
||||||
return string("<qt><head><title></title></head><body>"
|
return string("<qt><head><title></title></head><body>"
|
||||||
"<pre>");
|
"<pre>");
|
||||||
// Note we could also use the following for line-folding instead of <br>s
|
// Note: we could also use the following for
|
||||||
// This would be possible without recomputing the whole text, much better perfs
|
// line-folding instead of <br>s This would be
|
||||||
// for toggling wrap/no-wrap
|
// possible without recomputing the whole text, much
|
||||||
// "<pre style=\"white-space: pre-wrap\">");
|
// better perfs for toggling wrap/no-wrap:
|
||||||
|
// <pre style=\"white-space: pre-wrap\">
|
||||||
} else {
|
} else {
|
||||||
m_eolbr = true;
|
m_eolbr = true;
|
||||||
return string("<qt><head><title></title></head><body>");
|
return string("<qt><head><title></title></head><body>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual string startMatch()
|
|
||||||
|
virtual string startMatch(unsigned int grpidx)
|
||||||
{
|
{
|
||||||
return string("<span style='color: ")
|
LOGDEB2(("startMatch, grpidx %u\n", grpidx));
|
||||||
+ string((const char *)(prefs.qtermcolor.toUtf8()))
|
grpidx = m_hdata->grpsugidx[grpidx];
|
||||||
+ string(";font-weight: bold;")
|
LOGDEB2(("startMatch, ugrpidx %u\n", grpidx));
|
||||||
+ string("'>");
|
m_groupanchors[grpidx].push_back(++m_lastanchor);
|
||||||
|
m_groupcuranchors[grpidx] = 0;
|
||||||
|
return string("<span style='color: ").
|
||||||
|
append((const char *)(prefs.qtermcolor.toUtf8())).
|
||||||
|
append(";font-weight: bold;").
|
||||||
|
append("'>").
|
||||||
|
append("<a name=\"").
|
||||||
|
append(termAnchorName(m_lastanchor)).
|
||||||
|
append("\">");
|
||||||
}
|
}
|
||||||
virtual string endMatch() {return string("</span>");}
|
|
||||||
virtual string termAnchorName(int i) {
|
virtual string endMatch()
|
||||||
|
{
|
||||||
|
return string("</a></span>");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual string termAnchorName(int i) const
|
||||||
|
{
|
||||||
static const char *termAnchorNameBase = "TRM";
|
static const char *termAnchorNameBase = "TRM";
|
||||||
char acname[sizeof(termAnchorNameBase) + 20];
|
char acname[sizeof(termAnchorNameBase) + 20];
|
||||||
sprintf(acname, "%s%d", termAnchorNameBase, i);
|
sprintf(acname, "%s%d", termAnchorNameBase, i);
|
||||||
if (i > lastanchor)
|
|
||||||
lastanchor = i;
|
|
||||||
return string(acname);
|
return string(acname);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual string startAnchor(int i) {
|
virtual string startChunk()
|
||||||
return string("<a name=\"") + termAnchorName(i) + "\">";
|
{
|
||||||
|
return "<pre>";
|
||||||
}
|
}
|
||||||
virtual string endAnchor() {
|
|
||||||
return string("</a>");
|
int nextAnchorNum(int grpidx)
|
||||||
|
{
|
||||||
|
LOGDEB2(("nextAnchorNum: group %d\n", grpidx));
|
||||||
|
map<unsigned int, unsigned int>::iterator curit =
|
||||||
|
m_groupcuranchors.find(grpidx);
|
||||||
|
map<unsigned int, vector<int> >::iterator vecit =
|
||||||
|
m_groupanchors.find(grpidx);
|
||||||
|
if (grpidx == -1 || curit == m_groupcuranchors.end() ||
|
||||||
|
vecit == m_groupanchors.end()) {
|
||||||
|
if (m_curanchor >= m_lastanchor)
|
||||||
|
m_curanchor = 1;
|
||||||
|
else
|
||||||
|
m_curanchor++;
|
||||||
|
} else {
|
||||||
|
if (curit->second >= vecit->second.size() -1)
|
||||||
|
m_groupcuranchors[grpidx] = 0;
|
||||||
|
else
|
||||||
|
m_groupcuranchors[grpidx]++;
|
||||||
|
m_curanchor = vecit->second[m_groupcuranchors[grpidx]];
|
||||||
|
LOGDEB2(("nextAnchorNum: curanchor now %d\n", m_curanchor));
|
||||||
|
}
|
||||||
|
return m_curanchor;
|
||||||
}
|
}
|
||||||
virtual string startChunk() { return "<pre>";}
|
|
||||||
|
int prevAnchorNum(int grpidx)
|
||||||
|
{
|
||||||
|
map<unsigned int, unsigned int>::iterator curit =
|
||||||
|
m_groupcuranchors.find(grpidx);
|
||||||
|
map<unsigned int, vector<int> >::iterator vecit =
|
||||||
|
m_groupanchors.find(grpidx);
|
||||||
|
if (grpidx == -1 || curit == m_groupcuranchors.end() ||
|
||||||
|
vecit == m_groupanchors.end()) {
|
||||||
|
if (m_curanchor <= 1)
|
||||||
|
m_curanchor = m_lastanchor;
|
||||||
|
else
|
||||||
|
m_curanchor--;
|
||||||
|
} else {
|
||||||
|
if (curit->second <= 0)
|
||||||
|
m_groupcuranchors[grpidx] = vecit->second.size() -1;
|
||||||
|
else
|
||||||
|
m_groupcuranchors[grpidx]--;
|
||||||
|
m_curanchor = vecit->second[m_groupcuranchors[grpidx]];
|
||||||
|
}
|
||||||
|
return m_curanchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString curAnchorName() const
|
||||||
|
{
|
||||||
|
return QString::fromUtf8(termAnchorName(m_curanchor).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_curanchor;
|
||||||
|
int m_lastanchor;
|
||||||
|
// Lists of anchor numbers (match locations) for the term (groups)
|
||||||
|
// in the query (the map key is and index into HighlightData.groups).
|
||||||
|
map<unsigned int, vector<int> > m_groupanchors;
|
||||||
|
map<unsigned int, unsigned int> m_groupcuranchors;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Preview::init()
|
void Preview::init()
|
||||||
|
@ -141,8 +217,24 @@ void Preview::init()
|
||||||
QHBoxLayout *layout3 = new QHBoxLayout(0);
|
QHBoxLayout *layout3 = new QHBoxLayout(0);
|
||||||
searchLabel = new QLabel(this);
|
searchLabel = new QLabel(this);
|
||||||
layout3->addWidget(searchLabel);
|
layout3->addWidget(searchLabel);
|
||||||
searchTextLine = new QLineEdit(this);
|
|
||||||
layout3->addWidget(searchTextLine);
|
searchTextCMB = new QComboBox(this);
|
||||||
|
searchTextCMB->setEditable(true);
|
||||||
|
searchTextCMB->setInsertPolicy(QComboBox::NoInsert);
|
||||||
|
searchTextCMB->setDuplicatesEnabled(false);
|
||||||
|
for (unsigned int i = 0; i < m_hData.ugroups.size(); i++) {
|
||||||
|
QString s;
|
||||||
|
for (unsigned int j = 0; j < m_hData.ugroups[i].size(); j++) {
|
||||||
|
s.append(QString::fromUtf8(m_hData.ugroups[i][j].c_str()));
|
||||||
|
if (j != m_hData.ugroups[i].size()-1)
|
||||||
|
s.append(" ");
|
||||||
|
}
|
||||||
|
searchTextCMB->addItem(s);
|
||||||
|
}
|
||||||
|
searchTextCMB->setEditText("");
|
||||||
|
|
||||||
|
layout3->addWidget(searchTextCMB);
|
||||||
|
|
||||||
nextButton = new QPushButton(this);
|
nextButton = new QPushButton(this);
|
||||||
nextButton->setEnabled(TRUE);
|
nextButton->setEnabled(TRUE);
|
||||||
layout3->addWidget(nextButton);
|
layout3->addWidget(nextButton);
|
||||||
|
@ -160,7 +252,7 @@ void Preview::init()
|
||||||
resize(QSize(640, 480).expandedTo(minimumSizeHint()));
|
resize(QSize(640, 480).expandedTo(minimumSizeHint()));
|
||||||
|
|
||||||
// buddies
|
// buddies
|
||||||
searchLabel->setBuddy(searchTextLine);
|
searchLabel->setBuddy(searchTextCMB);
|
||||||
|
|
||||||
searchLabel->setText(tr("&Search for:"));
|
searchLabel->setText(tr("&Search for:"));
|
||||||
nextButton->setText(tr("&Next"));
|
nextButton->setText(tr("&Next"));
|
||||||
|
@ -176,26 +268,25 @@ void Preview::init()
|
||||||
"RCL.SEARCH.PREVIEW");
|
"RCL.SEARCH.PREVIEW");
|
||||||
|
|
||||||
// signals and slots connections
|
// signals and slots connections
|
||||||
connect(searchTextLine, SIGNAL(textChanged(const QString&)),
|
connect(searchTextCMB, SIGNAL(activated(int)),
|
||||||
this, SLOT(searchTextLine_textChanged(const QString&)));
|
this, SLOT(searchTextFromIndex(int)));
|
||||||
|
connect(searchTextCMB, SIGNAL(editTextChanged(const QString&)),
|
||||||
|
this, SLOT(searchTextChanged(const QString&)));
|
||||||
connect(nextButton, SIGNAL(clicked()), this, SLOT(nextPressed()));
|
connect(nextButton, SIGNAL(clicked()), this, SLOT(nextPressed()));
|
||||||
connect(prevButton, SIGNAL(clicked()), this, SLOT(prevPressed()));
|
connect(prevButton, SIGNAL(clicked()), this, SLOT(prevPressed()));
|
||||||
connect(clearPB, SIGNAL(clicked()), searchTextLine, SLOT(clear()));
|
connect(clearPB, SIGNAL(clicked()), searchTextCMB, SLOT(clearEditText()));
|
||||||
connect(pvTab, SIGNAL(currentChanged(QWidget *)),
|
connect(pvTab, SIGNAL(currentChanged(QWidget *)),
|
||||||
this, SLOT(currentChanged(QWidget *)));
|
this, SLOT(currentChanged(QWidget *)));
|
||||||
connect(bt, SIGNAL(clicked()), this, SLOT(closeCurrentTab()));
|
connect(bt, SIGNAL(clicked()), this, SLOT(closeCurrentTab()));
|
||||||
|
|
||||||
m_dynSearchActive = false;
|
m_dynSearchActive = false;
|
||||||
m_canBeep = true;
|
m_canBeep = true;
|
||||||
m_currentW = 0;
|
|
||||||
if (prefs.pvwidth > 100) {
|
if (prefs.pvwidth > 100) {
|
||||||
resize(prefs.pvwidth, prefs.pvheight);
|
resize(prefs.pvwidth, prefs.pvheight);
|
||||||
}
|
}
|
||||||
m_loading = false;
|
m_loading = false;
|
||||||
currentChanged(pvTab->currentWidget());
|
currentChanged(pvTab->currentWidget());
|
||||||
m_justCreated = true;
|
m_justCreated = true;
|
||||||
m_haveAnchors = false;
|
|
||||||
m_curAnchor = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preview::closeEvent(QCloseEvent *e)
|
void Preview::closeEvent(QCloseEvent *e)
|
||||||
|
@ -273,11 +364,11 @@ bool Preview::eventFilter(QObject *target, QEvent *event)
|
||||||
} else if (m_dynSearchActive) {
|
} else if (m_dynSearchActive) {
|
||||||
if (keyEvent->key() == Qt::Key_F3) {
|
if (keyEvent->key() == Qt::Key_F3) {
|
||||||
LOGDEB2(("Preview::eventFilter: got F3\n"));
|
LOGDEB2(("Preview::eventFilter: got F3\n"));
|
||||||
doSearch(searchTextLine->text(), true, false);
|
doSearch(searchTextCMB->currentText(), true, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (target != searchTextLine)
|
if (target != searchTextCMB)
|
||||||
return QApplication::sendEvent(searchTextLine, event);
|
return QApplication::sendEvent(searchTextCMB, event);
|
||||||
} else {
|
} else {
|
||||||
if (edit &&
|
if (edit &&
|
||||||
(target == edit || target == edit->viewport())) {
|
(target == edit || target == edit->viewport())) {
|
||||||
|
@ -285,7 +376,7 @@ bool Preview::eventFilter(QObject *target, QEvent *event)
|
||||||
(keyEvent->key() == Qt::Key_F &&
|
(keyEvent->key() == Qt::Key_F &&
|
||||||
(keyEvent->modifiers() & Qt::ControlModifier))) {
|
(keyEvent->modifiers() & Qt::ControlModifier))) {
|
||||||
LOGDEB2(("Preview::eventFilter: got / or C-F\n"));
|
LOGDEB2(("Preview::eventFilter: got / or C-F\n"));
|
||||||
searchTextLine->setFocus();
|
searchTextCMB->setFocus();
|
||||||
m_dynSearchActive = true;
|
m_dynSearchActive = true;
|
||||||
return true;
|
return true;
|
||||||
} else if (keyEvent->key() == Qt::Key_Space) {
|
} else if (keyEvent->key() == Qt::Key_Space) {
|
||||||
|
@ -307,23 +398,27 @@ bool Preview::eventFilter(QObject *target, QEvent *event)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preview::searchTextLine_textChanged(const QString & text)
|
void Preview::searchTextChanged(const QString & text)
|
||||||
{
|
{
|
||||||
LOGDEB2(("search line text changed. text: '%s'\n", text.ascii()));
|
LOGDEB1(("Search line text changed. text: '%s'\n",
|
||||||
|
(const char *)text.toAscii()));
|
||||||
|
m_searchTextFromIndex = -1;
|
||||||
if (text.isEmpty()) {
|
if (text.isEmpty()) {
|
||||||
m_dynSearchActive = false;
|
m_dynSearchActive = false;
|
||||||
// nextButton->setEnabled(false);
|
|
||||||
// prevButton->setEnabled(false);
|
|
||||||
clearPB->setEnabled(false);
|
clearPB->setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
m_dynSearchActive = true;
|
m_dynSearchActive = true;
|
||||||
// nextButton->setEnabled(true);
|
|
||||||
// prevButton->setEnabled(true);
|
|
||||||
clearPB->setEnabled(true);
|
clearPB->setEnabled(true);
|
||||||
doSearch(text, false, false);
|
doSearch(text, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Preview::searchTextFromIndex(int idx)
|
||||||
|
{
|
||||||
|
LOGDEB1(("search line from index %d\n", idx));
|
||||||
|
m_searchTextFromIndex = idx;
|
||||||
|
}
|
||||||
|
|
||||||
PreviewTextEdit *Preview::currentEditor()
|
PreviewTextEdit *Preview::currentEditor()
|
||||||
{
|
{
|
||||||
LOGDEB2(("Preview::currentEditor()\n"));
|
LOGDEB2(("Preview::currentEditor()\n"));
|
||||||
|
@ -351,9 +446,9 @@ void Preview::emitSaveDocToFile()
|
||||||
void Preview::doSearch(const QString &_text, bool next, bool reverse,
|
void Preview::doSearch(const QString &_text, bool next, bool reverse,
|
||||||
bool wordOnly)
|
bool wordOnly)
|
||||||
{
|
{
|
||||||
LOGDEB(("Preview::doSearch: text [%s] txtlen %d next %d rev %d word %d\n",
|
LOGDEB(("Preview::doSearch: text [%s] idx %d next %d rev %d word %d\n",
|
||||||
(const char *)_text.toUtf8(), _text.length(), int(next),
|
(const char *)_text.toUtf8(), m_searchTextFromIndex, int(next),
|
||||||
int(reverse), int(wordOnly)));
|
int(reverse), int(wordOnly)));
|
||||||
QString text = _text;
|
QString text = _text;
|
||||||
|
|
||||||
bool matchCase = matchCheck->isChecked();
|
bool matchCase = matchCheck->isChecked();
|
||||||
|
@ -363,25 +458,19 @@ void Preview::doSearch(const QString &_text, bool next, bool reverse,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.isEmpty()) {
|
if (text.isEmpty() || m_searchTextFromIndex != -1) {
|
||||||
if (m_haveAnchors == false) {
|
if (!edit->m_plaintorich->haveAnchors()) {
|
||||||
LOGDEB(("NO ANCHORS\n"));
|
LOGDEB(("NO ANCHORS\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// The combobox indices are equal to the search ugroup indices
|
||||||
|
// in hldata, that's how we built the list.
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
if (m_curAnchor == 1)
|
edit->m_plaintorich->prevAnchorNum(m_searchTextFromIndex);
|
||||||
m_curAnchor = edit->m_plaintorich->lastanchor;
|
|
||||||
else
|
|
||||||
m_curAnchor--;
|
|
||||||
} else {
|
} else {
|
||||||
if (m_curAnchor == edit->m_plaintorich->lastanchor)
|
edit->m_plaintorich->nextAnchorNum(m_searchTextFromIndex);
|
||||||
m_curAnchor = 1;
|
|
||||||
else
|
|
||||||
m_curAnchor++;
|
|
||||||
}
|
}
|
||||||
LOGDEB(("m_curAnchor: %d\n", m_curAnchor));
|
QString aname = edit->m_plaintorich->curAnchorName();
|
||||||
QString aname =
|
|
||||||
QString::fromUtf8(edit->m_plaintorich->termAnchorName(m_curAnchor).c_str());
|
|
||||||
LOGDEB(("Calling scrollToAnchor(%s)\n", (const char *)aname.toUtf8()));
|
LOGDEB(("Calling scrollToAnchor(%s)\n", (const char *)aname.toUtf8()));
|
||||||
edit->scrollToAnchor(aname);
|
edit->scrollToAnchor(aname);
|
||||||
// Position the cursor approximately at the anchor (top of
|
// Position the cursor approximately at the anchor (top of
|
||||||
|
@ -440,14 +529,14 @@ void Preview::doSearch(const QString &_text, bool next, bool reverse,
|
||||||
|
|
||||||
void Preview::nextPressed()
|
void Preview::nextPressed()
|
||||||
{
|
{
|
||||||
LOGDEB2(("PreviewTextEdit::nextPressed\n"));
|
LOGDEB2(("Preview::nextPressed\n"));
|
||||||
doSearch(searchTextLine->text(), true, false);
|
doSearch(searchTextCMB->currentText(), true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preview::prevPressed()
|
void Preview::prevPressed()
|
||||||
{
|
{
|
||||||
LOGDEB2(("PreviewTextEdit::prevPressed\n"));
|
LOGDEB2(("Preview::prevPressed\n"));
|
||||||
doSearch(searchTextLine->text(), true, true);
|
doSearch(searchTextCMB->currentText(), true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when user clicks on tab
|
// Called when user clicks on tab
|
||||||
|
@ -456,7 +545,6 @@ void Preview::currentChanged(QWidget * tw)
|
||||||
LOGDEB2(("PreviewTextEdit::currentChanged\n"));
|
LOGDEB2(("PreviewTextEdit::currentChanged\n"));
|
||||||
PreviewTextEdit *edit =
|
PreviewTextEdit *edit =
|
||||||
tw->findChild<PreviewTextEdit*>("pvEdit");
|
tw->findChild<PreviewTextEdit*>("pvEdit");
|
||||||
m_currentW = tw;
|
|
||||||
LOGDEB1(("Preview::currentChanged(). Editor: %p\n", edit));
|
LOGDEB1(("Preview::currentChanged(). Editor: %p\n", edit));
|
||||||
|
|
||||||
if (edit == 0) {
|
if (edit == 0) {
|
||||||
|
@ -470,7 +558,7 @@ void Preview::currentChanged(QWidget * tw)
|
||||||
connect(this, SIGNAL(printCurrentPreviewRequest()), edit, SLOT(print()));
|
connect(this, SIGNAL(printCurrentPreviewRequest()), edit, SLOT(print()));
|
||||||
edit->installEventFilter(this);
|
edit->installEventFilter(this);
|
||||||
edit->viewport()->installEventFilter(this);
|
edit->viewport()->installEventFilter(this);
|
||||||
searchTextLine->installEventFilter(this);
|
searchTextCMB->installEventFilter(this);
|
||||||
emit(previewExposed(this, m_searchId, edit->m_docnum));
|
emit(previewExposed(this, m_searchId, edit->m_docnum));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,7 +595,7 @@ PreviewTextEdit *Preview::addEditorTab()
|
||||||
|
|
||||||
void Preview::setCurTabProps(const Rcl::Doc &doc, int docnum)
|
void Preview::setCurTabProps(const Rcl::Doc &doc, int docnum)
|
||||||
{
|
{
|
||||||
LOGDEB1(("PreviewTextEdit::setCurTabProps\n"));
|
LOGDEB1(("Preview::setCurTabProps\n"));
|
||||||
QString title;
|
QString title;
|
||||||
string ctitle;
|
string ctitle;
|
||||||
if (doc.getmeta(Rcl::Doc::keytt, &ctitle) && !ctitle.empty()) {
|
if (doc.getmeta(Rcl::Doc::keytt, &ctitle) && !ctitle.empty()) {
|
||||||
|
@ -721,8 +809,6 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
|
||||||
LoadGuard guard(&m_loading);
|
LoadGuard guard(&m_loading);
|
||||||
CancelCheck::instance().setCancel(false);
|
CancelCheck::instance().setCancel(false);
|
||||||
|
|
||||||
m_haveAnchors = false;
|
|
||||||
|
|
||||||
setCurTabProps(idoc, docnum);
|
setCurTabProps(idoc, docnum);
|
||||||
|
|
||||||
QString msg = QString("Loading: %1 (size %2 bytes)")
|
QString msg = QString("Loading: %1 (size %2 bytes)")
|
||||||
|
@ -956,23 +1042,20 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
|
||||||
|
|
||||||
|
|
||||||
// Position the editor so that the first search term is visible
|
// Position the editor so that the first search term is visible
|
||||||
m_haveAnchors = editor->m_plaintorich->lastanchor != 0;
|
if (searchTextCMB->currentText().length() != 0) {
|
||||||
if (searchTextLine->text().length() != 0) {
|
|
||||||
// If there is a current search string, perform the search
|
// If there is a current search string, perform the search
|
||||||
m_canBeep = true;
|
m_canBeep = true;
|
||||||
doSearch(searchTextLine->text(), true, false);
|
doSearch(searchTextCMB->currentText(), true, false);
|
||||||
} else {
|
} else {
|
||||||
// Position to the first query term
|
// Position to the first query term
|
||||||
if (m_haveAnchors) {
|
if (editor->m_plaintorich->haveAnchors()) {
|
||||||
QString aname =
|
QString aname = editor->m_plaintorich->curAnchorName();
|
||||||
QString::fromUtf8(editor->m_plaintorich->termAnchorName(1).c_str());
|
|
||||||
LOGDEB2(("Call movetoanchor(%s)\n", (const char *)aname.toUtf8()));
|
LOGDEB2(("Call movetoanchor(%s)\n", (const char *)aname.toUtf8()));
|
||||||
editor->scrollToAnchor(aname);
|
editor->scrollToAnchor(aname);
|
||||||
// Position the cursor approximately at the anchor (top of
|
// Position the cursor approximately at the anchor (top of
|
||||||
// viewport) so that searches start from here
|
// viewport) so that searches start from here
|
||||||
QTextCursor cursor = editor->cursorForPosition(QPoint(0, 0));
|
QTextCursor cursor = editor->cursorForPosition(QPoint(0, 0));
|
||||||
editor->setTextCursor(cursor);
|
editor->setTextCursor(cursor);
|
||||||
m_curAnchor = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -989,14 +1072,15 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PreviewTextEdit::PreviewTextEdit(QWidget* parent,const char* name, Preview *pv)
|
PreviewTextEdit::PreviewTextEdit(QWidget* parent, const char* nm, Preview *pv)
|
||||||
: QTextEdit(parent), m_preview(pv), m_dspflds(false), m_docnum(-1)
|
: QTextEdit(parent), m_preview(pv),
|
||||||
|
m_plaintorich(new PlainToRichQtPreview()),
|
||||||
|
m_dspflds(false), m_docnum(-1)
|
||||||
{
|
{
|
||||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
setObjectName(name);
|
setObjectName(nm);
|
||||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
|
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
|
||||||
this, SLOT(createPopupMenu(const QPoint&)));
|
this, SLOT(createPopupMenu(const QPoint&)));
|
||||||
m_plaintorich = new PlainToRichQtPreview();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PreviewTextEdit::~PreviewTextEdit()
|
PreviewTextEdit::~PreviewTextEdit()
|
||||||
|
|
|
@ -58,22 +58,27 @@ protected:
|
||||||
void mouseDoubleClickEvent(QMouseEvent *);
|
void mouseDoubleClickEvent(QMouseEvent *);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PlainToRichQtPreview *m_plaintorich;
|
|
||||||
Preview *m_preview;
|
Preview *m_preview;
|
||||||
bool m_dspflds;
|
PlainToRichQtPreview *m_plaintorich;
|
||||||
|
|
||||||
|
bool m_dspflds;
|
||||||
string m_url; // filename for this tab
|
string m_url; // filename for this tab
|
||||||
string m_ipath; // Internal doc path inside file
|
string m_ipath; // Internal doc path inside file
|
||||||
int m_docnum; // Index of doc in db search results.
|
int m_docnum; // Index of doc in db search results.
|
||||||
|
|
||||||
// doc out of internfile (previous fields come from the index) with
|
// doc out of internfile (previous fields come from the index) with
|
||||||
// main text erased (for space).
|
// main text erased (for space).
|
||||||
Rcl::Doc m_fdoc;
|
Rcl::Doc m_fdoc;
|
||||||
|
|
||||||
// The input doc out of the index/query list
|
// The input doc out of the index/query list
|
||||||
Rcl::Doc m_dbdoc;
|
Rcl::Doc m_dbdoc;
|
||||||
|
|
||||||
// Saved rich (or plain actually) text: the textedit seems to
|
// Saved rich (or plain actually) text: the textedit seems to
|
||||||
// sometimes (but not always) return its text stripped of tags, so
|
// sometimes (but not always) return its text stripped of tags, so
|
||||||
// this is needed (for printing for example)
|
// this is needed (for printing for example)
|
||||||
QString m_richtxt;
|
QString m_richtxt;
|
||||||
Qt::TextFormat m_format;
|
Qt::TextFormat m_format;
|
||||||
|
|
||||||
// Temporary file name (possibly, if displaying image). The
|
// Temporary file name (possibly, if displaying image). The
|
||||||
// TempFile itself is kept inside main.cpp (because that's where
|
// TempFile itself is kept inside main.cpp (because that's where
|
||||||
// signal cleanup happens), but we use its name to ask for release
|
// signal cleanup happens), but we use its name to ask for release
|
||||||
|
@ -92,14 +97,14 @@ class Preview : public QWidget {
|
||||||
|
|
||||||
Preview(int sid, // Search Id
|
Preview(int sid, // Search Id
|
||||||
const HighlightData& hdata) // Search terms etc. for highlighting
|
const HighlightData& hdata) // Search terms etc. for highlighting
|
||||||
: QWidget(0), m_searchId(sid), m_hData(hdata)
|
: QWidget(0), m_searchId(sid), m_searchTextFromIndex(-1), m_hData(hdata)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
~Preview(){}
|
|
||||||
|
|
||||||
virtual void closeEvent(QCloseEvent *e );
|
virtual void closeEvent(QCloseEvent *e );
|
||||||
virtual bool eventFilter(QObject *target, QEvent *event );
|
virtual bool eventFilter(QObject *target, QEvent *event );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrange for the document to be displayed either by exposing the tab
|
* Arrange for the document to be displayed either by exposing the tab
|
||||||
* if already loaded, or by creating a new tab and loading it.
|
* if already loaded, or by creating a new tab and loading it.
|
||||||
|
@ -112,7 +117,8 @@ class Preview : public QWidget {
|
||||||
friend class PreviewTextEdit;
|
friend class PreviewTextEdit;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void searchTextLine_textChanged(const QString& text);
|
virtual void searchTextChanged(const QString& text);
|
||||||
|
virtual void searchTextFromIndex(int);
|
||||||
virtual void doSearch(const QString& str, bool next, bool reverse,
|
virtual void doSearch(const QString& str, bool next, bool reverse,
|
||||||
bool wo = false);
|
bool wo = false);
|
||||||
virtual void nextPressed();
|
virtual void nextPressed();
|
||||||
|
@ -138,18 +144,17 @@ private:
|
||||||
int m_searchId;
|
int m_searchId;
|
||||||
|
|
||||||
bool m_dynSearchActive;
|
bool m_dynSearchActive;
|
||||||
|
// Index value the search text comes from. -1 if text was edited
|
||||||
|
int m_searchTextFromIndex;
|
||||||
|
|
||||||
bool m_canBeep;
|
bool m_canBeep;
|
||||||
bool m_loading;
|
bool m_loading;
|
||||||
QWidget *m_currentW;
|
|
||||||
HighlightData m_hData;
|
HighlightData m_hData;
|
||||||
bool m_justCreated; // First tab create is different
|
bool m_justCreated; // First tab create is different
|
||||||
bool m_haveAnchors; // Search terms are marked in text
|
|
||||||
int m_lastAnchor; // Number of last anchor. Then rewind to 1
|
|
||||||
int m_curAnchor;
|
|
||||||
|
|
||||||
QTabWidget* pvTab;
|
QTabWidget* pvTab;
|
||||||
QLabel* searchLabel;
|
QLabel* searchLabel;
|
||||||
QLineEdit* searchTextLine;
|
QComboBox *searchTextCMB;
|
||||||
QPushButton* nextButton;
|
QPushButton* nextButton;
|
||||||
QPushButton* prevButton;
|
QPushButton* prevButton;
|
||||||
QPushButton* clearPB;
|
QPushButton* clearPB;
|
||||||
|
|
|
@ -250,12 +250,22 @@ string QtGuiResListPager::iconUrl(RclConfig *config, Rcl::Doc& doc)
|
||||||
|
|
||||||
class PlainToRichQtReslist : public PlainToRich {
|
class PlainToRichQtReslist : public PlainToRich {
|
||||||
public:
|
public:
|
||||||
virtual ~PlainToRichQtReslist() {}
|
virtual string startMatch(unsigned int idx)
|
||||||
virtual string startMatch() {
|
{
|
||||||
|
if (m_hdata) {
|
||||||
|
string s1, s2;
|
||||||
|
stringsToString<vector<string> >(m_hdata->groups[idx], s1);
|
||||||
|
stringsToString<vector<string> >(m_hdata->ugroups[m_hdata->grpsugidx[idx]], s2);
|
||||||
|
LOGDEB(("Reslist startmatch: group %s user group %s\n", s1.c_str(), s2.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
return string("<span class='rclmatch' style='color: ")
|
return string("<span class='rclmatch' style='color: ")
|
||||||
+ string((const char *)prefs.qtermcolor.toAscii()) + string("'>");
|
+ string((const char *)prefs.qtermcolor.toAscii()) + string("'>");
|
||||||
}
|
}
|
||||||
virtual string endMatch() {return string("</span>");}
|
virtual string endMatch()
|
||||||
|
{
|
||||||
|
return string("</span>");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
static PlainToRichQtReslist g_hiliter;
|
static PlainToRichQtReslist g_hiliter;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ public:
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
~SSearch(){}
|
|
||||||
|
|
||||||
virtual void init();
|
virtual void init();
|
||||||
virtual void setAnyTermMode();
|
virtual void setAnyTermMode();
|
||||||
|
|
|
@ -49,13 +49,22 @@ static string vecStringToString(const vector<string>& t)
|
||||||
return sterms;
|
return sterms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MatchEntry {
|
||||||
|
pair<int, int> offs;
|
||||||
|
unsigned int grpidx;
|
||||||
|
MatchEntry(int sta, int sto, unsigned int idx)
|
||||||
|
: offs(sta, sto), grpidx(idx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Text splitter used to take note of the position of query terms
|
// Text splitter used to take note of the position of query terms
|
||||||
// inside the result text. This is then used to insert highlight tags.
|
// inside the result text. This is then used to insert highlight tags.
|
||||||
class TextSplitPTR : public TextSplit {
|
class TextSplitPTR : public TextSplit {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Out: begin and end byte positions of query terms/groups in text
|
// Out: begin and end byte positions of query terms/groups in text
|
||||||
vector<pair<int, int> > tboffs;
|
vector<MatchEntry> tboffs;
|
||||||
|
|
||||||
TextSplitPTR(const HighlightData& hdata)
|
TextSplitPTR(const HighlightData& hdata)
|
||||||
: m_wcount(0), m_hdata(hdata)
|
: m_wcount(0), m_hdata(hdata)
|
||||||
|
@ -67,7 +76,7 @@ class TextSplitPTR : public TextSplit {
|
||||||
for (vector<vector<string> >::const_iterator vit = hdata.groups.begin();
|
for (vector<vector<string> >::const_iterator vit = hdata.groups.begin();
|
||||||
vit != hdata.groups.end(); vit++) {
|
vit != hdata.groups.end(); vit++) {
|
||||||
if (vit->size() == 1) {
|
if (vit->size() == 1) {
|
||||||
m_terms.insert(vit->front());
|
m_terms[vit->front()] = vit - hdata.groups.begin();
|
||||||
} else if (vit->size() > 1) {
|
} else if (vit->size() > 1) {
|
||||||
for (vector<string>::const_iterator it = vit->begin();
|
for (vector<string>::const_iterator it = vit->begin();
|
||||||
it != vit->end(); it++) {
|
it != vit->end(); it++) {
|
||||||
|
@ -91,8 +100,9 @@ class TextSplitPTR : public TextSplit {
|
||||||
// pos, bts, bte));
|
// pos, bts, bte));
|
||||||
|
|
||||||
// If this word is a search term, remember its byte-offset span.
|
// If this word is a search term, remember its byte-offset span.
|
||||||
if (m_terms.find(dumb) != m_terms.end()) {
|
map<string, unsigned int>::const_iterator it = m_terms.find(dumb);
|
||||||
tboffs.push_back(pair<int, int>(bts, bte));
|
if (it != m_terms.end()) {
|
||||||
|
tboffs.push_back(MatchEntry(bts, bte, (*it).second));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If word is part of a search group, update its positions list
|
// If word is part of a search group, update its positions list
|
||||||
|
@ -114,13 +124,13 @@ class TextSplitPTR : public TextSplit {
|
||||||
virtual bool matchGroups();
|
virtual bool matchGroups();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool matchGroup(const vector<string>& terms, int dist);
|
virtual bool matchGroup(unsigned int idx);
|
||||||
|
|
||||||
// Word count. Used to call checkCancel from time to time.
|
// Word count. Used to call checkCancel from time to time.
|
||||||
int m_wcount;
|
int m_wcount;
|
||||||
|
|
||||||
// In: user query terms
|
// In: user query terms
|
||||||
set<string> m_terms;
|
map<string, unsigned int> m_terms;
|
||||||
|
|
||||||
// m_gterms holds all the terms in m_groups, as a set for quick lookup
|
// m_gterms holds all the terms in m_groups, as a set for quick lookup
|
||||||
set<string> m_gterms;
|
set<string> m_gterms;
|
||||||
|
@ -191,9 +201,12 @@ static bool do_proximity_test(int window, vector<vector<int>* >& plists,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find NEAR matches for the input group of terms, update highlight map
|
// Find NEAR matches for one group of terms, update highlight map
|
||||||
bool TextSplitPTR::matchGroup(const vector<string>& terms, int window)
|
bool TextSplitPTR::matchGroup(unsigned int grpidx)
|
||||||
{
|
{
|
||||||
|
const vector<string>& terms = m_hdata.groups[grpidx];
|
||||||
|
int window = m_hdata.groups[grpidx].size() + m_hdata.slacks[grpidx];
|
||||||
|
|
||||||
LOGDEB0(("TextSplitPTR::matchGroup:d %d: %s\n", window,
|
LOGDEB0(("TextSplitPTR::matchGroup:d %d: %s\n", window,
|
||||||
vecStringToString(terms).c_str()));
|
vecStringToString(terms).c_str()));
|
||||||
|
|
||||||
|
@ -203,26 +216,23 @@ bool TextSplitPTR::matchGroup(const vector<string>& terms, int window)
|
||||||
// A revert plist->term map. This is so that we can find who is who after
|
// A revert plist->term map. This is so that we can find who is who after
|
||||||
// sorting the plists by length.
|
// sorting the plists by length.
|
||||||
map<vector<int>*, string> plistToTerm;
|
map<vector<int>*, string> plistToTerm;
|
||||||
// For traces
|
|
||||||
vector<string> realgroup;
|
|
||||||
|
|
||||||
// Find the position list for each term in the group. Not all
|
// Find the position list for each term in the group. It is
|
||||||
// necessarily exist (esp for NEAR where terms have been
|
// possible that this particular group was not actually matched by
|
||||||
// stem-expanded: we don't know which matched)
|
// the search, so that some terms are not found.
|
||||||
for (vector<string>::const_iterator it = terms.begin();
|
for (vector<string>::const_iterator it = terms.begin();
|
||||||
it != terms.end(); it++) {
|
it != terms.end(); it++) {
|
||||||
map<string, vector<int> >::iterator pl = m_plists.find(*it);
|
map<string, vector<int> >::iterator pl = m_plists.find(*it);
|
||||||
if (pl == m_plists.end()) {
|
if (pl == m_plists.end()) {
|
||||||
LOGDEB0(("TextSplitPTR::matchGroup: [%s] not found in m_plists\n",
|
LOGDEB0(("TextSplitPTR::matchGroup: [%s] not found in m_plists\n",
|
||||||
(*it).c_str()));
|
(*it).c_str()));
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
plists.push_back(&(pl->second));
|
plists.push_back(&(pl->second));
|
||||||
plistToTerm[&(pl->second)] = *it;
|
plistToTerm[&(pl->second)] = *it;
|
||||||
realgroup.push_back(*it);
|
|
||||||
}
|
}
|
||||||
LOGDEB0(("TextSplitPTR::matchGroup:d %d:real group after expansion %s\n",
|
// I think this can't actually happen, was useful when we used to
|
||||||
window, vecStringToString(realgroup).c_str()));
|
// prune the groups, but doesn't hurt.
|
||||||
if (plists.size() < 2) {
|
if (plists.size() < 2) {
|
||||||
LOGDEB0(("TextSplitPTR::matchGroup: no actual groups found\n"));
|
LOGDEB0(("TextSplitPTR::matchGroup: no actual groups found\n"));
|
||||||
return false;
|
return false;
|
||||||
|
@ -261,15 +271,13 @@ bool TextSplitPTR::matchGroup(const vector<string>& terms, int window)
|
||||||
SETMINMAX(pos, sta, sto);
|
SETMINMAX(pos, sta, sto);
|
||||||
minpos = sto+1;
|
minpos = sto+1;
|
||||||
// Translate the position window into a byte offset window
|
// Translate the position window into a byte offset window
|
||||||
int bs = 0;
|
|
||||||
map<int, pair<int, int> >::iterator i1 = m_gpostobytes.find(sta);
|
map<int, pair<int, int> >::iterator i1 = m_gpostobytes.find(sta);
|
||||||
map<int, pair<int, int> >::iterator i2 = m_gpostobytes.find(sto);
|
map<int, pair<int, int> >::iterator i2 = m_gpostobytes.find(sto);
|
||||||
if (i1 != m_gpostobytes.end() && i2 != m_gpostobytes.end()) {
|
if (i1 != m_gpostobytes.end() && i2 != m_gpostobytes.end()) {
|
||||||
LOGDEB0(("TextSplitPTR::matchGroup: pushing bpos %d %d\n",
|
LOGDEB0(("TextSplitPTR::matchGroup: pushing bpos %d %d\n",
|
||||||
i1->second.first, i2->second.second));
|
i1->second.first, i2->second.second));
|
||||||
tboffs.push_back(pair<int, int>(i1->second.first,
|
tboffs.push_back(MatchEntry(i1->second.first,
|
||||||
i2->second.second));
|
i2->second.second, grpidx));
|
||||||
bs = i1->second.first;
|
|
||||||
} else {
|
} else {
|
||||||
LOGDEB(("matchGroup: no bpos found for %d or %d\n", sta, sto));
|
LOGDEB(("matchGroup: no bpos found for %d or %d\n", sta, sto));
|
||||||
}
|
}
|
||||||
|
@ -284,22 +292,23 @@ bool TextSplitPTR::matchGroup(const vector<string>& terms, int window)
|
||||||
/** Sort integer pairs by increasing first value and decreasing width */
|
/** Sort integer pairs by increasing first value and decreasing width */
|
||||||
class PairIntCmpFirst {
|
class PairIntCmpFirst {
|
||||||
public:
|
public:
|
||||||
bool operator()(pair<int,int> a, pair<int, int>b) {
|
bool operator()(const MatchEntry& a, const MatchEntry& b) {
|
||||||
if (a.first != b.first)
|
if (a.offs.first != b.offs.first)
|
||||||
return a.first < b.first;
|
return a.offs.first < b.offs.first;
|
||||||
return a.second > b.second;
|
return a.offs.second > b.offs.second;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Look for matches to PHRASE and NEAR term groups. Actually, we
|
// Look for matches to PHRASE and NEAR term groups and finalize the
|
||||||
// handle all groups as NEAR (ignore order).
|
// matched regions list (sort it by increasing start then decreasing
|
||||||
|
// length)
|
||||||
|
// Actually, we handle all groups as NEAR (ignore order).
|
||||||
bool TextSplitPTR::matchGroups()
|
bool TextSplitPTR::matchGroups()
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < m_hdata.groups.size(); i++) {
|
for (unsigned int i = 0; i < m_hdata.groups.size(); i++) {
|
||||||
if (m_hdata.groups[i].size() <= 1)
|
if (m_hdata.groups[i].size() <= 1)
|
||||||
continue;
|
continue;
|
||||||
matchGroup(m_hdata.groups[i],
|
matchGroup(i);
|
||||||
m_hdata.groups[i].size() + m_hdata.slacks[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort regions by increasing start and decreasing width.
|
// Sort regions by increasing start and decreasing width.
|
||||||
|
@ -324,6 +333,7 @@ bool PlainToRich::plaintorich(const string& in,
|
||||||
{
|
{
|
||||||
Chrono chron;
|
Chrono chron;
|
||||||
|
|
||||||
|
m_hdata = &hdata;
|
||||||
// Compute the positions for the query terms. We use the text
|
// Compute the positions for the query terms. We use the text
|
||||||
// splitter to break the text into words, and compare the words to
|
// splitter to break the text into words, and compare the words to
|
||||||
// the search terms,
|
// the search terms,
|
||||||
|
@ -346,8 +356,8 @@ bool PlainToRich::plaintorich(const string& in,
|
||||||
// Iterator for the list of input term positions. We use it to
|
// Iterator for the list of input term positions. We use it to
|
||||||
// output highlight tags and to compute term positions in the
|
// output highlight tags and to compute term positions in the
|
||||||
// output text
|
// output text
|
||||||
vector<pair<int, int> >::iterator tPosIt = splitter.tboffs.begin();
|
vector<MatchEntry>::iterator tPosIt = splitter.tboffs.begin();
|
||||||
vector<pair<int, int> >::iterator tPosEnd = splitter.tboffs.end();
|
vector<MatchEntry>::iterator tPosEnd = splitter.tboffs.end();
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
for (vector<pair<int, int> >::const_iterator it = splitter.tboffs.begin();
|
for (vector<pair<int, int> >::const_iterator it = splitter.tboffs.begin();
|
||||||
|
@ -365,8 +375,6 @@ bool PlainToRich::plaintorich(const string& in,
|
||||||
int hadcr = 0;
|
int hadcr = 0;
|
||||||
int inindent = 1;
|
int inindent = 1;
|
||||||
|
|
||||||
// Value for numbered anchors at each term match
|
|
||||||
int anchoridx = 1;
|
|
||||||
// HTML state
|
// HTML state
|
||||||
bool intag = false, inparamvalue = false;
|
bool intag = false, inparamvalue = false;
|
||||||
// My tag state
|
// My tag state
|
||||||
|
@ -391,22 +399,20 @@ bool PlainToRich::plaintorich(const string& in,
|
||||||
// we are at or after a term match, mark.
|
// we are at or after a term match, mark.
|
||||||
if (tPosIt != tPosEnd) {
|
if (tPosIt != tPosEnd) {
|
||||||
int ibyteidx = chariter.getBpos();
|
int ibyteidx = chariter.getBpos();
|
||||||
if (ibyteidx == tPosIt->first) {
|
if (ibyteidx == tPosIt->offs.first) {
|
||||||
if (!intag && ibyteidx >= (int)headend) {
|
if (!intag && ibyteidx >= (int)headend) {
|
||||||
*olit += startAnchor(anchoridx);
|
*olit += startMatch(tPosIt->grpidx);
|
||||||
*olit += startMatch();
|
|
||||||
}
|
}
|
||||||
anchoridx++;
|
|
||||||
inrcltag = 1;
|
inrcltag = 1;
|
||||||
} else if (ibyteidx == tPosIt->second) {
|
} else if (ibyteidx == tPosIt->offs.second) {
|
||||||
// Output end of match region tags
|
// Output end of match region tags
|
||||||
if (!intag && ibyteidx > (int)headend) {
|
if (!intag && ibyteidx > (int)headend) {
|
||||||
*olit += endMatch();
|
*olit += endMatch();
|
||||||
*olit += endAnchor();
|
|
||||||
}
|
}
|
||||||
// Skip all highlight areas that would overlap this one
|
// Skip all highlight areas that would overlap this one
|
||||||
int crend = tPosIt->second;
|
int crend = tPosIt->offs.second;
|
||||||
while (tPosIt != splitter.tboffs.end() && tPosIt->first < crend)
|
while (tPosIt != splitter.tboffs.end() &&
|
||||||
|
tPosIt->offs.first < crend)
|
||||||
tPosIt++;
|
tPosIt++;
|
||||||
inrcltag = 0;
|
inrcltag = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "hldata.h"
|
#include "hldata.h"
|
||||||
|
#include "cstr.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for highlighting search results. Overridable methods allow
|
* A class for highlighting search results. Overridable methods allow
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
class PlainToRich {
|
class PlainToRich {
|
||||||
public:
|
public:
|
||||||
PlainToRich()
|
PlainToRich()
|
||||||
: m_inputhtml(false)
|
: m_inputhtml(false), m_eolbr(false), m_hdata(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,18 +72,35 @@ public:
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Overridable output methods for headers, highlighting and marking tags */
|
/* Overridable output methods for headers, highlighting and marking tags */
|
||||||
virtual std::string header() {return snull;}
|
|
||||||
virtual std::string startMatch() {return snull;}
|
virtual std::string header()
|
||||||
virtual std::string endMatch() {return snull;}
|
{
|
||||||
virtual std::string startAnchor(int) {return snull;}
|
return cstr_null;
|
||||||
virtual std::string endAnchor() {return snull;}
|
}
|
||||||
virtual std::string startChunk() {return snull;}
|
|
||||||
|
/** Return match prefix (e.g.: <div class="match">).
|
||||||
|
@param groupidx the index into hdata.groups */
|
||||||
|
virtual std::string startMatch(unsigned int)
|
||||||
|
{
|
||||||
|
return cstr_null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return data for end of match area (e.g.: </div>). */
|
||||||
|
virtual std::string endMatch()
|
||||||
|
{
|
||||||
|
return cstr_null;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::string startChunk()
|
||||||
|
{
|
||||||
|
return cstr_null;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const std::string snull;
|
|
||||||
bool m_inputhtml;
|
bool m_inputhtml;
|
||||||
// Use <br> to break plain text lines (else caller has used a <pre> tag)
|
// Use <br> to break plain text lines (else caller has used a <pre> tag)
|
||||||
bool m_eolbr;
|
bool m_eolbr;
|
||||||
|
const HighlightData *m_hdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _PLAINTORICH_H_INCLUDED_ */
|
#endif /* _PLAINTORICH_H_INCLUDED_ */
|
||||||
|
|
|
@ -40,9 +40,14 @@ static const string cstr_hlfontcolor("<font color=\"blue\">");
|
||||||
static const string cstr_hlendfont("</font>");
|
static const string cstr_hlendfont("</font>");
|
||||||
class PlainToRichHtReslist : public PlainToRich {
|
class PlainToRichHtReslist : public PlainToRich {
|
||||||
public:
|
public:
|
||||||
virtual ~PlainToRichHtReslist() {}
|
virtual string startMatch(unsigned int)
|
||||||
virtual string startMatch() {return cstr_hlfontcolor;}
|
{
|
||||||
virtual string endMatch() {return cstr_hlendfont;}
|
return cstr_hlfontcolor;
|
||||||
|
}
|
||||||
|
virtual string endMatch()
|
||||||
|
{
|
||||||
|
return cstr_hlendfont;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
static PlainToRichHtReslist g_hiliter;
|
static PlainToRichHtReslist g_hiliter;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue