diff --git a/src/qtgui/guiutils.cpp b/src/qtgui/guiutils.cpp index 10e42422..fd868801 100644 --- a/src/qtgui/guiutils.cpp +++ b/src/qtgui/guiutils.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: guiutils.cpp,v 1.13 2006-09-08 09:02:47 dockes Exp $ (C) 2005 Jean-Francois Dockes"; +static char rcsid[] = "@(#$Id: guiutils.cpp,v 1.14 2006-09-11 09:08:44 dockes Exp $ (C) 2005 Jean-Francois Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -162,32 +162,26 @@ void rwSettings(bool writing) // known dbs and active (searched) ones. // When starting up, we also add from the RECOLL_EXTRA_DBS environment // variable. - QStringList qsl; + // This are stored inside the dynamic configuration file (aka: history), + // as they are likely to depend on RECOLL_CONFDIR. + const string allEdbsSk = "allExtDbs"; + const string actEdbsSk = "actExtDbs"; if (writing) { + LOGDEB(("settings: writing to dynconf\n")); + g_dynconf->eraseAll(allEdbsSk); for (list::const_iterator it = prefs.allExtraDbs.begin(); it != prefs.allExtraDbs.end(); it++) { - string b64; - base64_encode(*it, b64); - qsl.push_back(QString::fromAscii(b64.c_str())); + g_dynconf->enterString(allEdbsSk, *it); } - settings.writeEntry("/Recoll/prefs/query/allExtraDbs", qsl); - qsl.clear(); + g_dynconf->eraseAll(actEdbsSk); for (list::const_iterator it = prefs.activeExtraDbs.begin(); it != prefs.activeExtraDbs.end(); it++) { - string b64; - base64_encode(*it, b64); - qsl.push_back(QString::fromAscii(b64.c_str())); + g_dynconf->enterString(actEdbsSk, *it); + } - settings.writeEntry("/Recoll/prefs/query/activeExtraDbs", qsl); } else { - qsl = settings.readListEntry("/Recoll/prefs/query/allExtraDbs"); - prefs.allExtraDbs.clear(); - for (QStringList::iterator it = qsl.begin(); it != qsl.end(); it++) { - string dec; - base64_decode((*it).ascii(), dec); - prefs.allExtraDbs.push_back(dec); - } + prefs.allExtraDbs = g_dynconf->getStringList(allEdbsSk); const char *cp; if ((cp = getenv("RECOLL_EXTRA_DBS")) != 0) { list dbl; @@ -207,13 +201,7 @@ void rwSettings(bool writing) prefs.allExtraDbs.push_back(dbdir); } } - qsl = settings.readListEntry("/Recoll/prefs/query/activeExtraDbs"); - prefs.activeExtraDbs.clear(); - for (QStringList::iterator it = qsl.begin(); it != qsl.end(); it++) { - string dec; - base64_decode((*it).ascii(), dec); - prefs.activeExtraDbs.push_back(dec); - } + prefs.activeExtraDbs = g_dynconf->getStringList(actEdbsSk); } #if 0 diff --git a/src/qtgui/main.cpp b/src/qtgui/main.cpp index 523fcca5..86345d94 100644 --- a/src/qtgui/main.cpp +++ b/src/qtgui/main.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: main.cpp,v 1.46 2006-09-08 09:02:47 dockes Exp $ (C) 2005 J.F.Dockes"; +static char rcsid[] = "@(#$Id: main.cpp,v 1.47 2006-09-11 09:08:44 dockes Exp $ (C) 2005 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -68,6 +68,8 @@ const string recoll_datadir = RECOLL_DATADIR; RclConfig *rclconfig; Rcl::Db *rcldb; + +RclHistory *g_dynconf; int recollNeedsExit; static string dbdir; static RclMain *mainWindow; @@ -194,7 +196,6 @@ int main(int argc, char **argv) translatdir.c_str() ); app.installTranslator( &translator ); - rwSettings(false); string reason; rclconfig = recollinit(recollCleanup, sigcleanup, reason, &a_config); @@ -205,6 +206,17 @@ int main(int argc, char **argv) exit(1); } + string historyfile = path_cat(rclconfig->getConfDir(), "history"); + g_dynconf = new RclHistory(historyfile); + if (!g_dynconf || !g_dynconf->ok()) { + QString msg = app.translate("Main", "Configuration problem (dynconf"); + QMessageBox::critical(0, "Recoll", msg); + exit(1); + } + + rwSettings(false); + + // Create main window and set its size to previous session's #ifdef WITH_KDE #if 0 diff --git a/src/qtgui/rclmain.cpp b/src/qtgui/rclmain.cpp index aaa64991..d6d64771 100644 --- a/src/qtgui/rclmain.cpp +++ b/src/qtgui/rclmain.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: rclmain.cpp,v 1.28 2006-09-04 15:13:01 dockes Exp $ (C) 2005 J.F.Dockes"; +static char rcsid[] = "@(#$Id: rclmain.cpp,v 1.29 2006-09-11 09:08:44 dockes Exp $ (C) 2005 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -91,8 +91,6 @@ void RclMain::init() QFont nfont(prefs.reslistfontfamily, prefs.reslistfontsize); resList->setFont(nfont); } - string historyfile = path_cat(rclconfig->getConfDir(), "history"); - m_history = new RclDHistory(historyfile); connect(sSearch, SIGNAL(startSearch(Rcl::AdvSearchData)), this, SLOT(startAdvSearch(Rcl::AdvSearchData))); @@ -352,7 +350,6 @@ void RclMain::showAdvSearchDialog() asearchform = new AdvSearch(0, tr("Advanced search"), FALSE, WStyle_Customize | WStyle_NormalBorder | WStyle_Title | WStyle_SysMenu); - asearchform->setSizeGripEnabled(FALSE); connect(asearchform, SIGNAL(startSearch(Rcl::AdvSearchData)), this, SLOT(startAdvSearch(Rcl::AdvSearchData))); asearchform->show(); @@ -369,7 +366,6 @@ void RclMain::showSortDialog() sortform = new SortForm(0, tr("Sort criteria"), FALSE, WStyle_Customize | WStyle_NormalBorder | WStyle_Title | WStyle_SysMenu); - sortform->setSizeGripEnabled(FALSE); connect(sortform, SIGNAL(sortDataChanged(RclSortSpec)), this, SLOT(sortDataChanged(RclSortSpec))); sortform->show(); @@ -387,7 +383,6 @@ void RclMain::showUIPrefs() uiprefs = new UIPrefsDialog(0, tr("User interface preferences"), FALSE, WStyle_Customize | WStyle_NormalBorder | WStyle_Title | WStyle_SysMenu); - uiprefs->setSizeGripEnabled(FALSE); connect(uiprefs, SIGNAL(uiprefsDone()), this, SLOT(setUIPrefs())); uiprefs->show(); } else { @@ -458,7 +453,7 @@ void RclMain::startPreview(int docnum) } (void)curPreview->addEditorTab(); } - m_history->enterDocument(fn, doc.ipath); + g_dynconf->enterDoc(fn, doc.ipath); if (!curPreview->loadFileInCurrentTab(fn, st.st_size, doc)) curPreview->closeCurrentTab(); } @@ -544,7 +539,7 @@ void RclMain::startNativeViewer(int docnum) QString::fromUtf8(prcmd.c_str()) + "]"; stb->message(msg, 5000); } - m_history->enterDocument(fn, doc.ipath); + g_dynconf->enterDoc(fn, doc.ipath); system(ncmd.c_str()); } @@ -601,11 +596,12 @@ void RclMain::showDocHistory() DocSequence *docsource; if (sortspecs.sortwidth > 0) { - DocSequenceHistory myseq(rcldb, m_history, string(tr("Document history").utf8())); + DocSequenceHistory myseq(rcldb, g_dynconf, + string(tr("Document history").utf8())); docsource = new DocSeqSorted(myseq, sortspecs, string(tr("Document history (sorted)").utf8())); } else { - docsource = new DocSequenceHistory(rcldb, m_history, + docsource = new DocSequenceHistory(rcldb, g_dynconf, string(tr("Document history").utf8())); } Rcl::AdvSearchData sdata; diff --git a/src/qtgui/rclmain.h b/src/qtgui/rclmain.h index cf6182d5..db87e8bf 100644 --- a/src/qtgui/rclmain.h +++ b/src/qtgui/rclmain.h @@ -71,7 +71,6 @@ private: SortForm *sortform; UIPrefsDialog *uiprefs; RclSortSpec sortspecs; - RclDHistory *m_history; virtual void init(); }; diff --git a/src/qtgui/recoll.h b/src/qtgui/recoll.h index a1538383..10ffd7e0 100644 --- a/src/qtgui/recoll.h +++ b/src/qtgui/recoll.h @@ -16,12 +16,13 @@ */ #ifndef _RECOLL_H_INCLUDED_ #define _RECOLL_H_INCLUDED_ -/* @(#$Id: recoll.h,v 1.15 2006-01-31 11:39:12 dockes Exp $ (C) 2004 J.F.Dockes */ +/* @(#$Id: recoll.h,v 1.16 2006-09-11 09:08:44 dockes Exp $ (C) 2004 J.F.Dockes */ #include #include "rclconfig.h" #include "rcldb.h" #include "idxthread.h" +#include "history.h" // Misc declarations in need of sharing between the UI files @@ -32,5 +33,6 @@ extern RclConfig *rclconfig; extern Rcl::Db *rcldb; extern int recollNeedsExit; extern const std::string recoll_datadir; +extern RclHistory *g_dynconf; #endif /* _RECOLL_H_INCLUDED_ */ diff --git a/src/qtgui/uiprefs.ui b/src/qtgui/uiprefs.ui index 968d8ef4..35b34b63 100644 --- a/src/qtgui/uiprefs.ui +++ b/src/qtgui/uiprefs.ui @@ -276,7 +276,7 @@ May be slow for big documents. ExtraDb - Extra Databases + External Databases @@ -352,10 +352,10 @@ May be slow for big documents. textLabel2_2 - All extra databases + All databases - All known extra databases + Databases currently not used @@ -456,10 +456,10 @@ May be slow for big documents. textLabel3 - Active extra databases + Active databases - Extra databases that will be searched in addition to the main one + Databases that will be searched in addition to the main one diff --git a/src/query/docseq.h b/src/query/docseq.h index 35fdd252..3d55872f 100644 --- a/src/query/docseq.h +++ b/src/query/docseq.h @@ -16,7 +16,7 @@ */ #ifndef _DOCSEQ_H_INCLUDED_ #define _DOCSEQ_H_INCLUDED_ -/* @(#$Id: docseq.h,v 1.7 2006-02-21 12:52:48 dockes Exp $ (C) 2004 J.F.Dockes */ +/* @(#$Id: docseq.h,v 1.8 2006-09-11 09:08:44 dockes Exp $ (C) 2004 J.F.Dockes */ #include "rcldb.h" #include "history.h" @@ -71,7 +71,7 @@ class DocSequenceDb : public DocSequence { /** A DocSequence coming from the history file */ class DocSequenceHistory : public DocSequence { public: - DocSequenceHistory(Rcl::Db *d, RclDHistory *h, const std::string &t) + DocSequenceHistory(Rcl::Db *d, RclHistory *h, const std::string &t) : DocSequence(t), m_db(d), m_hist(h), m_prevnum(-1), m_prevtime(-1) {} virtual ~DocSequenceHistory() {} @@ -79,7 +79,7 @@ class DocSequenceHistory : public DocSequence { virtual int getResCnt(); private: Rcl::Db *m_db; - RclDHistory *m_hist; + RclHistory *m_hist; int m_prevnum; long m_prevtime; diff --git a/src/query/history.cpp b/src/query/history.cpp index 89b42df1..e51ec8b3 100644 --- a/src/query/history.cpp +++ b/src/query/history.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: history.cpp,v 1.5 2006-01-23 13:32:28 dockes Exp $ (C) 2005 J.F.Dockes"; +static char rcsid[] = "@(#$Id: history.cpp,v 1.6 2006-09-11 09:08:44 dockes Exp $ (C) 2005 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -33,66 +33,83 @@ using namespace std; static const char *docSubkey = "docs"; -RclDHistory::RclDHistory(const string &fn, unsigned int mxs) - : m_mlen(mxs), m_data(fn.c_str()) +// Encode/decode document history entry: Unix time + base64 of fn + +// base64 of ipath separated by a space. If ipath is not set, there +// are only 2 parts +bool RclDHistoryEntry::encode(string& value) { + char chartime[20]; + sprintf(chartime, "%ld", unixtime); + string bfn, bipath; + base64_encode(fn, bfn); + base64_encode(ipath, bipath); + value = string(chartime) + " " + bfn + " " + bipath; + return true; } - -bool RclDHistory::decodeValue(const string &value, RclDHistoryEntry *e) +bool RclDHistoryEntry::decode(const string &value) { list vall; stringToStrings(value, vall); list::const_iterator it1 = vall.begin(); if (vall.size() < 2) return false; - e->unixtime = atol((*it1++).c_str()); - base64_decode(*it1++, e->fn); + unixtime = atol((*it1++).c_str()); + base64_decode(*it1++, fn); if (vall.size() == 3) - base64_decode(*it1, e->ipath); + base64_decode(*it1, ipath); else - e->ipath.erase(); + ipath.erase(); return true; } - -bool RclDHistory::enterDocument(const string fn, const string ipath) +bool RclDHistoryEntry::equal(const HistoryEntry& other) { - LOGDEB(("RclDHistory::enterDocument: [%s] [%s] into %s\n", - fn.c_str(), ipath.c_str(), m_data.getFilename().c_str())); - // Encode value part: Unix time + base64 of fn + base64 of ipath - // separated by a space. If ipath is not set, there are only 2 parts - char chartime[20]; - time_t now = time(0); - sprintf(chartime, "%ld", (long)now); - string bfn, bipath; - base64_encode(fn, bfn); - base64_encode(ipath, bipath); - string value = string(chartime) + " " + bfn + " " + bipath; + const RclDHistoryEntry& e = dynamic_cast(other); + return e.fn == fn && e.ipath == ipath; +} - LOGDEB1(("Encoded value [%s] (%d)\n", value.c_str(), value.size())); +// 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 HistoryEntry& other) +{ + const RclSListEntry& e = dynamic_cast(other); + return e.value == value; +} + +bool RclHistory::insertNew(const string &sk, HistoryEntry &n, HistoryEntry &s) +{ // Is this doc already in history ? If it is we remove the old entry - list names = m_data.getNames(docSubkey); + list names = m_data.getNames(sk); list::const_iterator it; bool changed = false; for (it = names.begin(); it != names.end(); it++) { string oval; - if (!m_data.get(*it, oval, docSubkey)) { + if (!m_data.get(*it, oval, sk)) { LOGDEB(("No data for %s\n", (*it).c_str())); continue; } - RclDHistoryEntry entry; - decodeValue(oval, &entry); + s.decode(oval); - if (entry.fn == fn && entry.ipath == ipath) { + if (s.equal(n)) { LOGDEB(("Erasing old entry\n")); - m_data.erase(*it, docSubkey); + m_data.erase(*it, sk); changed = true; } } // Maybe reget list if (changed) - names = m_data.getNames(docSubkey); + names = m_data.getNames(sk); // How many do we have if (names.size() >= m_mlen) { @@ -101,7 +118,7 @@ bool RclDHistory::enterDocument(const string fn, const string ipath) // history is 4 billion entries old it = names.begin(); for (unsigned int i = 0; i < names.size() - m_mlen + 1; i++, it++) { - m_data.erase(*it, docSubkey); + m_data.erase(*it, sk); } } @@ -112,33 +129,65 @@ bool RclDHistory::enterDocument(const string fn, const string ipath) char nname[20]; sprintf(nname, "%010u", hi); - if (!m_data.set(string(nname), value, docSubkey)) { - LOGERR(("RclDHistory::enterDocument: set failed\n")); + string value; + n.encode(value); + LOGDEB1(("Encoded value [%s] (%d)\n", value.c_str(), value.size())); + if (!m_data.set(string(nname), value, sk)) { + LOGERR(("RclDHistory::insertNew: set failed\n")); return false; } return true; + +} + +bool RclHistory::eraseAll(const string &sk) +{ + // Is this doc already in history ? If it is we remove the old entry + list names = m_data.getNames(sk); + list::const_iterator it; + for (it = names.begin(); it != names.end(); it++) { + m_data.erase(*it, sk); + } + return true; } -list RclDHistory::getDocHistory() + +bool RclHistory::enterString(const string sk, const string value) { - list mlist; - RclDHistoryEntry entry; - list names = m_data.getNames(docSubkey); - for (list::const_iterator it = names.begin(); - it != names.end(); it++) { - string value; - if (m_data.get(*it, value, docSubkey)) { - if (!decodeValue(value, &entry)) - continue; - mlist.push_front(entry); - } - } - return mlist; + RclSListEntry ne(value); + RclSListEntry scratch; + return insertNew(sk, ne, scratch); } +list RclHistory::getStringList(const string sk) +{ + list el = getHistory(sk); + list sl; + for (list::const_iterator it = el.begin(); + it != el.end(); it++) + sl.push_back(it->value); + return sl; +} + +/// *************** History entries specific methods +bool RclHistory::enterDoc(const string fn, const string ipath) +{ + LOGDEB(("RclDHistory::enterDoc: [%s] [%s] into %s\n", + fn.c_str(), ipath.c_str(), m_data.getFilename().c_str())); + RclDHistoryEntry ne(time(0), fn, ipath); + RclDHistoryEntry scratch; + return insertNew(docSubkey, ne, scratch); +} + +list RclHistory::getDocHistory() +{ + return getHistory(docSubkey); +} + #else #include +#include #include "history.h" #include "debuglog.h" @@ -147,22 +196,71 @@ list RclDHistory::getDocHistory() using namespace std; #endif +static string thisprog; + +static string usage = + " [-e] [-s ]" + " \n\n" + ; + +static void +Usage(void) +{ + cerr << thisprog << ": usage:\n" << usage; + exit(1); +} + +static int op_flags; +#define OPT_e 0x2 +#define OPT_s 0x4 + int main(int argc, char **argv) { - RclDHistory hist("toto", 5); + string sk = "docs"; + + thisprog = argv[0]; + argc--; argv++; + + while (argc > 0 && **argv == '-') { + (*argv)++; + if (!(**argv)) + /* Cas du "adb - core" */ + Usage(); + while (**argv) + switch (*(*argv)++) { + case 's': op_flags |= OPT_s; if (argc < 2) Usage(); + sk = *(++argv); + argc--; + goto b1; + case 'e': op_flags |= OPT_e; break; + default: Usage(); break; + } + b1: argc--; argv++; + } + if (argc != 0) + Usage(); + + RclHistory hist("toto", 5); DebugLog::getdbl()->setloglevel(DEBDEB1); DebugLog::setfilename("stderr"); - for (int i = 0; i < 10; i++) { - char docname[100]; - sprintf(docname, "A very long document document name is very long indeed and this is the end of it here and exactly here:\n%d", i); - hist.enterDocument(string(docname), "ipathx"); - } + if (op_flags & OPT_e) { + hist.eraseAll(sk); + } else { + for (int i = 0; i < 10; i++) { + char docname[100]; + sprintf(docname, "A very long document document name" + "is very long indeed and this is the end of " + "it here and exactly here:\n%d", i); + hist.enterDoc(string(docname), "ipathx"); + } - list< pair > hlist = hist.getDocHistory(); - for (list< pair >::const_iterator it = hlist.begin(); - it != hlist.end(); it++) { - printf("[%s] [%s]\n", it->first.c_str(), it->second.c_str()); + list hlist = hist.getDocHistory(); + for (list::const_iterator it = hlist.begin(); + it != hlist.end(); it++) { + printf("[%ld] [%s] [%s]\n", it->unixtime, + it->fn.c_str(), it->ipath.c_str()); + } } } diff --git a/src/query/history.h b/src/query/history.h index bcf59d1b..47514552 100644 --- a/src/query/history.h +++ b/src/query/history.h @@ -16,7 +16,23 @@ */ #ifndef _HISTORY_H_INCLUDED_ #define _HISTORY_H_INCLUDED_ -/* @(#$Id: history.h,v 1.3 2006-01-30 11:15:28 dockes Exp $ (C) 2004 J.F.Dockes */ +/* @(#$Id: history.h,v 1.4 2006-09-11 09:08:44 dockes Exp $ (C) 2004 J.F.Dockes */ + +/** + * Dynamic configuration storage + * + * The term "history" is a misnomer as this code is now used to save + * all dynamic parameters except those that are stored in the global + * recollrc QT preferences file. Examples: + * - History of documents selected for preview + * - Active and inactive external databases (these should depend on the + * configuration directory and cant be stored in recollrc). + * - ... + * + * The storage is performed in a ConSimple file, with subkeys and + * encodings which depend on the data stored. + * + */ #include #include @@ -24,32 +40,90 @@ #include "conftree.h" -/** Holder for data returned when querying history */ -class RclDHistoryEntry { +#ifndef NO_NAMESPACES +using namespace std; +#endif + +class HistoryEntry { + public: + virtual bool decode(const string &value) = 0; + virtual bool encode(string& value) = 0; + virtual bool equal(const HistoryEntry &other) = 0; +}; + + +/** Document history entry */ +class RclDHistoryEntry : public HistoryEntry { public: RclDHistoryEntry() : unixtime(0) {} + RclDHistoryEntry(long t, const string& f, const string& i) + : unixtime(t), fn(f), ipath(i) {} + virtual ~RclDHistoryEntry() {} + virtual bool decode(const string &value); + virtual bool encode(string& value); + virtual bool equal(const HistoryEntry& other); long unixtime; string fn; string ipath; }; -/** - * The documents history class. This is based on a ConfTree for no - * imperative reason - */ -class RclDHistory { + +/** String storage generic object */ +class RclSListEntry : public HistoryEntry { public: - RclDHistory(const std::string &fn, unsigned int maxsize=1000); - bool ok() {return m_data.getStatus() == ConfSimple::STATUS_RW;} + RclSListEntry() {} + RclSListEntry(const string& v) : value(v) {} + virtual ~RclSListEntry() {} + virtual bool decode(const string &enc); + virtual bool encode(string& enc); + virtual bool equal(const HistoryEntry& other); - bool enterDocument(const std::string fn, const std::string ipath); - std::list getDocHistory(); - - private: - bool decodeValue(const string &value, RclDHistoryEntry *e); - unsigned int m_mlen; - ConfSimple m_data; + string value; }; +/** + * The history class. This uses a ConfSimple for storage, and should be + * renamed something like dynconf, as it is used to stored quite a few + * things beyond doc history: all dynamic configuration parameters that are + * not suitable for QT settings because they are specific to a RECOLL_CONFDIR + */ +class RclHistory { + public: + RclHistory(const string &fn, unsigned int maxsize=100) + : m_mlen(maxsize), m_data(fn.c_str()) {} + bool ok() {return m_data.getStatus() == ConfSimple::STATUS_RW;} + + // Specific methods for history entries + bool enterDoc(const string fn, const string ipath); + list getDocHistory(); + + // Generic methods used for string lists, designated by the subkey value + bool enterString(const string sk, const string value); + list getStringList(const string sk); + bool eraseAll(const string& sk); + + private: + unsigned int m_mlen; + ConfSimple m_data; + bool insertNew(const string& sk, HistoryEntry &n, HistoryEntry &s); + template list getHistory(const string& sk); +}; + +template list RclHistory::getHistory(const string &sk) +{ + list mlist; + Tp entry; + list names = m_data.getNames(sk); + for (list::const_iterator it = names.begin(); + it != names.end(); it++) { + string value; + if (m_data.get(*it, value, sk)) { + if (!entry.decode(value)) + continue; + mlist.push_front(entry); + } + } + return mlist; +} #endif /* _HISTORY_H_INCLUDED_ */