From 08bd33090c5f7b9a6577dccab108edb4fd99eace Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Wed, 5 Dec 2018 13:46:26 +0100 Subject: [PATCH] GUI: implemented qt webengine compatibility. Default is still to use qt webkit --- src/qtgui/main.cpp | 14 ++ src/qtgui/recoll.pro.in | 1 + src/qtgui/reslist.cpp | 266 +++++++++++++++++++++++++++++-------- src/qtgui/reslist.h | 44 +++++- src/qtgui/snippets_w.cpp | 95 ++++++++----- src/qtgui/snippets_w.h | 37 +++++- src/query/reslistpager.cpp | 85 ++++++------ src/query/reslistpager.h | 1 + 8 files changed, 403 insertions(+), 140 deletions(-) diff --git a/src/qtgui/main.cpp b/src/qtgui/main.cpp index 86d976f0..c0ceb26d 100644 --- a/src/qtgui/main.cpp +++ b/src/qtgui/main.cpp @@ -229,7 +229,21 @@ int main(int argc, char **argv) } } +#ifdef USING_WEBENGINE + // This is necessary for allowing webengine to load local resources (icons) + // It is not an issue because we never access remote sites. + char arg_disable_web_security[] = "--disable-web-security"; + int appargc = argc + 1; + char** appargv = new char*[appargc+1]; + for(int i = 0; i < argc; i++) { + appargv[i] = argv[i]; + } + appargv[argc] = arg_disable_web_security; + appargv[argc+1] = nullptr; + QApplication app(appargc, appargv); +#else QApplication app(argc, argv); +#endif QCoreApplication::setOrganizationName("Recoll.org"); QCoreApplication::setApplicationName("recoll"); diff --git a/src/qtgui/recoll.pro.in b/src/qtgui/recoll.pro.in index d3f6ecce..f69aaa27 100644 --- a/src/qtgui/recoll.pro.in +++ b/src/qtgui/recoll.pro.in @@ -19,6 +19,7 @@ QT += xml greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport @QMAKE_ENABLE_WEBKIT@ greaterThan(QT_MAJOR_VERSION, 4): QT += webkitwidgets +@QMAKE_ENABLE_WEBENGINE@ greaterThan(QT_MAJOR_VERSION, 4): QT += webenginewidgets CONFIG += qt warn_on thread release diff --git a/src/qtgui/reslist.cpp b/src/qtgui/reslist.cpp index 9882125c..4a795821 100644 --- a/src/qtgui/reslist.cpp +++ b/src/qtgui/reslist.cpp @@ -61,11 +61,48 @@ static const QKeySequence quitKeySeq("Ctrl+q"); static const QKeySequence closeKeySeq("Ctrl+w"); #if defined(USING_WEBKIT) -#include -#include -#include +# include +# include +# include +# define QWEBSETTINGS QWebSettings +#elif defined(USING_WEBENGINE) +// Notes for WebEngine +// - All links must begin with http:// for acceptNavigationRequest to be +// called. +// - The links passed to acceptNav.. have the host part +// lowercased -> we change S0 to http://localhost/S0, not http://S0 +# include +# include +# include +# define QWEBSETTINGS QWebEngineSettings #endif +#ifdef USING_WEBENGINE +// This script saves the location details when a mouse button is +// clicked. This is for replacing data provided by Webkit QWebElement +// on a right-click as QT WebEngine does not have an equivalent service. +static const string locdetailscript(R"raw( +var locDetails = ''; +function saveLoc(ev) +{ + el = ev.target; + locDetails = ''; + while (el && el.attributes && !el.attributes.getNamedItem("rcldocnum")) { + el = el.parentNode; + } + rcldocnum = el.attributes.getNamedItem("rcldocnum"); + if (rcldocnum) { + rcldocnumvalue = rcldocnum.value; + } else { + rcldocnumvalue = ""; + } + if (el && el.attributes) { + locDetails = 'rcldocnum = ' + rcldocnumvalue + } +} +)raw"); +#endif // webengine + // Decide if we set font family and style with a css section in the // html or with qwebsettings setfont... calls. We currently do // it with websettings because this gives an instant redisplay, and @@ -91,6 +128,9 @@ public: map >& sugg); virtual string absSep() {return (const char *)(prefs.abssep.toUtf8());} virtual string iconUrl(RclConfig *, Rcl::Doc& doc); +#ifdef USING_WEBENGINE + virtual string linkPrefix() override {return "http://localhost/";} +#endif private: ResList *m_reslist; }; @@ -124,8 +164,9 @@ bool QtGuiResListPager::append(const string& data, int docnum, LOGDEB2("QtGuiReslistPager::appendDoc: blockCount " << m_reslist->document()->blockCount() << ", " << data << "\n"); logdata(data.c_str()); -#if defined(USING_WEBKIT) - QString sdoc = QString("
").arg(docnum); +#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) + QString sdoc = QString( + "
").arg(docnum); m_reslist->append(sdoc); m_reslist->append(QString::fromUtf8(data.c_str())); m_reslist->append("
"); @@ -151,7 +192,7 @@ string QtGuiResListPager::trans(const string& in) string QtGuiResListPager::detailsLink() { - string chunk = ""; + string chunk = string(""; chunk += trans("(show query)"); chunk += ""; return chunk; @@ -190,6 +231,11 @@ string QtGuiResListPager::headerContent() #endif out += string("color: ") + qs2utf8s(prefs.fontcolor) + ";\n"; out += string("}\n\n"); +#if defined(USING_WEBENGINE) + out += "\n"; +#endif out += qs2utf8s(prefs.reslistheadertext); return out; } @@ -257,23 +303,22 @@ string QtGuiResListPager::iconUrl(RclConfig *config, Rcl::Doc& doc) class PlainToRichQtReslist : public PlainToRich { public: - virtual string startMatch(unsigned int idx) - { - if (0 && m_hdata) { - string s1, s2; - stringsToString >(m_hdata->groups[idx], s1); - stringsToString >(m_hdata->ugroups[m_hdata->grpsugidx[idx]], s2); - LOGDEB2("Reslist startmatch: group " << s1 << " user group " << - s2 << "\n"); - } + virtual string startMatch(unsigned int idx) { + if (0 && m_hdata) { + string s1, s2; + stringsToString >(m_hdata->groups[idx], s1); + stringsToString >( + m_hdata->ugroups[m_hdata->grpsugidx[idx]], s2); + LOGDEB2("Reslist startmatch: group " << s1 << " user group " << + s2 << "\n"); + } - return string(""); - } - virtual string endMatch() - { - return string(""); - } + return string(""); + } + virtual string endMatch() { + return string(""); + } }; static PlainToRichQtReslist g_hiliter; @@ -286,13 +331,19 @@ ResList::ResList(QWidget* parent, const char* name) setObjectName("resList"); else setObjectName(name); -#if defined(USING_WEBKIT) + +#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) + setPage(new RclWebPage(this)); +#ifdef USING_WEBKIT LOGDEB("Reslist: using Webkit\n"); + page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); // signals and slots connections connect(this, SIGNAL(linkClicked(const QUrl &)), - this, SLOT(linkWasClicked(const QUrl &))); - page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); - settings()->setAttribute(QWebSettings::JavascriptEnabled, true); + this, SLOT(onLinkClicked(const QUrl &))); +#else + LOGDEB("Reslist: using Webengine\n"); +#endif + settings()->setAttribute(QWEBSETTINGS::JavascriptEnabled, true); #else LOGDEB("Reslist: using QTextBrowser\n"); setReadOnly(true); @@ -301,7 +352,7 @@ ResList::ResList(QWidget* parent, const char* name) setTabChangesFocus(true); // signals and slots connections connect(this, SIGNAL(anchorClicked(const QUrl &)), - this, SLOT(linkWasClicked(const QUrl &))); + this, SLOT(onLinkClicked(const QUrl &))); #endif setFont(); @@ -366,23 +417,39 @@ void ResList::setRclMain(RclMain *m, bool ismain) } } -void ResList::setFont() +void ResList::runStoredJS() +{ + runJS(m_js); + m_js.clear(); +} + +void ResList::runJS(const QString& js) { #if defined(USING_WEBKIT) -#ifndef SETFONT_WITH_HEADSTYLE - QWebSettings *websettings = settings(); + page()->mainFrame()->evaluateJavaScript(js); +#elif defined(USING_WEBENGINE) + page()->runJavaScript(js); +#else + Q_UNUSED(js); +#endif +} + +void ResList::setFont() +{ +#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) +# ifndef SETFONT_WITH_HEADSTYLE if (prefs.reslistfontfamily.length()) { // For some reason there is (12-2014) an offset of 3 between what // we request from webkit and what we get. - websettings->setFontSize(QWebSettings::DefaultFontSize, + settings()->setFontSize(QWEBSETTINGS::DefaultFontSize, prefs.reslistfontsize + 3); - websettings->setFontFamily(QWebSettings::StandardFont, + settings()->setFontFamily(QWEBSETTINGS::StandardFont, prefs.reslistfontfamily); } else { - websettings->resetFontSize(QWebSettings::DefaultFontSize); - websettings->resetFontFamily(QWebSettings::StandardFont); + settings()->resetFontSize(QWEBSETTINGS::DefaultFontSize); + settings()->resetFontFamily(QWEBSETTINGS::StandardFont); } -#endif +# endif #else if (prefs.reslistfontfamily.length()) { QFont nfont(prefs.reslistfontfamily, prefs.reslistfontsize); @@ -399,8 +466,6 @@ int ResList::newListId() return ++id; } -extern "C" int XFlush(void *); - void ResList::setDocSource(std::shared_ptr nsource) { LOGDEB("ResList::setDocSource()\n"); @@ -441,7 +506,7 @@ void ResList::resetView() // slow search, the user will wonder if anything happened. The // following helps making sure that the textedit is really // blank. Else, there are often icons or text left around -#if defined(USING_WEBKIT) +#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) m_text = ""; setHtml(""); #else @@ -596,12 +661,21 @@ void ResList::highlighted(const QString& ) // fair enough, else we go to next/previous result page. void ResList::resPageUpOrBack() { -#if defined(USING_WEBKIT) + #if defined(USING_WEBKIT) if (scrollIsAtTop()) { resultPageBack(); } else { - QWebFrame *frame = page()->mainFrame(); - frame->scroll(0, -int(0.9*geometry().height())); + page()->mainFrame()->scroll(0, -int(0.9*geometry().height())); + } + setupArrows(); +#elif defined(USING_WEBENGINE) + if (scrollIsAtTop()) { + resultPageBack(); + } else { + QString js = "window.scrollBy(" + + QString::number(0) + ", " + + QString::number(-int(0.9*geometry().height())) + ");"; + runJS(js); } setupArrows(); #else @@ -618,8 +692,17 @@ void ResList::resPageDownOrNext() if (scrollIsAtBottom()) { resultPageNext(); } else { - QWebFrame *frame = page()->mainFrame(); - frame->scroll(0, int(0.9*geometry().height())); + page()->mainFrame()->scroll(0, int(0.9*geometry().height())); + } + setupArrows(); +#elif defined(USING_WEBENGINE) + if (scrollIsAtBottom()) { + resultPageNext(); + } else { + QString js = "window.scrollBy(" + + QString::number(0) + ", " + + QString::number(int(0.9*geometry().height())) + ");"; + runJS(js); } setupArrows(); #else @@ -653,6 +736,16 @@ bool ResList::scrollIsAtBottom() } LOGDEB2("scrollIsAtBottom: returning " << ret << "\n"); return ret; +#elif defined(USING_WEBENGINE) + QSize css = page()->contentsSize().toSize(); + QSize wss = size(); + QPoint sp = page()->scrollPosition().toPoint(); + LOGDEB1("atBottom: contents W " << css.width() << " H " << css.height() << + " widget W " << wss.width() << " Y " << wss.height() << + " scroll X " << sp.x() << " Y " << sp.y() << "\n"); + // This seems to work but it's mysterious as points and pixels + // should not be the same + return wss.height() + sp.y() >= css.height() - 10; #else return false; #endif @@ -673,6 +766,8 @@ bool ResList::scrollIsAtTop() } LOGDEB2("scrollIsAtTop: returning " << ret << "\n"); return ret; +#elif defined(USING_WEBENGINE) + return page()->scrollPosition().toPoint().ry() == 0; #else return false; #endif @@ -715,7 +810,7 @@ void ResList::resultPageFor(int docnum) void ResList::append(const QString &text) { LOGDEB2("QtGuiReslistPager::appendQString : " << qs2utf8s(text) << "\n"); -#if defined(USING_WEBKIT) +#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) m_text += text; #else QTextBrowser::append(text); @@ -732,6 +827,15 @@ void ResList::displayPage() setHtml(m_text); #endif +#if defined(USING_WEBENGINE) + // Have to delay running this. Alternative would be to set it as + // onload on the body element in the html, like upplay does, but + // this would need an ennoying reslistpager modification. + m_js = "elt=document.getElementsByTagName('body')[0];" + "elt.addEventListener('contextmenu', saveLoc);"; + QTimer::singleShot(200, this, SLOT(runStoredJS())); +#endif + LOGDEB0("ResList::displayPg: hasNext " << m_pager->hasNext() << " atBot " << scrollIsAtBottom() << " hasPrev " << m_pager->hasPrev() << " at Top " << scrollIsAtTop() << " \n"); @@ -759,6 +863,12 @@ void ResList::previewExposed(int docnum) } else { LOGDEB2("Not Found\n"); } +#elif defined(USING_WEBENGINE) + QString js = QString( + "elt=document.getElementById('%1');" + "if (elt){elt.removeAttribute('style');}" + ).arg(m_curPvDoc - pageFirstDocNum()); + runJS(js); #else pair blockrange = parnumfromdocnum(m_curPvDoc); if (blockrange.first != -1) { @@ -789,6 +899,12 @@ void ResList::previewExposed(int docnum) } else { LOGDEB2("Not Found\n"); } +#elif defined(USING_WEBENGINE) + QString js = QString( + "elt=document.getElementById('%1');" + "if(elt){elt.setAttribute('style', 'background: LightBlue');}" + ).arg(docnum - pageFirstDocNum()); + runJS(js); #else pair blockrange = parnumfromdocnum(docnum); @@ -814,8 +930,13 @@ void ResList::previewExposed(int docnum) void ResList::mouseDoubleClickEvent(QMouseEvent *event) { RESLIST_PARENTCLASS::mouseDoubleClickEvent(event); -#if defined(USING_WEBKIT) +#if defined(USING_WEBKIT) emit(wordSelect(selectedText())); +#elif defined(USING_WEBENGINE) + // webengineview does not have such an event function, and + // reimplementing event() itself is not useful (tried) as it does + // not get mouse clicks. We'd need javascript to do this, but it's + // not that useful, so left aside for now. #else if (textCursor().hasSelection()) emit(wordSelect(textCursor().selectedText())); @@ -834,14 +955,16 @@ void ResList::showQueryDetails() QMessageBox::information(this, tr("Query details"), desc); } -void ResList::linkWasClicked(const QUrl &url) +void ResList::onLinkClicked(const QUrl &qurl) { // qt5: url.toString() does not accept FullyDecoded, but that's what we // want. e.g. Suggestions links are like Sterm|spelling which we // receive as Sterm%7CSpelling - string strurl = url_decode(qs2utf8s(url.toString())); + string strurl = url_decode(qs2utf8s(qurl.toString())); - LOGDEB("ResList::linkWasClicked: [" << strurl << "]\n"); + LOGDEB1("ResList::onLinkClicked: [" << strurl << "] prefix " << + m_pager->linkPrefix() << "\n"); + strurl = strurl.substr(m_pager->linkPrefix().size()); int what = strurl[0]; switch (what) { @@ -854,7 +977,7 @@ void ResList::linkWasClicked(const QUrl &url) int i = atoi(strurl.c_str()+1) - 1; Rcl::Doc doc; if (!getDoc(i, doc)) { - LOGERR("ResList::linkWasClicked: can't get doc for " << i << "\n"); + LOGERR("ResList::onLinkClicked: can't get doc for " << i << "\n"); return; } emit(showSnippets(doc)); @@ -869,7 +992,7 @@ void ResList::linkWasClicked(const QUrl &url) int i = atoi(strurl.c_str()+1) - 1; Rcl::Doc doc; if (!getDoc(i, doc)) { - LOGERR("ResList::linkWasClicked: can't get doc for " << i << "\n"); + LOGERR("ResList::onLinkClicked: can't get doc for " << i << "\n"); return; } vector dups; @@ -885,7 +1008,7 @@ void ResList::linkWasClicked(const QUrl &url) int i = atoi(strurl.c_str()+1) - 1; Rcl::Doc doc; if (!getDoc(i, doc)) { - LOGERR("ResList::linkWasClicked: can't get doc for " << i << "\n"); + LOGERR("ResList::onLinkClicked: can't get doc for " << i << "\n"); return; } emit editRequested(ResultPopup::getParent(std::shared_ptr(), @@ -894,7 +1017,8 @@ void ResList::linkWasClicked(const QUrl &url) break; // Show query details - case 'H': + case 'h': + case 'H': { showQueryDetails(); break; @@ -907,7 +1031,7 @@ void ResList::linkWasClicked(const QUrl &url) int i = atoi(strurl.c_str()+1) - 1; Rcl::Doc doc; if (!getDoc(i, doc)) { - LOGERR("ResList::linkWasClicked: can't get doc for " << i << "\n"); + LOGERR("ResList::onLinkClicked: can't get doc for " << i << "\n"); return; } if (what == 'P') { @@ -934,7 +1058,7 @@ void ResList::linkWasClicked(const QUrl &url) case 'R': { int i = atoi(strurl.c_str() + 1) - 1; - QString s = url.toString(); + QString s = qurl.toString(); int bar = s.indexOf("|"); if (bar == -1 || bar >= s.size()-1) break; @@ -968,14 +1092,36 @@ void ResList::linkWasClicked(const QUrl &url) break; default: - LOGERR("ResList::linkWasClicked: bad link [" << strurl << "]\n"); + LOGERR("ResList::onLinkClicked: bad link [" << strurl << "]\n"); break;// ?? } } +void ResList::onPopupJsDone(const QVariant &jr) +{ + QString qs(jr.toString()); + LOGDEB("onPopupJsDone: parameter: " << qs2utf8s(qs) << "\n"); + QStringList qsl = qs.split("\n", QString::SkipEmptyParts); + for (int i = 0 ; i < qsl.size(); i++) { + int eq = qsl[i].indexOf("="); + if (eq > 0) { + QString nm = qsl[i].left(eq).trimmed(); + QString value = qsl[i].right(qsl[i].size() - (eq+1)).trimmed(); + if (!nm.compare("rcldocnum")) { + m_popDoc = atoi(qs2utf8s(value).c_str()); + } else { + LOGERR("onPopupJsDone: unknown key: " << qs2utf8s(nm) << "\n"); + } + } + } + doCreatePopupMenu(); +} + void ResList::createPopupMenu(const QPoint& pos) { LOGDEB("ResList::createPopupMenu(" << pos.x() << ", " << pos.y() << ")\n"); + m_popDoc = -1; + m_popPos = pos; #if defined(USING_WEBKIT) QWebHitTestResult htr = page()->mainFrame()->hitTestContent(pos); if (htr.isNull()) @@ -987,13 +1133,21 @@ void ResList::createPopupMenu(const QPoint& pos) return; QString snum = el.attribute("rcldocnum"); m_popDoc = pageFirstDocNum() + snum.toInt(); +#elif defined(USING_WEBENGINE) + QString js("window.locDetails;"); + RclWebPage *mypage = dynamic_cast(page()); + mypage->runJavaScript(js, [this](const QVariant &v) {onPopupJsDone(v);}); #else QTextCursor cursor = cursorForPosition(pos); int blocknum = cursor.blockNumber(); LOGDEB("ResList::createPopupMenu(): block " << blocknum << "\n"); m_popDoc = docnumfromparnum(blocknum); #endif + doCreatePopupMenu(); +} +void ResList::doCreatePopupMenu() +{ if (m_popDoc < 0) return; Rcl::Doc doc; @@ -1004,7 +1158,7 @@ void ResList::createPopupMenu(const QPoint& pos) if (m_ismainres) options |= ResultPopup::isMain; QMenu *popup = ResultPopup::create(this, options, m_source, doc); - popup->popup(mapToGlobal(pos)); + popup->popup(mapToGlobal(m_popPos)); } void ResList::menuPreview() diff --git a/src/qtgui/reslist.h b/src/qtgui/reslist.h index 8495fd28..1d208c4c 100644 --- a/src/qtgui/reslist.h +++ b/src/qtgui/reslist.h @@ -20,6 +20,7 @@ #include "autoconfig.h" #include +#include #if defined(USING_WEBENGINE) # include @@ -34,6 +35,7 @@ class RclMain; class QtGuiResListPager; +class QEvent; namespace Rcl { class Doc; } @@ -110,22 +112,26 @@ protected: void mouseReleaseEvent(QMouseEvent *e); void mouseDoubleClickEvent(QMouseEvent*); +public slots: + virtual void onLinkClicked(const QUrl &); + virtual void onPopupJsDone(const QVariant&); + void runJS(const QString& js); + void runStoredJS(); protected slots: virtual void languageChange(); - virtual void linkWasClicked(const QUrl &); private: QtGuiResListPager *m_pager{0}; std::shared_ptr m_source; int m_popDoc{-1}; // Docnum for the popup menu. + QPoint m_popPos; int m_curPvDoc{-1};// Docnum for current preview int m_lstClckMod{0}; // Last click modifier. int m_listId{0}; // query Id for matching with preview windows - #if defined(USING_WEBKIT) || defined(USING_WEBENGINE) // Webview makes it more difficult to append text incrementally, // so we store the page and display it when done. - QString m_text; + QString m_text; #else // Translate from textedit paragraph number to relative // docnum. Built while we insert text into the qtextedit @@ -133,10 +139,12 @@ private: virtual int docnumfromparnum(int); virtual std::pair parnumfromdocnum(int); #endif + QString m_js; RclMain *m_rclmain{0}; bool m_ismainres{true}; - virtual void displayPage(); // Display current page + void doCreatePopupMenu(); + virtual void displayPage(); static int newListId(); void resetView(); bool scrollIsAtTop(); @@ -144,5 +152,33 @@ private: void setupArrows(); }; +#ifdef USING_WEBENGINE + +// Subclass the page to hijack the link clicks +class RclWebPage: public QWebEnginePage { + Q_OBJECT + +public: + RclWebPage(ResList *parent) + : QWebEnginePage((QWidget *)parent), m_reslist(parent) {} + +protected: + virtual bool acceptNavigationRequest(const QUrl& url, + NavigationType, + bool) { + m_reslist->onLinkClicked(url); + return false; + } + +private: + ResList *m_reslist; +}; + +#else // Using Qt Webkit + +#define RclWebPage QWebPage + +#endif + #endif /* _RESLIST_H_INCLUDED_ */ diff --git a/src/qtgui/snippets_w.cpp b/src/qtgui/snippets_w.cpp index 83541500..07eb6812 100644 --- a/src/qtgui/snippets_w.cpp +++ b/src/qtgui/snippets_w.cpp @@ -21,15 +21,28 @@ #include #include #include -using namespace std; #if defined(USING_WEBKIT) -#include -#include -#include +# include +# include +# include +# define QWEBSETTINGS QWebSettings +# define QWEBPAGE QWebPage +#elif defined(USING_WEBENGINE) +// Notes for WebEngine +// - All links must begin with http:// for acceptNavigationRequest to be +// called. +// - The links passed to acceptNav.. have the host part +// lowercased -> we change S0 to http://h/S0, not http://S0 +# include +# include +# include +# define QWEBSETTINGS QWebEngineSettings +# define QWEBPAGE QWebEnginePage #else #include #endif + #include #include "log.h" @@ -40,10 +53,12 @@ using namespace std; #include "rclhelp.h" #include "plaintorich.h" -// Note: the internal search currently does not work with QTextBrowser. To be -// fixed by looking at the preview code if someone asks for it... +using namespace std; + #if defined(USING_WEBKIT) #define browser ((QWebView*)browserw) +#elif defined(USING_WEBENGINE) +#define browser ((QWebEngineView*)browserw) #else #define browser ((QTextBrowser*)browserw) #endif @@ -97,22 +112,32 @@ void SnippetsW::init() verticalLayout->insertWidget(0, browserw); browser->setUrl(QUrl(QString::fromUtf8("about:blank"))); connect(browser, SIGNAL(linkClicked(const QUrl &)), - this, SLOT(linkWasClicked(const QUrl &))); + this, SLOT(onLinkClicked(const QUrl &))); browser->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); browser->page()->currentFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); - QWebSettings *ws = browser->page()->settings(); + QWEBSETTINGS *ws = browser->page()->settings(); if (prefs.reslistfontfamily != "") { - ws->setFontFamily(QWebSettings::StandardFont, prefs.reslistfontfamily); - ws->setFontSize(QWebSettings::DefaultFontSize, prefs.reslistfontsize); + ws->setFontFamily(QWEBSETTINGS::StandardFont, prefs.reslistfontfamily); + ws->setFontSize(QWEBSETTINGS::DefaultFontSize, prefs.reslistfontsize); } if (!prefs.snipCssFile.isEmpty()) ws->setUserStyleSheetUrl(QUrl::fromLocalFile(prefs.snipCssFile)); +#elif defined(USING_WEBENGINE) + browserw = new QWebEngineView(this); + verticalLayout->insertWidget(0, browserw); + browser->setPage(new SnipWebPage(this)); + QWEBSETTINGS *ws = browser->page()->settings(); + if (prefs.reslistfontfamily != "") { + ws->setFontFamily(QWEBSETTINGS::StandardFont, prefs.reslistfontfamily); + ws->setFontSize(QWEBSETTINGS::DefaultFontSize, prefs.reslistfontsize); + } + // Stylesheet TBD #else browserw = new QTextBrowser(this); verticalLayout->insertWidget(0, browserw); connect(browser, SIGNAL(anchorClicked(const QUrl &)), - this, SLOT(linkWasClicked(const QUrl &))); + this, SLOT(onLinkClicked(const QUrl &))); browser->setReadOnly(true); browser->setUndoRedoEnabled(false); browser->setOpenLinks(false); @@ -165,23 +190,23 @@ void SnippetsW::init() g_hiliter.set_inputhtml(false); bool nomatch = true; - for (vector::const_iterator it = vpabs.begin(); - it != vpabs.end(); it++) { - if (it->page == -1) { + for (const auto& snippet : vpabs) { + if (snippet.page == -1) { oss << "" << - it->snippet << "" << endl; + snippet.snippet << "" << endl; continue; } list lr; - if (!g_hiliter.plaintorich(it->snippet, lr, hdata)) { - LOGDEB1("No match for [" << (it->snippet) << "]\n" ); + if (!g_hiliter.plaintorich(snippet.snippet, lr, hdata)) { + LOGDEB1("No match for [" << snippet.snippet << "]\n"); continue; } nomatch = false; oss << ""; - if (it->page > 0) { - oss << "page << "T" << it->term << "\">" - << "P. " << it->page << ""; + if (snippet.page > 0) { + oss << "" + << "P. " << snippet.page << ""; } oss << "" << lr.front().c_str() << "" << endl; } @@ -193,10 +218,12 @@ void SnippetsW::init() "generator got lost in a maze...

")); } oss << "\n"; -#if defined(USING_WEBKIT) +#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) browser->setHtml(QString::fromUtf8(oss.str().c_str())); #else browser->insertHtml(QString::fromUtf8(oss.str().c_str())); + browser->moveCursor (QTextCursor::Start); + browser->ensureCursorVisible(); #endif } @@ -212,20 +239,21 @@ void SnippetsW::slotEditFindNext() if (!searchFM->isVisible()) slotEditFind(); -#if defined(USING_WEBKIT) +#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) browser->findText(searchLE->text()); #else - browser->find(searchLE->text(), 0); + browser->find(searchLE->text()); #endif } + void SnippetsW::slotEditFindPrevious() { if (!searchFM->isVisible()) slotEditFind(); -#if defined(USING_WEBKIT) - browser->findText(searchLE->text(), QWebPage::FindBackward); +#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) + browser->findText(searchLE->text(), QWEBPAGE::FindBackward); #else browser->find(searchLE->text(), QTextDocument::FindBackward); #endif @@ -233,17 +261,22 @@ void SnippetsW::slotEditFindPrevious() void SnippetsW::slotSearchTextChanged(const QString& txt) { -#if defined(USING_WEBKIT) +#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) browser->findText(txt); #else + // Cursor thing is so that we don't go to the next occurrence with + // each character, but rather try to extend the current match + QTextCursor cursor = browser->textCursor(); + cursor.setPosition(cursor.anchor(), QTextCursor::KeepAnchor); + browser->setTextCursor(cursor); browser->find(txt, 0); #endif } -void SnippetsW::linkWasClicked(const QUrl &url) +void SnippetsW::onLinkClicked(const QUrl &url) { - string ascurl = (const char *)url.toString().toUtf8(); - LOGDEB("Snippets::linkWasClicked: [" << (ascurl) << "]\n" ); + string ascurl = qs2u8s(url.toString()).substr(9); + LOGDEB("Snippets::onLinkClicked: [" << ascurl << "]\n"); if (ascurl.size() > 3) { int what = ascurl[0]; @@ -264,7 +297,5 @@ void SnippetsW::linkWasClicked(const QUrl &url) } } } - LOGERR("Snippets::linkWasClicked: bad link [" << (ascurl) << "]\n" ); + LOGERR("Snippets::onLinkClicked: bad link [" << ascurl << "]\n"); } - - diff --git a/src/qtgui/snippets_w.h b/src/qtgui/snippets_w.h index 8c7dff86..d12ed624 100644 --- a/src/qtgui/snippets_w.h +++ b/src/qtgui/snippets_w.h @@ -33,26 +33,49 @@ class SnippetsW : public QWidget, public Ui::Snippets { Q_OBJECT public: - SnippetsW(Rcl::Doc doc, std::shared_ptr source, QWidget* parent = 0) - : QWidget(parent), m_doc(doc), m_source(source) - { - setupUi((QDialog*)this); - init(); + SnippetsW(Rcl::Doc doc, std::shared_ptr source, + QWidget* parent = 0) + : QWidget(parent), m_doc(doc), m_source(source) { + setupUi((QDialog*)this); + init(); } +public slots: + virtual void onLinkClicked(const QUrl &); + protected slots: - virtual void linkWasClicked(const QUrl &); virtual void slotEditFind(); virtual void slotEditFindNext(); virtual void slotEditFindPrevious(); virtual void slotSearchTextChanged(const QString&); signals: void startNativeViewer(Rcl::Doc, int pagenum, QString term); - + private: void init(); Rcl::Doc m_doc; std::shared_ptr m_source; }; +#ifdef USING_WEBENGINE +#include +// Subclass the page to hijack the link clicks +class SnipWebPage: public QWebEnginePage { + Q_OBJECT +public: + SnipWebPage(SnippetsW *parent) + : QWebEnginePage((QWidget *)parent), m_parent(parent) {} +protected: + virtual bool acceptNavigationRequest(const QUrl& url, + NavigationType, + bool) { + m_parent->onLinkClicked(url); + return false; + } +private: + SnippetsW *m_parent; +}; +#endif + + #endif /* _SNIPPETS_W_H_INCLUDED_ */ diff --git a/src/query/reslistpager.cpp b/src/query/reslistpager.cpp index 6bc5127f..174b485d 100644 --- a/src/query/reslistpager.cpp +++ b/src/query/reslistpager.cpp @@ -25,6 +25,7 @@ #include #include +#include #include using std::ostringstream; using std::endl; @@ -69,12 +70,13 @@ ResListPager::ResListPager(int pagesize) void ResListPager::resultPageNext() { if (!m_docSource) { - LOGDEB("ResListPager::resultPageNext: null source\n" ); + LOGDEB("ResListPager::resultPageNext: null source\n"); return; } int resCnt = m_docSource->getResCnt(); - LOGDEB("ResListPager::resultPageNext: rescnt " << (resCnt) << ", winfirst " << (m_winfirst) << "\n" ); + LOGDEB("ResListPager::resultPageNext: rescnt " << resCnt << + ", winfirst " << m_winfirst << "\n"); if (m_winfirst < 0) { m_winfirst = 0; @@ -126,12 +128,13 @@ static string maybeEscapeHtml(const string& fld) void ResListPager::resultPageFor(int docnum) { if (!m_docSource) { - LOGDEB("ResListPager::resultPageFor: null source\n" ); + LOGDEB("ResListPager::resultPageFor: null source\n"); return; } int resCnt = m_docSource->getResCnt(); - LOGDEB("ResListPager::resultPageFor(" << (docnum) << "): rescnt " << (resCnt) << ", winfirst " << (m_winfirst) << "\n" ); + LOGDEB("ResListPager::resultPageFor(" << docnum << "): rescnt " << + resCnt << ", winfirst " << m_winfirst << "\n"); m_winfirst = (docnum / m_pagesize) * m_pagesize; // Get the next page of results. @@ -250,7 +253,7 @@ void ResListPager::displayDoc(RclConfig *config, int i, Rcl::Doc& doc, // Links; ostringstream linksbuf; if (canIntern(doc.mimetype, config)) { - linksbuf << "" + linksbuf << "" << trans("Preview") << "  "; } @@ -258,12 +261,12 @@ void ResListPager::displayDoc(RclConfig *config, int i, Rcl::Doc& doc, doc.getmeta(Rcl::Doc::keyapptg, &apptag); if (!config->getMimeViewerDef(doc.mimetype, apptag, false).empty()) { - linksbuf << "" + linksbuf << "" << trans("Open") << ""; } ostringstream snipsbuf; if (doc.haspages) { - snipsbuf << "" + snipsbuf << "" << trans("Snippets") << "  "; linksbuf << "  " << snipsbuf.str(); } @@ -272,8 +275,8 @@ void ResListPager::displayDoc(RclConfig *config, int i, Rcl::Doc& doc, if (doc.getmeta(Rcl::Doc::keycc, &collapscnt) && !collapscnt.empty()) { ostringstream collpsbuf; int clc = atoi(collapscnt.c_str()) + 1; - collpsbuf << "" - << trans("Dups") << "(" << clc << ")" << "  "; + collpsbuf << "" + << trans("Dups") << "(" << clc << ")" << "  "; linksbuf << "  " << collpsbuf.str(); } @@ -326,7 +329,7 @@ void ResListPager::displayDoc(RclConfig *config, int i, Rcl::Doc& doc, // the table approach for 1.15 for now (in guiutils.cpp) // chunk << "
" << endl; - LOGDEB2("Chunk: [" << ((const char *)chunk.rdbuf()->str()) << "]\n" ); + LOGDEB2("Chunk: [" << chunk.rdbuf()->str() << "]\n"); append(chunk.rdbuf()->str(), i, doc); } @@ -342,13 +345,13 @@ bool ResListPager::getDoc(int num, Rcl::Doc& doc) void ResListPager::displayPage(RclConfig *config) { - LOGDEB("ResListPager::displayPage\n" ); + LOGDEB("ResListPager::displayPage. linkPrefix: " << linkPrefix() << "\n"); if (!m_docSource) { - LOGDEB("ResListPager::displayPage: null source\n" ); + LOGDEB("ResListPager::displayPage: null source\n"); return; } if (m_winfirst < 0 && !pageEmpty()) { - LOGDEB("ResListPager::displayPage: sequence error: winfirst < 0\n" ); + LOGDEB("ResListPager::displayPage: sequence error: winfirst < 0\n"); return; } @@ -363,14 +366,14 @@ void ResListPager::displayPage(RclConfig *config) // accumulator // Also note that there can be results beyond the estimated resCnt. chunk << "" << endl - << "" << endl - << headerContent() - << "" << endl - << pageTop() - << "

" - << m_docSource->title() - << "   "; + << "" << endl + << headerContent() + << "" << endl + << pageTop() + << "

" + << m_docSource->title() + << "   "; if (pageEmpty()) { chunk << trans("

No results found
"); @@ -389,11 +392,11 @@ void ResListPager::displayPage(RclConfig *config) if (o_index_stripchars) { chunk << trans("

Alternate spellings (accents suppressed): ") - << "

"; + << "
"; } else { chunk << trans("

Alternate spellings: ") - << "

"; + << "
"; } @@ -412,27 +415,27 @@ void ResListPager::displayPage(RclConfig *config) unsigned int resCnt = m_docSource->getResCnt(); if (m_winfirst + m_respage.size() < resCnt) { chunk << trans("Documents") << " " << m_winfirst + 1 - << "-" << m_winfirst + m_respage.size() << " " - << trans("out of at least") << " " - << resCnt << " " << trans("for") << " " ; + << "-" << m_winfirst + m_respage.size() << " " + << trans("out of at least") << " " + << resCnt << " " << trans("for") << " " ; } else { chunk << trans("Documents") << " " - << m_winfirst + 1 << "-" << m_winfirst + m_respage.size() - << " " << trans("for") << " "; + << m_winfirst + 1 << "-" << m_winfirst + m_respage.size() + << " " << trans("for") << " "; } } chunk << detailsLink(); if (hasPrev() || hasNext()) { chunk << "  "; if (hasPrev()) { - chunk << "" - << trans("Previous") - << "   "; + chunk << "" + << trans("Previous") + << "   "; } if (hasNext()) { - chunk << "" - << trans("Next") - << ""; + chunk << "" + << trans("Next") + << ""; } } chunk << "

" << endl; @@ -457,14 +460,14 @@ void ResListPager::displayPage(RclConfig *config) chunk << "

"; if (hasPrev() || hasNext()) { if (hasPrev()) { - chunk << "" - << trans("Previous") - << "   "; + chunk << "" + << trans("Previous") + << "   "; } if (hasNext()) { - chunk << "" - << trans("Next") - << ""; + chunk << "" + << trans("Next") + << ""; } } chunk << "

" << endl; @@ -505,7 +508,7 @@ string ResListPager::trans(const string& in) string ResListPager::detailsLink() { - string chunk = ""; + string chunk = string(""; chunk += trans("(show query)") + ""; return chunk; } diff --git a/src/query/reslistpager.h b/src/query/reslistpager.h index 087e307d..74e71941 100644 --- a/src/query/reslistpager.h +++ b/src/query/reslistpager.h @@ -121,6 +121,7 @@ public: sugg.clear(); } virtual string absSep() {return "…";} + virtual string linkPrefix() {return "";} private: int m_pagesize; int m_newpagesize;