GUI: implemented qt webengine compatibility. Default is still to use qt webkit

This commit is contained in:
Jean-Francois Dockes 2018-12-05 13:46:26 +01:00
parent 504705879b
commit 08bd33090c
8 changed files with 403 additions and 140 deletions

View File

@ -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");

View File

@ -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

View File

@ -61,11 +61,48 @@ static const QKeySequence quitKeySeq("Ctrl+q");
static const QKeySequence closeKeySeq("Ctrl+w");
#if defined(USING_WEBKIT)
#include <QWebFrame>
#include <QWebElement>
#include <QWebSettings>
# 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
// it with websettings because this gives an instant redisplay, and
@ -91,6 +128,9 @@ public:
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 @@ 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("<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::trans(const string& in)
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 @@ string QtGuiResListPager::headerContent()
#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 @@ 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<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 @@ 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<DocSequence> 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("<html><body></body></html>");
#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<int,int> 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<int,int> 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<Rcl::Doc> 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<DocSequence>(),
@ -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<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 @@ 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()

View File

@ -20,6 +20,7 @@
#include "autoconfig.h"
#include <map>
#include <QPoint>
#if defined(USING_WEBENGINE)
# include <QWebEngineView>
@ -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<DocSequence> 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<int,int> 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_ */

View File

@ -21,15 +21,28 @@
#include <string>
#include <vector>
#include <sstream>
using namespace std;
#if defined(USING_WEBKIT)
#include <QWebSettings>
#include <QWebFrame>
#include <QUrl>
# include <QWebSettings>
# include <QWebFrame>
# include <QUrl>
# 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 <QWebEnginePage>
# include <QWebEngineSettings>
# include <QtWebEngineWidgets>
# define QWEBSETTINGS QWebEngineSettings
# define QWEBPAGE QWebEnginePage
#else
#include <QTextBrowser>
#endif
#include <QShortcut>
#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<Rcl::Snippet>::const_iterator it = vpabs.begin();
it != vpabs.end(); it++) {
if (it->page == -1) {
for (const auto& snippet : vpabs) {
if (snippet.page == -1) {
oss << "<tr><td colspan=\"2\">" <<
it->snippet << "</td></tr>" << endl;
snippet.snippet << "</td></tr>" << endl;
continue;
}
list<string> 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 << "<tr><td>";
if (it->page > 0) {
oss << "<a href=\"P" << it->page << "T" << it->term << "\">"
<< "P.&nbsp;" << it->page << "</a>";
if (snippet.page > 0) {
oss << "<a href=\"http://h/P" << snippet.page << "T" <<
snippet.term << "\">"
<< "P.&nbsp;" << snippet.page << "</a>";
}
oss << "</td><td>" << lr.front().c_str() << "</td></tr>" << endl;
}
@ -193,10 +218,12 @@ void SnippetsW::init()
"generator got lost in a maze...</p>"));
}
oss << "\n</body></html>";
#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");
}

View File

@ -33,26 +33,49 @@ class SnippetsW : public QWidget, public Ui::Snippets
{
Q_OBJECT
public:
SnippetsW(Rcl::Doc doc, std::shared_ptr<DocSequence> source, QWidget* parent = 0)
: QWidget(parent), m_doc(doc), m_source(source)
{
setupUi((QDialog*)this);
init();
SnippetsW(Rcl::Doc doc, std::shared_ptr<DocSequence> 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<DocSequence> m_source;
};
#ifdef USING_WEBENGINE
#include <QWebEnginePage>
// 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_ */

View File

@ -25,6 +25,7 @@
#include <stdint.h>
#include <sstream>
#include <iostream>
#include <list>
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 << "<a href=\"P" << docnumforlinks << "\">"
linksbuf << "<a href=\""<< linkPrefix()<< "P" << docnumforlinks << "\">"
<< trans("Preview") << "</a>&nbsp;&nbsp;";
}
@ -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 << "<a href=\"E" << docnumforlinks << "\">"
linksbuf << "<a href=\"" <<linkPrefix() + "E" <<docnumforlinks << "\">"
<< trans("Open") << "</a>";
}
ostringstream snipsbuf;
if (doc.haspages) {
snipsbuf << "<a href=\"A" << docnumforlinks << "\">"
snipsbuf << "<a href=\"" <<linkPrefix()<<"A" << docnumforlinks << "\">"
<< trans("Snippets") << "</a>&nbsp;&nbsp;";
linksbuf << "&nbsp;&nbsp;" << 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 << "<a href=\"D" << docnumforlinks << "\">"
<< trans("Dups") << "(" << clc << ")" << "</a>&nbsp;&nbsp;";
collpsbuf << "<a href=\""<<linkPrefix()<<"D" << docnumforlinks << "\">"
<< trans("Dups") << "(" << clc << ")" << "</a>&nbsp;&nbsp;";
linksbuf << "&nbsp;&nbsp;" << 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 << "<br style='clear:both;height:0;line-height:0;'>" << 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 << "<html><head>" << endl
<< "<meta http-equiv=\"content-type\""
<< " content=\"text/html; charset=utf-8\">" << endl
<< headerContent()
<< "</head><body>" << endl
<< pageTop()
<< "<p><span style=\"font-size:110%;\"><b>"
<< m_docSource->title()
<< "</b></span>&nbsp;&nbsp;&nbsp;";
<< "<meta http-equiv=\"content-type\""
<< " content=\"text/html; charset=utf-8\">" << endl
<< headerContent()
<< "</head><body>" << endl
<< pageTop()
<< "<p><span style=\"font-size:110%;\"><b>"
<< m_docSource->title()
<< "</b></span>&nbsp;&nbsp;&nbsp;";
if (pageEmpty()) {
chunk << trans("<p><b>No results found</b><br>");
@ -389,11 +392,11 @@ void ResListPager::displayPage(RclConfig *config)
if (o_index_stripchars) {
chunk <<
trans("<p><i>Alternate spellings (accents suppressed): </i>")
<< "<br /><blockquote>";
<< "<br /><blockquote>";
} else {
chunk <<
trans("<p><i>Alternate spellings: </i>")
<< "<br /><blockquote>";
<< "<br /><blockquote>";
}
@ -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") << " <b>" << m_winfirst + 1
<< "-" << m_winfirst + m_respage.size() << "</b> "
<< trans("out of at least") << " "
<< resCnt << " " << trans("for") << " " ;
<< "-" << m_winfirst + m_respage.size() << "</b> "
<< trans("out of at least") << " "
<< resCnt << " " << trans("for") << " " ;
} else {
chunk << trans("Documents") << " <b>"
<< m_winfirst + 1 << "-" << m_winfirst + m_respage.size()
<< "</b> " << trans("for") << " ";
<< m_winfirst + 1 << "-" << m_winfirst + m_respage.size()
<< "</b> " << trans("for") << " ";
}
}
chunk << detailsLink();
if (hasPrev() || hasNext()) {
chunk << "&nbsp;&nbsp;";
if (hasPrev()) {
chunk << "<a href=\"" + prevUrl() + "\"><b>"
<< trans("Previous")
<< "</b></a>&nbsp;&nbsp;&nbsp;";
chunk << "<a href=\"" << linkPrefix() + prevUrl() + "\"><b>"
<< trans("Previous")
<< "</b></a>&nbsp;&nbsp;&nbsp;";
}
if (hasNext()) {
chunk << "<a href=\""+ nextUrl() + "\"><b>"
<< trans("Next")
<< "</b></a>";
chunk << "<a href=\"" << linkPrefix() + nextUrl() + "\"><b>"
<< trans("Next")
<< "</b></a>";
}
}
chunk << "</p>" << endl;
@ -457,14 +460,14 @@ void ResListPager::displayPage(RclConfig *config)
chunk << "<p align=\"center\">";
if (hasPrev() || hasNext()) {
if (hasPrev()) {
chunk << "<a href=\"" + prevUrl() + "\"><b>"
<< trans("Previous")
<< "</b></a>&nbsp;&nbsp;&nbsp;";
chunk << "<a href=\"" + linkPrefix() + prevUrl() + "\"><b>"
<< trans("Previous")
<< "</b></a>&nbsp;&nbsp;&nbsp;";
}
if (hasNext()) {
chunk << "<a href=\""+ nextUrl() + "\"><b>"
<< trans("Next")
<< "</b></a>";
chunk << "<a href=\"" << linkPrefix() + nextUrl() + "\"><b>"
<< trans("Next")
<< "</b></a>";
}
}
chunk << "</p>" << endl;
@ -505,7 +508,7 @@ string ResListPager::trans(const string& in)
string ResListPager::detailsLink()
{
string chunk = "<a href=\"H-1\">";
string chunk = string("<a href=\"") + linkPrefix() + "H-1\">";
chunk += trans("(show query)") + "</a>";
return chunk;
}

View File

@ -121,6 +121,7 @@ public:
sugg.clear();
}
virtual string absSep() {return "&hellip;";}
virtual string linkPrefix() {return "";}
private:
int m_pagesize;
int m_newpagesize;