implemented advanced search history feature

This commit is contained in:
Jean-Francois Dockes 2012-10-16 13:37:56 +02:00
parent 303da79621
commit 2296a8d287
20 changed files with 736 additions and 97 deletions

View file

@ -1,3 +0,0 @@
#!/bin/sh
kchmviewer --url $2 $1

View file

@ -146,6 +146,7 @@ filters/rcldia
filters/rcldjvu
filters/rcldoc
filters/rcldvi
filters/rclepub
filters/rclexecm.py
filters/rclfb2
filters/rclgaim
@ -292,6 +293,8 @@ qtgui/
qtgui/advsearch.ui
qtgui/advsearch_w.cpp
qtgui/advsearch_w.h
qtgui/advshist.cpp
qtgui/advshist.h
qtgui/confgui/
qtgui/confgui/confgui.cpp
qtgui/confgui/confgui.h

View file

@ -27,18 +27,12 @@
#include <qtooltip.h>
#include <qwhatsthis.h>
#include <qmessagebox.h>
#include <QShortcut>
#include <list>
#include <string>
#include <map>
#include <algorithm>
#ifndef NO_NAMESPACES
using std::list;
using std::string;
using std::map;
using std::unique;
#endif /* NO_NAMESPACES */
using namespace std;
#include "recoll.h"
#include "rclconfig.h"
@ -52,7 +46,6 @@ static const unsigned int iclausescnt = sizeof(initclausetypes) / sizeof(int);
static map<QString,QString> cat_translations;
static map<QString,QString> cat_rtranslations;
void AdvSearch::init()
{
(void)new HelpClient(this);
@ -83,6 +76,9 @@ void AdvSearch::init()
connect(addClausePB, SIGNAL(clicked()), this, SLOT(addClause()));
connect(delClausePB, SIGNAL(clicked()), this, SLOT(delClause()));
new QShortcut(QKeySequence(Qt::Key_Up), this, SLOT(slotHistoryNext()));;
new QShortcut(QKeySequence(Qt::Key_Down), this, SLOT(slotHistoryPrev()));
conjunctCMB->insertItem(1, tr("All clauses"));
conjunctCMB->insertItem(2, tr("Any clause"));
@ -92,7 +88,7 @@ void AdvSearch::init()
}
// Tune initial state according to last saved
{
std::list<SearchClauseW *>::iterator cit = m_clauseWins.begin();
vector<SearchClauseW *>::iterator cit = m_clauseWins.begin();
for (vector<int>::iterator it = prefs.advSearchClauses.begin();
it != prefs.advSearchClauses.end(); it++) {
if (cit != m_clauseWins.end()) {
@ -109,6 +105,8 @@ void AdvSearch::init()
int minyear, maxyear;
if (rcldb) {
rcldb->maxYearSpan(&minyear, &maxyear);
minDateDTE->setDisplayFormat("dd.MM.yyyy");
maxDateDTE->setDisplayFormat("dd.MM.yyyy");
minDateDTE->setDate(QDate(minyear, 1, 1));
maxDateDTE->setDate(QDate(maxyear, 12, 31));
}
@ -158,7 +156,7 @@ void AdvSearch::saveCnf()
{
// Save my state
prefs.advSearchClauses.clear();
for (std::list<SearchClauseW *>::iterator cit = m_clauseWins.begin();
for (vector<SearchClauseW *>::iterator cit = m_clauseWins.begin();
cit != m_clauseWins.end(); cit++) {
prefs.advSearchClauses.push_back((*cit)->sTpCMB->currentIndex());
}
@ -170,12 +168,6 @@ bool AdvSearch::close()
return QWidget::close();
}
void AdvSearch::delAFiltypPB_clicked()
{
yesFiltypsLB->selectAll();
delFiltypPB_clicked();
}
void AdvSearch::addClause()
{
addClause(0);
@ -208,6 +200,12 @@ void AdvSearch::delClause()
}
}
void AdvSearch::delAFiltypPB_clicked()
{
yesFiltypsLB->selectAll();
delFiltypPB_clicked();
}
// Move selected file types from the searched to the ignored box
void AdvSearch::delFiltypPB_clicked()
{
@ -329,6 +327,12 @@ void AdvSearch::saveFileTypes()
rwSettings(true);
}
void AdvSearch::browsePB_clicked()
{
QString dir = myGetFileName(true);
subtreeCMB->setEditText(dir);
}
size_t AdvSearch::stringToSize(QString qsize)
{
size_t size = size_t(-1);
@ -361,7 +365,7 @@ void AdvSearch::runSearch()
SCLT_AND : SCLT_OR, stemLang));
bool hasclause = false;
for (list<SearchClauseW*>::iterator it = m_clauseWins.begin();
for (vector<SearchClauseW*>::iterator it = m_clauseWins.begin();
it != m_clauseWins.end(); it++) {
SearchDataClause *cl;
if ((cl = (*it)->getClause())) {
@ -441,12 +445,121 @@ void AdvSearch::runSearch()
prefs.asearchSubdirHist.push_back(subtreeCMB->itemText(index));
}
saveCnf();
g_advshistory && g_advshistory->push(sdata);
emit startSearch(sdata);
}
void AdvSearch::browsePB_clicked()
// Set up fields from existing search data, which must be compatible
// with what we can do...
void AdvSearch::fromSearch(RefCntr<SearchData> sdata)
{
QString dir = myGetFileName(true);
subtreeCMB->setEditText(dir);
if (sdata->m_tp == SCLT_OR)
conjunctCMB->setCurrentIndex(1);
else
conjunctCMB->setCurrentIndex(0);
while (sdata->m_query.size() > m_clauseWins.size()) {
addClause();
}
for (unsigned int i = 0; i < sdata->m_query.size(); i++) {
// Set fields from clause
if (sdata->m_query[i]->getTp() == SCLT_SUB) {
LOGERR(("AdvSearch::fromSearch: SUB clause found !\n"));
continue;
}
SearchDataClauseSimple *cs =
dynamic_cast<SearchDataClauseSimple*>(sdata->m_query[i]);
m_clauseWins[i]->setFromClause(cs);
}
for (unsigned int i = sdata->m_query.size(); i < m_clauseWins.size(); i++) {
m_clauseWins[i]->clear();
}
restrictCtCB->setChecked(0);
if (!sdata->m_filetypes.empty()) {
restrictFtCB_toggled(1);
delAFiltypPB_clicked();
for (unsigned int i = 0; i < sdata->m_filetypes.size(); i++) {
QString ft = QString::fromUtf8(sdata->m_filetypes[i].c_str());
QList<QListWidgetItem *> lst =
noFiltypsLB->findItems(ft, Qt::MatchExactly);
if (!lst.isEmpty()) {
int row = noFiltypsLB->row(lst[0]);
QListWidgetItem *item = noFiltypsLB->takeItem(row);
yesFiltypsLB->insertItem(0, item);
}
}
yesFiltypsLB->sortItems();
} else {
addAFiltypPB_clicked();
restrictFtCB_toggled(0);
}
if (sdata->m_haveDates) {
filterDatesCB->setChecked(1);
DateInterval &di(sdata->m_dates);
QDate mindate(di.y1, di.m1, di.d1);
QDate maxdate(di.y2, di.m2, di.d2);
minDateDTE->setDate(mindate);
maxDateDTE->setDate(maxdate);
} else {
filterDatesCB->setChecked(0);
QDate date;
minDateDTE->setDate(date);
maxDateDTE->setDate(date);
}
if (sdata->m_maxSize != (size_t)-1 || sdata->m_minSize != (size_t)-1) {
filterSizesCB->setChecked(1);
QString sz;
if (sdata->m_minSize != (size_t)-1) {
sz.setNum(sdata->m_minSize);
minSizeLE->setText(sz);
} else {
minSizeLE->setText("");
}
if (sdata->m_maxSize != (size_t)-1) {
sz.setNum(sdata->m_maxSize);
maxSizeLE->setText(sz);
} else {
maxSizeLE->setText("");
}
} else {
filterSizesCB->setChecked(0);
minSizeLE->setText("");
maxSizeLE->setText("");
}
if (!sdata->m_dirspecs.empty()) {
// Can only use one entry
QString qdir = QString::fromLocal8Bit(sdata->m_dirspecs[0].dir.c_str());
subtreeCMB->setEditText(qdir);
direxclCB->setChecked(sdata->m_dirspecs[0].exclude);
} else {
subtreeCMB->setEditText("");
direxclCB->setChecked(0);
}
}
void AdvSearch::slotHistoryNext()
{
if (g_advshistory == 0)
return;
RefCntr<Rcl::SearchData> sd = g_advshistory->getnewer();
if (sd.isNull())
return;
fromSearch(sd);
}
void AdvSearch::slotHistoryPrev()
{
if (g_advshistory == 0)
return;
RefCntr<Rcl::SearchData> sd = g_advshistory->getolder();
if (sd.isNull())
return;
fromSearch(sd);
}

View file

@ -16,7 +16,9 @@
*/
#ifndef _ADVSEARCH_W_H_INCLUDED_
#define _ADVSEARCH_W_H_INCLUDED_
#include <list>
#include <vector>
#include <qvariant.h>
#include <qdialog.h>
@ -24,6 +26,7 @@
#include "recoll.h"
#include "refcntr.h"
#include "searchdata.h"
#include "advshist.h"
class QDialog;
@ -53,19 +56,22 @@ public slots:
virtual void restrictFtCB_toggled(bool);
virtual void restrictCtCB_toggled(bool);
virtual void runSearch();
virtual void fromSearch(RefCntr<Rcl::SearchData> sdata);
virtual void browsePB_clicked();
virtual void saveFileTypes();
virtual void delClause();
virtual void addClause();
virtual void addClause(int);
virtual bool close();
virtual void slotHistoryNext();
virtual void slotHistoryPrev();
signals:
void startSearch(RefCntr<Rcl::SearchData>);
private:
virtual void init();
std::list<SearchClauseW *> m_clauseWins;
std::vector<SearchClauseW *> m_clauseWins;
QStringList m_ignTypes;
bool m_ignByCats;
void saveCnf();

253
src/qtgui/advshist.cpp Normal file
View file

@ -0,0 +1,253 @@
/* Copyright (C) 2005 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "autoconfig.h"
#include "advshist.h"
#include "guiutils.h"
#include "debuglog.h"
#include <QtXml/QXmlDefaultHandler>
using namespace std;
using namespace Rcl;
class SDHXMLHandler : public QXmlDefaultHandler {
public:
SDHXMLHandler()
: slack(0)
{
}
bool startElement(const QString & /* namespaceURI */,
const QString & /* localName */,
const QString &qName,
const QXmlAttributes &attributes);
bool endElement(const QString & /* namespaceURI */,
const QString & /* localName */,
const QString &qName);
bool characters(const QString &str)
{
currentText += str;
return true;
}
// The object we set up
RefCntr<SearchData> sd;
private:
void resetTemps()
{
currentText = whatclause = "";
text.clear();
field.clear();
slack = 0;
d = m = y = di.d1 = di.m1 = di.y1 = di.d2 = di.m2 = di.y2 = 0;
hasdates = false;
}
// Temporary data while parsing.
QString currentText;
QString whatclause;
string field, text;
int slack;
int d, m, y;
DateInterval di;
bool hasdates;
};
bool SDHXMLHandler::startElement(const QString & /* namespaceURI */,
const QString & /* localName */,
const QString &qName,
const QXmlAttributes &)
{
LOGDEB2(("SDHXMLHandler::startElement: name [%s]\n",
(const char *)qName.toAscii()));
if (qName == "SD") {
resetTemps();
// A new search descriptor. Allocate data structure
sd = RefCntr<SearchData>(new SearchData);
if (sd.isNull()) {
LOGERR(("SDHXMLHandler::startElement: out of memory\n"));
return false;
}
}
return true;
}
bool SDHXMLHandler::endElement(const QString & /* namespaceURI */,
const QString & /* localName */,
const QString &qName)
{
LOGDEB2(("SDHXMLHandler::endElement: name [%s]\n",
(const char *)qName.toAscii()));
if (qName == "CLT") {
if (currentText == "OR") {
sd->setTp(SCLT_OR);
}
} else if (qName == "CT") {
whatclause = currentText.trimmed();
} else if (qName == "F") {
field = base64_decode(qs2utf8s(currentText.trimmed()));
} else if (qName == "T") {
text = base64_decode(qs2utf8s(currentText.trimmed()));
} else if (qName == "S") {
slack = atoi((const char *)currentText.toAscii());
} else if (qName == "C") {
SearchDataClauseSimple *c;
if (whatclause == "AND" || whatclause.isEmpty()) {
c = new SearchDataClauseSimple(SCLT_AND, text, field);
} else if (whatclause == "OR") {
c = new SearchDataClauseSimple(SCLT_OR, text, field);
} else if (whatclause == "EX") {
c = new SearchDataClauseSimple(SCLT_EXCL, text, field);
} else if (whatclause == "FN") {
c = new SearchDataClauseFilename(text);
} else if (whatclause == "PH") {
c = new SearchDataClauseDist(SCLT_PHRASE, text, slack, field);
} else if (whatclause == "NE") {
c = new SearchDataClauseDist(SCLT_NEAR, text, slack, field);
} else {
LOGERR(("Bad clause type [%s]\n", qs2utf8s(whatclause).c_str()));
return false;
}
sd->addClause(c);
whatclause = "";
text.clear();
field.clear();
slack = 0;
} else if (qName == "D") {
d = atoi((const char *)currentText.toAscii());
} else if (qName == "M") {
m = atoi((const char *)currentText.toAscii());
} else if (qName == "Y") {
y = atoi((const char *)currentText.toAscii());
} else if (qName == "DMI") {
di.d1 = d;
di.m1 = m;
di.y1 = y;
hasdates = true;
} else if (qName == "DMA") {
di.d2 = d;
di.m2 = m;
di.y2 = y;
hasdates = true;
} else if (qName == "MIS") {
sd->setMinSize(atoll((const char *)currentText.toAscii()));
} else if (qName == "MAS") {
sd->setMaxSize(atoll((const char *)currentText.toAscii()));
} else if (qName == "ST") {
string types = (const char *)currentText.toAscii();
vector<string> vt;
stringToTokens(types, vt);
for (unsigned int i = 0; i < vt.size(); i++)
sd->addFiletype(vt[i]);
} else if (qName == "IT") {
string types = (const char *)currentText.toAscii();
vector<string> vt;
stringToTokens(types, vt);
for (unsigned int i = 0; i < vt.size(); i++)
sd->remFiletype(vt[i]);
} else if (qName == "YD") {
string d;
base64_decode((const char*)currentText.trimmed().toAscii(), d);
sd->addDirSpec(d);
} else if (qName == "ND") {
string d;
base64_decode((const char*)currentText.trimmed().toAscii(), d);
sd->addDirSpec(d, true);
} else if (qName == "SD") {
// Closing current search descriptor. Finishing touches...
if (hasdates)
sd->setDateSpan(&di);
resetTemps();
}
currentText.clear();
return true;
}
AdvSearchHist::AdvSearchHist()
{
read();
m_current = -1;
}
AdvSearchHist::~AdvSearchHist()
{
for (vector<RefCntr<SearchData> >::iterator it = m_entries.begin();
it != m_entries.end(); it++) {
it->release();
}
}
RefCntr<Rcl::SearchData> AdvSearchHist::getolder()
{
m_current++;
if (m_current >= int(m_entries.size())) {
m_current--;
return RefCntr<Rcl::SearchData>();
}
return m_entries[m_current];
}
RefCntr<Rcl::SearchData> AdvSearchHist::getnewer()
{
if (m_current == -1 || m_current == 0 || m_entries.empty())
return RefCntr<Rcl::SearchData>();
return m_entries[--m_current];
}
bool AdvSearchHist::push(RefCntr<SearchData> sd)
{
m_entries.insert(m_entries.begin(), sd);
if (m_current != -1)
m_current++;
string xml = sd->asXML();
g_dynconf->enterString(advSearchHistSk, xml, 100);
return true;
}
bool AdvSearchHist::read()
{
if (!g_dynconf)
return false;
list<string> lxml = g_dynconf->getStringList(advSearchHistSk);
for (list<string>::const_iterator it = lxml.begin(); it != lxml.end();
it++) {
SDHXMLHandler handler;
QXmlSimpleReader reader;
reader.setContentHandler(&handler);
reader.setErrorHandler(&handler);
QXmlInputSource xmlInputSource;
xmlInputSource.setData(QString::fromUtf8(it->c_str()));
if (!reader.parse(xmlInputSource)) {
LOGERR(("AdvSearchHist::read: parse failed for [%s]\n",
it->c_str()));
return false;
}
m_entries.push_back(handler.sd);
}
return true;
}
void AdvSearchHist::clear()
{
g_dynconf->eraseAll(advSearchHistSk);
}

43
src/qtgui/advshist.h Normal file
View file

@ -0,0 +1,43 @@
/* Copyright (C) 2005 J.F.Dockes
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _ADVSHIST_H_INCLUDED_
#define _ADVSHIST_H_INCLUDED_
#include <vector>
#include "recoll.h"
#include "refcntr.h"
#include "searchdata.h"
class AdvSearchHist {
public:
AdvSearchHist();
~AdvSearchHist();
RefCntr<Rcl::SearchData> getolder();
RefCntr<Rcl::SearchData> getnewer();
bool push(RefCntr<Rcl::SearchData>);
void clear();
private:
bool read();
int m_current;
std::vector<RefCntr<Rcl::SearchData> > m_entries;
};
#endif // _ADVSHIST_H_INCLUDED_

View file

@ -25,12 +25,14 @@
#include "guiutils.h"
#include "pathut.h"
#include "base64.h"
#include "advshist.h"
#include <QSettings>
#include <QStringList>
#include <QFileDialog>
RclDynConf *g_dynconf;
AdvSearchHist *g_advshistory;
// The table should not be necessary, but I found no css way to get
// qt 4.6 qtextedit to clear the margins after the float img without

View file

@ -27,6 +27,9 @@
#include "dynconf.h"
extern RclDynConf *g_dynconf;
#include "advshist.h"
extern AdvSearchHist *g_advshistory;
#ifndef NO_NAMESPACES
using std::string;
using std::list;

View file

@ -338,6 +338,7 @@ int main(int argc, char **argv)
QMessageBox::critical(0, "Recoll", msg);
exit(1);
}
g_advshistory = new AdvSearchHist;
// fprintf(stderr, "History done\n");
rwSettings(false);

View file

@ -1855,7 +1855,10 @@ void RclMain::eraseDocHistory()
void RclMain::eraseSearchHistory()
{
prefs.ssearchHistory.clear();
sSearch->queryText->clear();
if (sSearch)
sSearch->queryText->clear();
if (g_advshistory)
g_advshistory->clear();
}
// Called when the uiprefs dialog is ok'd

View file

@ -16,6 +16,7 @@
*/
#ifndef _RECOLL_H_INCLUDED_
#define _RECOLL_H_INCLUDED_
#include <string>
#include "rclconfig.h"
@ -51,4 +52,8 @@ class Aspell;
extern Aspell *aspell;
#endif
inline std::string qs2utf8s(const QString& qs)
{
return std::string((const char *)qs.toUtf8());
}
#endif /* _RECOLL_H_INCLUDED_ */

View file

@ -7,10 +7,13 @@ LANGUAGE = C++
@QMAKE_ENABLE_ZEITGEIST@QT += dbus
@QMAKE_ENABLE_ZEITGEIST@QMAKE_CXXFLAGS += -DUSE_ZEITGEIST
QT += xml
CONFIG += qt warn_on thread release
HEADERS += \
advsearch_w.h \
advshist.h \
confgui/confgui.h \
confgui/confguiindex.h \
crontool.h \
@ -34,6 +37,7 @@ HEADERS += \
SOURCES += \
advsearch_w.cpp \
advshist.cpp \
confgui/confgui.cpp \
confgui/confguiindex.cpp \
crontool.cpp \
@ -54,7 +58,6 @@ SOURCES += \
viewaction_w.cpp \
FORMS = \
advsearch.ui \
crontool.ui \

View file

@ -17,6 +17,7 @@
#include "autoconfig.h"
#include "recoll.h"
#include "debuglog.h"
#include "searchclause_w.h"
@ -28,6 +29,8 @@
#include <qtooltip.h>
#include <qwhatsthis.h>
using namespace Rcl;
/*
* Constructs a SearchClauseW as a child of 'parent', with the
* name 'name' and widget flags set to 'f'.
@ -100,11 +103,8 @@ void SearchClauseW::languageChange()
proxSlackSB->setToolTip(tr("Number of additional words that may be interspersed with the chosen ones"));
}
using namespace Rcl;
// Translate my window state into an Rcl search clause
SearchDataClause *
SearchClauseW::getClause()
SearchDataClause *SearchClauseW::getClause()
{
if (wordsLE->text().isEmpty())
return 0;
@ -134,6 +134,61 @@ SearchClauseW::getClause()
}
}
void SearchClauseW::setFromClause(SearchDataClauseSimple *cl)
{
LOGDEB(("SearchClauseW::setFromClause\n"));
switch(cl->getTp()) {
case SCLT_OR: tpChange(0); break;
case SCLT_AND: tpChange(1); break;
case SCLT_EXCL: tpChange(2); break;
case SCLT_PHRASE: tpChange(3); break;
case SCLT_NEAR: tpChange(4); break;
case SCLT_FILENAME: tpChange(5); break;
default: return;
}
LOGDEB(("SearchClauseW::setFromClause: calling erase\n"));
clear();
QString text = QString::fromUtf8(cl->gettext().c_str());
QString field = QString::fromUtf8(cl->getfield().c_str());
switch(cl->getTp()) {
case SCLT_OR: case SCLT_AND: case SCLT_EXCL:
case SCLT_PHRASE: case SCLT_NEAR:
if (!field.isEmpty()) {
int idx = fldCMB->findText(field);
if (field >= 0) {
fldCMB->setCurrentIndex(idx);
} else {
fldCMB->setEditText(field);
}
}
/* FALLTHROUGH */
case SCLT_FILENAME:
wordsLE->setText(text);
break;
default: break;
}
switch(cl->getTp()) {
case SCLT_PHRASE: case SCLT_NEAR:
{
SearchDataClauseDist *cls = dynamic_cast<SearchDataClauseDist*>(cl);
proxSlackSB->setValue(cls->getslack());
}
break;
default: break;
}
}
void SearchClauseW::clear()
{
wordsLE->setText("");
fldCMB->setCurrentIndex(0);
proxSlackSB->setValue(0);
}
// Handle combobox change: may need to enable/disable the distance
// spinbox and field spec
void SearchClauseW::tpChange(int index)

View file

@ -37,6 +37,8 @@ public:
SearchClauseW(QWidget* parent = 0);
~SearchClauseW();
Rcl::SearchDataClause *getClause();
void setFromClause(Rcl::SearchDataClauseSimple *cl);
void clear();
QComboBox* sTpCMB;
QComboBox* fldCMB;

View file

@ -25,22 +25,17 @@
#include "smallut.h"
#include "debuglog.h"
#ifndef NO_NAMESPACES
using namespace std;
#endif
// Well known keys for history and external indexes.
const string docHistSubKey = "docs";
const string allEdbsSk = "allExtDbs";
const string actEdbsSk = "actExtDbs";
const string advSearchHistSk = "advSearchHist";
// @param sk section this is for
// @param n new entry
// @param s a scratch entry used for decoding and comparisons.
// This avoids templating this routine for the actual entry type.
bool RclDynConf::insertNew(const string &sk, DynConfEntry &n, DynConfEntry &s,
int maxlen)
int maxlen)
{
// Is this doc already in list ? If it is we remove the old entry
vector<string> names = m_data.getNames(sk);
@ -98,37 +93,21 @@ bool RclDynConf::eraseAll(const string &sk)
vector<string> names = m_data.getNames(sk);
vector<string>::const_iterator it;
for (it = names.begin(); it != names.end(); it++) {
m_data.erase(*it, sk);
m_data.erase(*it, sk);
}
return true;
}
// Generic string list specialization ///////////////////////////////////
// Specialization for plain strings ///////////////////////////////////
// Encode/decode simple string. base64 used to avoid problems with
// strange chars
bool RclSListEntry::encode(string& enc)
{
base64_encode(value, enc);
return true;
}
bool RclSListEntry::decode(const string &enc)
{
base64_decode(enc, value);
return true;
}
bool RclSListEntry::equal(const DynConfEntry& other)
{
const RclSListEntry& e = dynamic_cast<const RclSListEntry&>(other);
return e.value == value;
}
bool RclDynConf::enterString(const string sk, const string value, int maxlen)
{
RclSListEntry ne(value);
RclSListEntry scratch;
return insertNew(sk, ne, scratch, maxlen);
}
list<string> RclDynConf::getStringList(const string sk)
{
list<RclSListEntry> el = getList<RclSListEntry>(sk);

View file

@ -34,6 +34,9 @@
* encodings which depend on the data stored. Under each section, the keys
* are sequential numeric, so this basically manages a set of lists.
*
* The code ensures that a a given value (as defined by the
* DynConfEntry::equal() method is only stored once. If undesirable,
* equal() should always return false.
*/
#include <string>
@ -41,67 +44,95 @@
#include <utility>
#include "conftree.h"
#include "base64.h"
#ifndef NO_NAMESPACES
using namespace std;
#endif
// Entry interface.
/** Interface for a stored object. */
class DynConfEntry {
public:
virtual ~DynConfEntry() {}
virtual bool decode(const string &value) = 0;
virtual bool encode(string& value) = 0;
/** Decode object-as-string coming out from storage */
virtual bool decode(const std::string &value) = 0;
/** Encode object state into state for storing */
virtual bool encode(std::string& value) = 0;
/** Compare objects */
virtual bool equal(const DynConfEntry &other) = 0;
};
/** String storage generic object */
/** Stored object specialization for generic string storage */
class RclSListEntry : public DynConfEntry {
public:
RclSListEntry() {}
RclSListEntry(const string& v) : value(v) {}
virtual ~RclSListEntry() {}
virtual bool decode(const string &enc);
virtual bool encode(string& enc);
virtual bool equal(const DynConfEntry& other);
RclSListEntry()
{
}
RclSListEntry(const std::string& v)
: value(v)
{
}
virtual bool decode(const std::string &enc)
{
base64_decode(enc, value);
return true;
}
virtual bool encode(std::string& enc)
{
base64_encode(value, enc);
return true;
}
virtual bool equal(const DynConfEntry& other)
{
const RclSListEntry& e = dynamic_cast<const RclSListEntry&>(other);
return e.value == value;
}
string value;
std::string value;
};
/** The dynamic configuration class */
class RclDynConf {
public:
RclDynConf(const string &fn)
: m_data(fn.c_str()) {}
bool ok() {return m_data.getStatus() == ConfSimple::STATUS_RW;}
string getFilename() {return m_data.getFilename();}
RclDynConf(const std::string &fn)
: m_data(fn.c_str())
{
}
bool ok()
{
return m_data.getStatus() == ConfSimple::STATUS_RW;
}
std::string getFilename()
{
return m_data.getFilename();
}
// Generic methods
bool eraseAll(const string& sk);
bool insertNew(const string& sk, DynConfEntry &n, DynConfEntry &s,
bool eraseAll(const std::string& sk);
/** Insert new entry for section sk
* @param sk section this is for
* @param n new entry
* @param s a scratch entry used for decoding and comparisons,
* avoiding templating the routine for the actual entry type.
*/
bool insertNew(const std::string& sk, DynConfEntry &n, DynConfEntry &s,
int maxlen = -1);
template<typename Tp> list<Tp> getList(const string& sk);
template<typename Tp> std::list<Tp> getList(const std::string& sk);
// Specialized methods for simple string lists, designated by the
// subkey value
bool enterString(const string sk, const string value, int maxlen = -1);
list<string> getStringList(const string sk);
bool enterString(const std::string sk, const std::string value, int maxlen = -1);
std::list<std::string> getStringList(const std::string sk);
private:
unsigned int m_mlen;
ConfSimple m_data;
};
template<typename Tp> list<Tp> RclDynConf::getList(const string &sk)
template<typename Tp> std::list<Tp> RclDynConf::getList(const std::string &sk)
{
list<Tp> mlist;
std::list<Tp> mlist;
Tp entry;
vector<string> names = m_data.getNames(sk);
for (vector<string>::const_iterator it = names.begin();
std::vector<std::string> names = m_data.getNames(sk);
for (std::vector<std::string>::const_iterator it = names.begin();
it != names.end(); it++) {
string value;
std::string value;
if (m_data.get(*it, value, sk)) {
if (!entry.decode(value))
continue;
@ -113,10 +144,12 @@ template<typename Tp> list<Tp> RclDynConf::getList(const string &sk)
// Defined subkeys. Values in dynconf.cpp
// History
extern const string docHistSubKey;
extern const std::string docHistSubKey;
// All external indexes
extern const string allEdbsSk;
extern const std::string allEdbsSk;
// Active external indexes
extern const string actEdbsSk;
extern const std::string actEdbsSk;
// Advanced search history
extern const std::string advSearchHistSk;
#endif /* _DYNCONF_H_INCLUDED_ */

View file

@ -25,6 +25,7 @@
#include <string>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std;
#include "xapian.h"
@ -44,6 +45,7 @@ using namespace std;
#include "synfamily.h"
#include "stemdb.h"
#include "expansiondbs.h"
#include "base64.h"
namespace Rcl {
@ -253,6 +255,112 @@ bool SearchData::clausesToQuery(Rcl::Db &db, SClType tp,
return true;
}
static string tpToString(SClType tp)
{
switch (tp) {
case SCLT_AND: return "AND";
case SCLT_OR: return "OR";
case SCLT_EXCL: return "EX";
case SCLT_FILENAME: return "FN";
case SCLT_PHRASE: return "PH";
case SCLT_NEAR: return "NE";
case SCLT_SUB: return "SU"; // Unsupported actually
default: return "UN";
}
}
string SearchData::asXML()
{
LOGDEB(("SearchData::asXML\n"));
ostringstream os;
// Searchdata
os << "<SD>" << endl;
// Clause list
os << "<CL>" << endl;
if (m_tp != SCLT_AND)
os << "<CLT>" << tpToString(m_tp) << "</CLT>" << endl;
for (unsigned int i = 0; i < m_query.size(); i++) {
SearchDataClause *c = m_query[i];
if (c->getTp() == SCLT_SUB) {
LOGERR(("SearchData::asXML: can't do subclauses !\n"));
continue;
}
SearchDataClauseSimple *cl =
dynamic_cast<SearchDataClauseSimple*>(c);
os << "<C>" << endl;
if (cl->getTp() != SCLT_AND) {
os << "<CT>" << tpToString(cl->getTp()) << "</CT>" << endl;
}
if (cl->getTp() != SCLT_FILENAME && !cl->getfield().empty()) {
os << "<F>" << base64_encode(cl->getfield()) << "</F>" << endl;
}
os << "<T>" << base64_encode(cl->gettext()) << "</T>" << endl;
if (cl->getTp() == SCLT_NEAR || cl->getTp() == SCLT_PHRASE) {
SearchDataClauseDist *cld =
dynamic_cast<SearchDataClauseDist*>(cl);
os << "<S>" << cld->getslack() << "</S>" << endl;
}
os << "</C>" << endl;
}
os << "</CL>" << endl;
if (m_haveDates) {
if (m_dates.y1 > 0) {
os << "<DMI>" <<
"<D>" << m_dates.d1 << "</D>" <<
"<M>" << m_dates.m1 << "</M>" <<
"<Y>" << m_dates.y1 << "</Y>"
<< "</DMI>" << endl;
}
if (m_dates.y2 > 0) {
os << "<DMA>" <<
"<D>" << m_dates.d2 << "</D>" <<
"<M>" << m_dates.m2 << "</M>" <<
"<Y>" << m_dates.y2 << "</Y>"
<< "</DMA>" << endl;
}
}
if (m_minSize != size_t(-1)) {
os << "<MIS>" << m_minSize << "</MIS>" << endl;
}
if (m_maxSize != size_t(-1)) {
os << "<MAS>" << m_maxSize << "</MAS>" << endl;
}
if (!m_filetypes.empty()) {
os << "<ST>";
for (vector<string>::iterator it = m_filetypes.begin();
it != m_filetypes.end(); it++) {
os << *it << " ";
}
os << "</ST>" << endl;
}
if (!m_nfiletypes.empty()) {
os << "<IT>";
for (vector<string>::iterator it = m_nfiletypes.begin();
it != m_nfiletypes.end(); it++) {
os << *it << " ";
}
os << "</IT>" << endl;
}
for (vector<DirSpec>::const_iterator dit = m_dirspecs.begin();
dit != m_dirspecs.end(); dit++) {
if (dit->exclude) {
os << "<ND>" << base64_encode(dit->dir) << "</ND>" << endl;
} else {
os << "<YD>" << base64_encode(dit->dir) << "</YD>" << endl;
}
}
os << "</SD>";
return os.str();
}
bool SearchData::toNativeQuery(Rcl::Db &db, void *d, int maxexp, int maxcl)
{
LOGDEB(("SearchData::toNativeQuery: stemlang [%s]\n", m_stemlang.c_str()));

View file

@ -34,6 +34,7 @@
#include "hldata.h"
class RclConfig;
class AdvSearch;
namespace Rcl {
@ -77,6 +78,12 @@ public:
if (m_tp != SCLT_OR && m_tp != SCLT_AND)
m_tp = SCLT_OR;
}
SearchData()
: m_tp(SCLT_AND), m_haveDates(false), m_maxSize(size_t(-1)),
m_minSize(size_t(-1)), m_haveWildCards(false), m_stemlang("english")
{
}
~SearchData() {erase();}
/** Make pristine */
@ -136,6 +143,12 @@ public:
std::string getDescription() {return m_description;}
void setDescription(const std::string& d) {m_description = d;}
virtual string asXML();
void setTp(SClType tp)
{
m_tp = tp;
}
friend class ::AdvSearch;
private:
// Combine type. Only SCLT_AND or SCLT_OR here
SClType m_tp;
@ -196,7 +209,7 @@ public:
virtual std::string getReason() const {return m_reason;}
virtual void getTerms(HighlightData & hldata) const = 0;
SClType getTp()
SClType getTp() const
{
return m_tp;
}
@ -326,6 +339,10 @@ public:
}
virtual bool toNativeQuery(Rcl::Db &, void *, int maxexp, int maxcl);
virtual int getslack() const
{
return m_slack;
}
private:
int m_slack;
};

View file

@ -91,7 +91,7 @@ bool base64_decode(const string& in, string& out)
{
int io = 0, state = 0, ch = 0;
unsigned int ii = 0;
out.erase();
out.clear();
size_t ilen = in.length();
out.reserve(ilen);
@ -217,7 +217,7 @@ void base64_encode(const string &in, string &out)
unsigned char input[3];
unsigned char output[4];
out.erase();
out.clear();
int srclength = in.length();
int sidx = 0;

View file

@ -20,5 +20,18 @@
void base64_encode(const std::string& in, std::string& out);
bool base64_decode(const std::string& in, std::string& out);
inline std::string base64_encode(const std::string& in)
{
std::string o;
base64_encode(in, o);
return o;
}
inline std::string base64_decode(const std::string& in)
{
std::string o;
if (base64_decode(in, o))
return o;
return std::string();
}
#endif /* _BASE64_H_INCLUDED_ */