--- a/src/qtgui/reslist.cpp
+++ b/src/qtgui/reslist.cpp
@@ -61,10 +61,47 @@
static const QKeySequence closeKeySeq("Ctrl+w");
#if defined(USING_WEBKIT)
-#include <QWebFrame>
-#include <QWebElement>
-#include <QWebSettings>
-#endif
+# include <QWebFrame>
+# include <QWebElement>
+# include <QWebSettings>
+# 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 <QWebEnginePage>
+# include <QWebEngineSettings>
+# include <QtWebEngineWidgets>
+# 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 <head> or with qwebsettings setfont... calls. We currently do
@@ -91,6 +128,9 @@
map<string, vector<string> >& 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 @@
LOGDEB2("QtGuiReslistPager::appendDoc: blockCount " <<
m_reslist->document()->blockCount() << ", " << data << "\n");
logdata(data.c_str());
-#if defined(USING_WEBKIT)
- QString sdoc = QString("<div class=\"rclresult\" rcldocnum=\"%1\">").arg(docnum);
+#if defined(USING_WEBKIT) || defined(USING_WEBENGINE)
+ QString sdoc = QString(
+ "<div class=\"rclresult\" id=\"%1\" rcldocnum=\"%1\">").arg(docnum);
m_reslist->append(sdoc);
m_reslist->append(QString::fromUtf8(data.c_str()));
m_reslist->append("</div>");
@@ -151,7 +192,7 @@
string QtGuiResListPager::detailsLink()
{
- string chunk = "<a href=\"H-1\">";
+ string chunk = string("<a href=\"") + linkPrefix() + "H-1\">";
chunk += trans("(show query)");
chunk += "</a>";
return chunk;
@@ -190,6 +231,11 @@
#endif
out += string("color: ") + qs2utf8s(prefs.fontcolor) + ";\n";
out += string("}\n</style>\n");
+#if defined(USING_WEBENGINE)
+ out += "<script type=\"text/javascript\">\n";
+ out += locdetailscript;
+ out += "</script>\n";
+#endif
out += qs2utf8s(prefs.reslistheadertext);
return out;
}
@@ -257,23 +303,22 @@
class PlainToRichQtReslist : public PlainToRich {
public:
- virtual string startMatch(unsigned int idx)
- {
- if (0 && m_hdata) {
- string s1, s2;
- stringsToString<vector<string> >(m_hdata->groups[idx], s1);
- stringsToString<vector<string> >(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<vector<string> >(m_hdata->groups[idx], s1);
+ stringsToString<vector<string> >(
+ m_hdata->ugroups[m_hdata->grpsugidx[idx]], s2);
+ LOGDEB2("Reslist startmatch: group " << s1 << " user group " <<
+ s2 << "\n");
+ }
- return string("<span class='rclmatch' style='")
- + qs2utf8s(prefs.qtermstyle) + string("'>");
- }
- virtual string endMatch()
- {
- return string("</span>");
- }
+ return string("<span class='rclmatch' style='")
+ + qs2utf8s(prefs.qtermstyle) + string("'>");
+ }
+ virtual string endMatch() {
+ return string("</span>");
+ }
};
static PlainToRichQtReslist g_hiliter;
@@ -286,13 +331,19 @@
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 @@
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::runStoredJS()
+{
+ runJS(m_js);
+ m_js.clear();
+}
+
+void ResList::runJS(const QString& js)
+{
+#if defined(USING_WEBKIT)
+ page()->mainFrame()->evaluateJavaScript(js);
+#elif defined(USING_WEBENGINE)
+ page()->runJavaScript(js);
+#else
+ Q_UNUSED(js);
+#endif
+}
+
void ResList::setFont()
{
-#if defined(USING_WEBKIT)
-#ifndef SETFONT_WITH_HEADSTYLE
- QWebSettings *websettings = settings();
+#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);
- }
-#endif
+ settings()->resetFontSize(QWEBSETTINGS::DefaultFontSize);
+ settings()->resetFontFamily(QWEBSETTINGS::StandardFont);
+ }
+# endif
#else
if (prefs.reslistfontfamily.length()) {
QFont nfont(prefs.reslistfontfamily, prefs.reslistfontsize);
@@ -398,8 +465,6 @@
static int id;
return ++id;
}
-
-extern "C" int XFlush(void *);
void ResList::setDocSource(std::shared_ptr<DocSequence> nsource)
{
@@ -441,7 +506,7 @@
// 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("<html><body></body></html>");
#else
@@ -596,12 +661,21 @@
// 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 @@
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 @@
}
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 @@
}
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::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);
@@ -730,6 +825,15 @@
#if defined(USING_WEBENGINE) || defined(USING_WEBKIT)
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() <<
@@ -759,6 +863,12 @@
} 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<int,int> blockrange = parnumfromdocnum(m_curPvDoc);
if (blockrange.first != -1) {
@@ -789,6 +899,12 @@
} 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<int,int> blockrange = parnumfromdocnum(docnum);
@@ -814,8 +930,13 @@
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 @@
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 @@
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 @@
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<Rcl::Doc> dups;
@@ -885,7 +1008,7 @@
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<DocSequence>(),
@@ -894,7 +1017,8 @@
break;
// Show query details
- case 'H':
+ case 'h':
+ case 'H':
{
showQueryDetails();
break;
@@ -907,7 +1031,7 @@
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 @@
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 @@
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 @@
return;
QString snum = el.attribute("rcldocnum");
m_popDoc = pageFirstDocNum() + snum.toInt();
+#elif defined(USING_WEBENGINE)
+ QString js("window.locDetails;");
+ RclWebPage *mypage = dynamic_cast<RclWebPage*>(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 @@
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()