From c2dab20d7ef8a2c583e7f44ba5e8664ed78cf7ca Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Thu, 20 Sep 2012 13:51:40 +0200 Subject: [PATCH] preliminary implementation for the snippets "open to page" popup window --- src/qtgui/preview_w.h | 1 + src/qtgui/rclmain_w.cpp | 14 +++-- src/qtgui/rclmain_w.h | 2 +- src/qtgui/recoll.pro.in | 3 ++ src/qtgui/reslist.cpp | 19 ++++++- src/qtgui/reslist.h | 8 ++- src/qtgui/snippets.ui | 67 ++++++++++++++++++++++++ src/qtgui/snippets_w.cpp | 109 +++++++++++++++++++++++++++++++++++++++ src/qtgui/snippets_w.h | 50 ++++++++++++++++++ src/query/docseq.h | 15 ++++++ src/query/docseqdb.cpp | 13 +++++ src/query/docseqdb.h | 1 + src/rcldb/rcldb.cpp | 74 ++++++++++++++++---------- src/rcldb/rcldb.h | 5 ++ src/rcldb/rcldb_p.h | 3 +- 15 files changed, 349 insertions(+), 35 deletions(-) create mode 100644 src/qtgui/snippets.ui create mode 100644 src/qtgui/snippets_w.cpp create mode 100644 src/qtgui/snippets_w.h diff --git a/src/qtgui/preview_w.h b/src/qtgui/preview_w.h index 7bce1eb1..878d82f1 100644 --- a/src/qtgui/preview_w.h +++ b/src/qtgui/preview_w.h @@ -25,6 +25,7 @@ #include +#include #include #include diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index 7f021061..5013e145 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -301,6 +301,7 @@ void RclMain::init() connect(restable, SIGNAL(docSaveToFileClicked(Rcl::Doc)), this, SLOT(saveDocToFile(Rcl::Doc))); + reslist->setRclMain(this); connect(this, SIGNAL(docSourceChanged(RefCntr)), reslist, SLOT(setDocSource(RefCntr))); connect(firstPageAction, SIGNAL(activated()), @@ -1501,8 +1502,9 @@ static bool lookForHtmlBrowser(string &exefile) return false; } -void RclMain::startNativeViewer(Rcl::Doc doc) +void RclMain::startNativeViewer(Rcl::Doc doc, int pagenum) { + LOGDEB(("RclMain::startNativeViewer: page %d\n", pagenum)); // Look for appropriate viewer string cmdplusattr; if (prefs.useDesktopOpen) { @@ -1520,11 +1522,13 @@ void RclMain::startNativeViewer(Rcl::Doc doc) return; } - int pagenum = 1; - if (m_source.isNotNull()) - pagenum = m_source->getFirstMatchPage(doc); - if (pagenum == -1) + if (pagenum == -1) { pagenum = 1; + if (m_source.isNotNull()) + pagenum = m_source->getFirstMatchPage(doc); + if (pagenum == -1) + pagenum = 1; + } char cpagenum[20]; sprintf(cpagenum, "%d", pagenum); diff --git a/src/qtgui/rclmain_w.h b/src/qtgui/rclmain_w.h index 0928d209..35b67ce9 100644 --- a/src/qtgui/rclmain_w.h +++ b/src/qtgui/rclmain_w.h @@ -119,7 +119,7 @@ public slots: virtual void docExpand(Rcl::Doc); virtual void startPreview(int docnum, Rcl::Doc doc, int keymods); virtual void startPreview(Rcl::Doc); - virtual void startNativeViewer(Rcl::Doc); + virtual void startNativeViewer(Rcl::Doc, int pagenum = -1); virtual void saveDocToFile(Rcl::Doc); virtual void previewNextInTab(Preview *, int sid, int docnum); virtual void previewPrevInTab(Preview *, int sid, int docnum); diff --git a/src/qtgui/recoll.pro.in b/src/qtgui/recoll.pro.in index 49f735ae..1c750a7a 100644 --- a/src/qtgui/recoll.pro.in +++ b/src/qtgui/recoll.pro.in @@ -25,6 +25,7 @@ HEADERS += \ restable.h \ rtitool.h \ searchclause_w.h \ + snippets_w.h \ spell_w.h \ ssearch_w.h \ uiprefs_w.h \ @@ -46,6 +47,7 @@ SOURCES += \ restable.cpp \ rtitool.cpp \ searchclause_w.cpp \ + snippets_w.cpp \ spell_w.cpp \ ssearch_w.cpp \ uiprefs_w.cpp \ @@ -64,6 +66,7 @@ FORMS = \ restable.ui \ rtitool.ui \ spell.ui \ + snippets.ui \ ssearchb.ui \ uiprefs.ui \ viewaction.ui \ diff --git a/src/qtgui/reslist.cpp b/src/qtgui/reslist.cpp index 491d93cf..dd341eeb 100644 --- a/src/qtgui/reslist.cpp +++ b/src/qtgui/reslist.cpp @@ -50,6 +50,7 @@ #include "refcntr.h" #include "internfile.h" #include "indexer.h" +#include "snippets_w.h" #include "reslist.h" #include "moc_reslist.cpp" @@ -272,7 +273,7 @@ static PlainToRichQtReslist g_hiliter; ///////////////////////////////////// ResList::ResList(QWidget* parent, const char* name) - : RESLIST_PARENTCLASS(parent) + : RESLIST_PARENTCLASS(parent), m_parent(0) { if (!name) setObjectName("resList"); @@ -892,6 +893,8 @@ void ResList::createPopupMenu(const QPoint& pos) this, SLOT(menuPreviewParent())); popup->addAction(tr("&Open Parent document/folder"), this, SLOT(menuOpenParent())); + popup->addAction(tr("Open &Snippets window"), + this, SLOT(menuOpenSnippets())); popup->popup(mapToGlobal(pos)); } @@ -943,6 +946,20 @@ void ResList::menuOpenParent() } } +void ResList::menuOpenSnippets() +{ + Rcl::Doc doc; + if (!getDoc(m_popDoc, doc) || m_source.isNull()) + return; + SnippetsW *sp = new SnippetsW(doc, m_source); + if (m_parent) { + connect(sp, SIGNAL(startNativeViewer(Rcl::Doc, int)), + m_parent, SLOT(startNativeViewer(Rcl::Doc, int))); + } + + sp->show(); +} + void ResList::menuEdit() { Rcl::Doc doc; diff --git a/src/qtgui/reslist.h b/src/qtgui/reslist.h index 78c8d7e0..8c47cb8f 100644 --- a/src/qtgui/reslist.h +++ b/src/qtgui/reslist.h @@ -41,6 +41,7 @@ using std::pair; #include "rcldoc.h" #include "reslistpager.h" +class RclMain; class QtGuiResListPager; /** @@ -66,7 +67,10 @@ class ResList : public RESLIST_PARENTCLASS int listId() const {return m_listId;} int pageFirstDocNum(); void setFont(); - + void setRclMain(RclMain *m) + { + m_parent = m; + } public slots: virtual void setDocSource(RefCntr nsource); virtual void resetList(); // Erase current list @@ -84,6 +88,7 @@ class ResList : public RESLIST_PARENTCLASS virtual void menuExpand(); virtual void menuPreviewParent(); virtual void menuOpenParent(); + virtual void menuOpenSnippets(); virtual void previewExposed(int); virtual void append(const QString &text); virtual void readDocSource(); @@ -132,6 +137,7 @@ class ResList : public RESLIST_PARENTCLASS // so we store the page and display it when done. QString m_text; #endif + RclMain *m_parent; virtual void displayPage(); // Display current page static int newListId(); diff --git a/src/qtgui/snippets.ui b/src/qtgui/snippets.ui new file mode 100644 index 00000000..5e3b3892 --- /dev/null +++ b/src/qtgui/snippets.ui @@ -0,0 +1,67 @@ + + + Snippets + + + + 0 + 0 + 516 + 395 + + + + Snippets + + + true + + + + + + + about:blank + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + QWebView + QWidget +
QtWebKit/QWebView
+
+
+ + + + buttonBox + clicked(QAbstractButton*) + Snippets + close() + + + 257 + 369 + + + 257 + 197 + + + + +
diff --git a/src/qtgui/snippets_w.cpp b/src/qtgui/snippets_w.cpp new file mode 100644 index 00000000..917c2041 --- /dev/null +++ b/src/qtgui/snippets_w.cpp @@ -0,0 +1,109 @@ +/* Copyright (C) 2012 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 +#include + +#include +#include +using namespace std; + +#include "debuglog.h" +#include "recoll.h" +#include "snippets_w.h" +#include "guiutils.h" +#include "rcldb.h" +#include "rclhelp.h" +#include "plaintorich.h" + +class PlainToRichQtSnippets : public PlainToRich { +public: + virtual string startMatch(unsigned int) + { + return string(""); + } + virtual string endMatch() + { + return string(""); + } +}; +static PlainToRichQtSnippets g_hiliter; + +void SnippetsW::init() +{ + if (m_source.isNull()) + return; + + vector > vpabs; + m_source->getAbstract(m_doc, vpabs); + + HighlightData hdata; + m_source->getTerms(hdata); + + QString html = QString::fromAscii( + "" + "" + ""); + + g_hiliter.set_inputhtml(false); + + for (vector >::const_iterator it = vpabs.begin(); + it != vpabs.end(); it++) { + if (it->first > 0) { + char buf[100]; + sprintf(buf, "P. %d", it->first); + html += ""; + html += buf; + html += " "; + } + list lr; + g_hiliter.plaintorich(it->second, lr, hdata); + html.append(QString::fromUtf8(lr.front().c_str())); + html.append("
\n"); + } + html.append(""); + webView->setHtml(html); + connect(webView, SIGNAL(linkClicked(const QUrl &)), + this, SLOT(linkWasClicked(const QUrl &))); + webView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); +} + + +void SnippetsW::linkWasClicked(const QUrl &url) +{ + string ascurl = (const char *)url.toString().toAscii();; + LOGDEB(("Snippets::linkWasClicked: [%s]\n", ascurl.c_str())); + + if (ascurl.size() > 3) { + int what = ascurl[0]; + switch (what) { + case 'P': + { + int page = atoi(ascurl.c_str()+2); + emit startNativeViewer(m_doc, page); + return; + } + } + } + LOGERR(("Snippets::linkWasClicked: bad link [%s]\n", ascurl.c_str())); +} + diff --git a/src/qtgui/snippets_w.h b/src/qtgui/snippets_w.h new file mode 100644 index 00000000..27f3b571 --- /dev/null +++ b/src/qtgui/snippets_w.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2012 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 _SNIPPETS_W_H_INCLUDED_ +#define _SNIPPETS_W_H_INCLUDED_ + +#include "rcldoc.h" +#include "refcntr.h" +#include "docseq.h" +#include "rclmain_w.h" + +#include "ui_snippets.h" + +class SnippetsW : public QWidget, public Ui::Snippets +{ + Q_OBJECT +public: + SnippetsW(Rcl::Doc doc, RefCntr source, QWidget* parent = 0) + : QWidget(parent), m_doc(doc), m_source(source) + { + setupUi((QDialog*)this); + init(); + } + +protected slots: + virtual void linkWasClicked(const QUrl &); + +signals: + void startNativeViewer(Rcl::Doc, int pagenum); + +private: + void init(); + Rcl::Doc m_doc; + RefCntr m_source; +}; + +#endif /* _SNIPPETS_W_H_INCLUDED_ */ diff --git a/src/query/docseq.h b/src/query/docseq.h index 4a2fbd78..69169975 100644 --- a/src/query/docseq.h +++ b/src/query/docseq.h @@ -95,6 +95,14 @@ class DocSequence { abs.push_back(doc.meta[Rcl::Doc::keyabs]); return true; } + virtual bool getAbstract(Rcl::Doc& doc, + std::vector >& abs) + { + fprintf(stderr, "DocSequence::getAbstract/pair\n"); + abs.push_back(std::pair(0, + doc.meta[Rcl::Doc::keyabs])); + return true; + } virtual int getFirstMatchPage(Rcl::Doc&) { return -1; @@ -157,6 +165,13 @@ public: return false; return m_seq->getAbstract(doc, abs); } + virtual bool getAbstract(Rcl::Doc& doc, + std::vector >& abs) + { + if (m_seq.isNull()) + return false; + return m_seq->getAbstract(doc, abs); + } virtual std::string getDescription() { if (m_seq.isNull()) diff --git a/src/query/docseqdb.cpp b/src/query/docseqdb.cpp index 36baa7dd..a1e1762d 100644 --- a/src/query/docseqdb.cpp +++ b/src/query/docseqdb.cpp @@ -65,6 +65,19 @@ int DocSequenceDb::getResCnt() return m_rescnt; } +bool DocSequenceDb::getAbstract(Rcl::Doc &doc, + vector >& vpabs) +{ + LOGDEB(("DocSequenceDb::getAbstract/pair\n")); + setQuery(); + if (m_q->whatDb() && + m_queryBuildAbstract && (doc.syntabs || m_queryReplaceAbstract)) { + m_q->whatDb()->makeDocAbstract(doc, m_q.getptr(), vpabs); + } + if (vpabs.empty()) + vpabs.push_back(pair(0, doc.meta[Rcl::Doc::keyabs])); + return true; +} bool DocSequenceDb::getAbstract(Rcl::Doc &doc, vector& vabs) { setQuery(); diff --git a/src/query/docseqdb.h b/src/query/docseqdb.h index f2f2b615..e4d1ad67 100644 --- a/src/query/docseqdb.h +++ b/src/query/docseqdb.h @@ -31,6 +31,7 @@ class DocSequenceDb : public DocSequence { virtual bool getDoc(int num, Rcl::Doc &doc, string * = 0); virtual int getResCnt(); virtual void getTerms(HighlightData& hld); + virtual bool getAbstract(Rcl::Doc &doc, vector >&); virtual bool getAbstract(Rcl::Doc &doc, vector&); virtual int getFirstMatchPage(Rcl::Doc&); virtual bool getEnclosing(Rcl::Doc& doc, Rcl::Doc& pdoc); diff --git a/src/rcldb/rcldb.cpp b/src/rcldb/rcldb.cpp index a93bbed7..f26ae7ae 100644 --- a/src/rcldb/rcldb.cpp +++ b/src/rcldb/rcldb.cpp @@ -415,7 +415,8 @@ int Db::Native::getFirstMatchPage(Xapian::docid docid, Query *query) // // DatabaseModified and other general exceptions are catched and // possibly retried by our caller -vector Db::Native::makeAbstract(Xapian::docid docid, Query *query) +bool Db::Native::makeAbstract(Xapian::docid docid, Query *query, + vector >& vabs) { Chrono chron; LOGDEB2(("makeAbstract:%d: maxlen %d wWidth %d\n", chron.ms(), @@ -429,7 +430,7 @@ vector Db::Native::makeAbstract(Xapian::docid docid, Query *query) noPrefixList(iterms, matchedTerms); if (matchedTerms.empty()) { LOGDEB(("makeAbstract::Empty term list\n")); - return vector(); + return false; } } listList("Match terms: ", matchedTerms); @@ -452,7 +453,7 @@ vector Db::Native::makeAbstract(Xapian::docid docid, Query *query) // This can't happen, but would crash us if (totalweight == 0.0) { LOGERR(("makeAbstract: totalweight == 0.0 !\n")); - return vector(); + return false; } /////////////////// @@ -466,7 +467,7 @@ vector Db::Native::makeAbstract(Xapian::docid docid, Query *query) map sparseDoc; // Total number of occurences for all terms. We stop when we have too much - int totaloccs = 0; + unsigned int totaloccs = 0; // Limit the total number of slots we populate. The 7 is taken as // average word size. It was a mistake to have the user max @@ -563,7 +564,7 @@ vector Db::Native::makeAbstract(Xapian::docid docid, Query *query) // etc. but not elsewhere ? if (totaloccs == 0) { LOGDEB1(("makeAbstract: no occurrences\n")); - return vector(); + return false; } // Walk all document's terms position lists and populate slots @@ -632,21 +633,19 @@ vector Db::Native::makeAbstract(Xapian::docid docid, Query *query) LOGABS(("makeAbstract:%d: extracting. Got %u pages\n", chron.millis(), vpbreaks.size())); // Finally build the abstract by walking the map (in order of position) - vector vabs; + vabs.clear(); string chunk; bool incjk = false; + int page = 0; for (map::const_iterator it = sparseDoc.begin(); it != sparseDoc.end(); it++) { LOGDEB2(("Abtract:output %u -> [%s]\n", it->first,it->second.c_str())); if (!occupiedmarker.compare(it->second)) continue; if (chunk.empty() && !vpbreaks.empty()) { - int pnum = getPageNumberForPosition(vpbreaks, it->first); - if (pnum > 0) { - ostringstream ss; - ss << pnum; - chunk += string(" [p ") + ss.str() + "] "; - } + page = getPageNumberForPosition(vpbreaks, it->first); + if (page < 0) + page = 0; } Utf8Iter uit(it->second); bool newcjk = false; @@ -656,17 +655,17 @@ vector Db::Native::makeAbstract(Xapian::docid docid, Query *query) chunk += " "; incjk = newcjk; if (it->second == cstr_ellipsis) { - vabs.push_back(chunk); + vabs.push_back(pair(page, chunk)); chunk.clear(); } else { chunk += it->second; } } if (!chunk.empty()) - vabs.push_back(chunk); + vabs.push_back(pair(page, chunk)); LOGDEB2(("makeAbtract: done in %d mS\n", chron.millis())); - return vabs; + return true; } /* Rcl::Db methods ///////////////////////////////// */ @@ -2120,31 +2119,54 @@ bool Db::stemDiffers(const string& lang, const string& word, return true; } -bool Db::makeDocAbstract(Doc &doc, Query *query, vector& abstract) +bool Db::makeDocAbstract(Doc &doc, Query *query, + vector >& abstract) { - LOGDEB1(("Db::makeDocAbstract: exti %d\n", exti)); if (!m_ndb || !m_ndb->m_isopen) { LOGERR(("Db::makeDocAbstract: no db\n")); return false; } - XAPTRY(abstract = m_ndb->makeAbstract(doc.xdocid, query), + bool ret = false; + XAPTRY(ret = m_ndb->makeAbstract(doc.xdocid, query, abstract), m_ndb->xrdb, m_reason); - return m_reason.empty() ? true : false; + return (ret && m_reason.empty()) ? true : false; +} + +bool Db::makeDocAbstract(Doc &doc, Query *query, vector& abstract) +{ + if (!m_ndb || !m_ndb->m_isopen) { + LOGERR(("Db::makeDocAbstract: no db\n")); + return false; + } + vector > vpabs; + if (!makeDocAbstract(doc, query, vpabs)) + return false; + for (vector >::const_iterator it = vpabs.begin(); + it != vpabs.end(); it++) { + string chunk; + if (it->first > 0) { + ostringstream ss; + ss << it->first; + chunk += string(" [p ") + ss.str() + "] "; + } + chunk += it->second; + abstract.push_back(chunk); + } + return true; } bool Db::makeDocAbstract(Doc &doc, Query *query, string& abstract) { - LOGDEB1(("Db::makeDocAbstract: exti %d\n", exti)); if (!m_ndb || !m_ndb->m_isopen) { LOGERR(("Db::makeDocAbstract: no db\n")); return false; } - vector vab; - XAPTRY(vab = m_ndb->makeAbstract(doc.xdocid, query), - m_ndb->xrdb, m_reason); - for (vector::const_iterator it = vab.begin(); - it != vab.end(); it++) { - abstract.append(*it); + vector > vpabs; + if (!makeDocAbstract(doc, query, vpabs)) + return false; + for (vector >::const_iterator it = vpabs.begin(); + it != vpabs.end(); it++) { + abstract.append(it->second); abstract.append(cstr_ellipsis); } return m_reason.empty() ? true : false; diff --git a/src/rcldb/rcldb.h b/src/rcldb/rcldb.h index 675e70a7..96630b16 100644 --- a/src/rcldb/rcldb.h +++ b/src/rcldb/rcldb.h @@ -223,8 +223,13 @@ class Db { /** Build synthetic abstract for document, extracting chunks relevant for * the input query. This uses index data only (no access to the file) */ + // Abstract return as one string bool makeDocAbstract(Doc &doc, Query *query, string& abstract); + // Returned as a snippets vector bool makeDocAbstract(Doc &doc, Query *query, vector& abstract); + // Returned as a vector of page,snippet page is 0 if unknown + bool makeDocAbstract(Doc &doc, Query *query, + vector >& abstract); /** Retrieve detected page breaks positions */ int getFirstMatchPage(Doc &doc, Query *query); diff --git a/src/rcldb/rcldb_p.h b/src/rcldb/rcldb_p.h index 133a510c..f5af8ece 100644 --- a/src/rcldb/rcldb_p.h +++ b/src/rcldb/rcldb_p.h @@ -89,7 +89,8 @@ class Db::Native { const vector& terms, std::multimap& byQ); void setDbWideQTermsFreqs(Query *query); - vector makeAbstract(Xapian::docid id, Query *query); + bool makeAbstract(Xapian::docid id, Query *query, + vector >&); bool getPagePositions(Xapian::docid docid, vector& vpos); int getFirstMatchPage(Xapian::docid docid, Query *query); int getPageNumberForPosition(const vector& pbreaks, unsigned int pos);