diff --git a/src/filters/rclshowchm b/src/filters/rclshowchm deleted file mode 100755 index a1ef8e06..00000000 --- a/src/filters/rclshowchm +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -kchmviewer --url $2 $1 diff --git a/src/mk/manifest.txt b/src/mk/manifest.txt index 0d9f45b7..18cf1672 100644 --- a/src/mk/manifest.txt +++ b/src/mk/manifest.txt @@ -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 diff --git a/src/qtgui/advsearch_w.cpp b/src/qtgui/advsearch_w.cpp index 9d0c62f5..d4986107 100644 --- a/src/qtgui/advsearch_w.cpp +++ b/src/qtgui/advsearch_w.cpp @@ -27,18 +27,12 @@ #include #include #include +#include -#include #include #include #include - -#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 cat_translations; static map 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::iterator cit = m_clauseWins.begin(); + vector::iterator cit = m_clauseWins.begin(); for (vector::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::iterator cit = m_clauseWins.begin(); + for (vector::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::iterator it = m_clauseWins.begin(); + for (vector::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 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(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 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 sd = g_advshistory->getnewer(); + if (sd.isNull()) + return; + fromSearch(sd); +} + +void AdvSearch::slotHistoryPrev() +{ + if (g_advshistory == 0) + return; + RefCntr sd = g_advshistory->getolder(); + if (sd.isNull()) + return; + fromSearch(sd); +} + diff --git a/src/qtgui/advsearch_w.h b/src/qtgui/advsearch_w.h index bc546874..7c77e071 100644 --- a/src/qtgui/advsearch_w.h +++ b/src/qtgui/advsearch_w.h @@ -16,7 +16,9 @@ */ #ifndef _ADVSEARCH_W_H_INCLUDED_ #define _ADVSEARCH_W_H_INCLUDED_ -#include + +#include + #include #include @@ -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 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); private: virtual void init(); - std::list m_clauseWins; + std::vector m_clauseWins; QStringList m_ignTypes; bool m_ignByCats; void saveCnf(); diff --git a/src/qtgui/advshist.cpp b/src/qtgui/advshist.cpp new file mode 100644 index 00000000..2b95305b --- /dev/null +++ b/src/qtgui/advshist.cpp @@ -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 + +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 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(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 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 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 >::iterator it = m_entries.begin(); + it != m_entries.end(); it++) { + it->release(); + } +} + +RefCntr AdvSearchHist::getolder() +{ + m_current++; + if (m_current >= int(m_entries.size())) { + m_current--; + return RefCntr(); + } + return m_entries[m_current]; +} + +RefCntr AdvSearchHist::getnewer() +{ + if (m_current == -1 || m_current == 0 || m_entries.empty()) + return RefCntr(); + return m_entries[--m_current]; +} + +bool AdvSearchHist::push(RefCntr 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 lxml = g_dynconf->getStringList(advSearchHistSk); + + for (list::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); +} diff --git a/src/qtgui/advshist.h b/src/qtgui/advshist.h new file mode 100644 index 00000000..60e5c44e --- /dev/null +++ b/src/qtgui/advshist.h @@ -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 + +#include "recoll.h" +#include "refcntr.h" +#include "searchdata.h" + +class AdvSearchHist { +public: + AdvSearchHist(); + ~AdvSearchHist(); + RefCntr getolder(); + RefCntr getnewer(); + bool push(RefCntr); + void clear(); + +private: + bool read(); + + int m_current; + std::vector > m_entries; +}; + + +#endif // _ADVSHIST_H_INCLUDED_ diff --git a/src/qtgui/guiutils.cpp b/src/qtgui/guiutils.cpp index e40fbd3c..ccf5af06 100644 --- a/src/qtgui/guiutils.cpp +++ b/src/qtgui/guiutils.cpp @@ -25,12 +25,14 @@ #include "guiutils.h" #include "pathut.h" #include "base64.h" +#include "advshist.h" #include #include #include 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 diff --git a/src/qtgui/guiutils.h b/src/qtgui/guiutils.h index 236419cc..d840e4f5 100644 --- a/src/qtgui/guiutils.h +++ b/src/qtgui/guiutils.h @@ -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; diff --git a/src/qtgui/main.cpp b/src/qtgui/main.cpp index 2e358ca9..88252020 100644 --- a/src/qtgui/main.cpp +++ b/src/qtgui/main.cpp @@ -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); diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index 93c9d772..95e0bdc7 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -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 diff --git a/src/qtgui/recoll.h b/src/qtgui/recoll.h index 175ea3b5..5e882ab7 100644 --- a/src/qtgui/recoll.h +++ b/src/qtgui/recoll.h @@ -16,6 +16,7 @@ */ #ifndef _RECOLL_H_INCLUDED_ #define _RECOLL_H_INCLUDED_ + #include #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_ */ diff --git a/src/qtgui/recoll.pro.in b/src/qtgui/recoll.pro.in index 1c750a7a..7a6b5ce4 100644 --- a/src/qtgui/recoll.pro.in +++ b/src/qtgui/recoll.pro.in @@ -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 \ diff --git a/src/qtgui/searchclause_w.cpp b/src/qtgui/searchclause_w.cpp index 1563a173..4fb90a44 100644 --- a/src/qtgui/searchclause_w.cpp +++ b/src/qtgui/searchclause_w.cpp @@ -17,6 +17,7 @@ #include "autoconfig.h" #include "recoll.h" +#include "debuglog.h" #include "searchclause_w.h" @@ -28,6 +29,8 @@ #include #include +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(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) diff --git a/src/qtgui/searchclause_w.h b/src/qtgui/searchclause_w.h index 47fae634..2878d18b 100644 --- a/src/qtgui/searchclause_w.h +++ b/src/qtgui/searchclause_w.h @@ -37,6 +37,8 @@ public: SearchClauseW(QWidget* parent = 0); ~SearchClauseW(); Rcl::SearchDataClause *getClause(); + void setFromClause(Rcl::SearchDataClauseSimple *cl); + void clear(); QComboBox* sTpCMB; QComboBox* fldCMB; diff --git a/src/query/dynconf.cpp b/src/query/dynconf.cpp index 31a28100..19ea556d 100644 --- a/src/query/dynconf.cpp +++ b/src/query/dynconf.cpp @@ -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 names = m_data.getNames(sk); @@ -98,37 +93,21 @@ bool RclDynConf::eraseAll(const string &sk) vector names = m_data.getNames(sk); vector::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(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 RclDynConf::getStringList(const string sk) { list el = getList(sk); diff --git a/src/query/dynconf.h b/src/query/dynconf.h index 4f2fbbac..8ac4dd4d 100644 --- a/src/query/dynconf.h +++ b/src/query/dynconf.h @@ -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 @@ -41,67 +44,95 @@ #include #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(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 list getList(const string& sk); + template std::list 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 getStringList(const string sk); + bool enterString(const std::string sk, const std::string value, int maxlen = -1); + std::list getStringList(const std::string sk); private: unsigned int m_mlen; ConfSimple m_data; - }; -template list RclDynConf::getList(const string &sk) +template std::list RclDynConf::getList(const std::string &sk) { - list mlist; + std::list mlist; Tp entry; - vector names = m_data.getNames(sk); - for (vector::const_iterator it = names.begin(); + std::vector names = m_data.getNames(sk); + for (std::vector::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 list 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_ */ diff --git a/src/rcldb/searchdata.cpp b/src/rcldb/searchdata.cpp index 03a13d28..0aa99ce2 100644 --- a/src/rcldb/searchdata.cpp +++ b/src/rcldb/searchdata.cpp @@ -25,6 +25,7 @@ #include #include #include +#include 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 << "" << endl; + + // Clause list + os << "" << endl; + if (m_tp != SCLT_AND) + os << "" << tpToString(m_tp) << "" << 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(c); + os << "" << endl; + if (cl->getTp() != SCLT_AND) { + os << "" << tpToString(cl->getTp()) << "" << endl; + } + if (cl->getTp() != SCLT_FILENAME && !cl->getfield().empty()) { + os << "" << base64_encode(cl->getfield()) << "" << endl; + } + os << "" << base64_encode(cl->gettext()) << "" << endl; + if (cl->getTp() == SCLT_NEAR || cl->getTp() == SCLT_PHRASE) { + SearchDataClauseDist *cld = + dynamic_cast(cl); + os << "" << cld->getslack() << "" << endl; + } + os << "" << endl; + } + os << "" << endl; + + if (m_haveDates) { + if (m_dates.y1 > 0) { + os << "" << + "" << m_dates.d1 << "" << + "" << m_dates.m1 << "" << + "" << m_dates.y1 << "" + << "" << endl; + } + if (m_dates.y2 > 0) { + os << "" << + "" << m_dates.d2 << "" << + "" << m_dates.m2 << "" << + "" << m_dates.y2 << "" + << "" << endl; + } + } + + + if (m_minSize != size_t(-1)) { + os << "" << m_minSize << "" << endl; + } + if (m_maxSize != size_t(-1)) { + os << "" << m_maxSize << "" << endl; + } + + if (!m_filetypes.empty()) { + os << ""; + for (vector::iterator it = m_filetypes.begin(); + it != m_filetypes.end(); it++) { + os << *it << " "; + } + os << "" << endl; + } + + if (!m_nfiletypes.empty()) { + os << ""; + for (vector::iterator it = m_nfiletypes.begin(); + it != m_nfiletypes.end(); it++) { + os << *it << " "; + } + os << "" << endl; + } + + for (vector::const_iterator dit = m_dirspecs.begin(); + dit != m_dirspecs.end(); dit++) { + if (dit->exclude) { + os << "" << base64_encode(dit->dir) << "" << endl; + } else { + os << "" << base64_encode(dit->dir) << "" << endl; + } + } + os << ""; + 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())); diff --git a/src/rcldb/searchdata.h b/src/rcldb/searchdata.h index a62d8691..ece579d0 100644 --- a/src/rcldb/searchdata.h +++ b/src/rcldb/searchdata.h @@ -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; }; diff --git a/src/utils/base64.cpp b/src/utils/base64.cpp index 61b98841..29c6e902 100644 --- a/src/utils/base64.cpp +++ b/src/utils/base64.cpp @@ -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; diff --git a/src/utils/base64.h b/src/utils/base64.h index d7acd306..86a5bc1e 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -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_ */