Switch to side-by-side view

--- a/src/qtgui/reslist.cpp
+++ b/src/qtgui/reslist.cpp
@@ -10,6 +10,7 @@
 #include <qapplication.h>
 #include <qvariant.h>
 #include <qevent.h>
+#include <qmenu.h>
 #include <qpushbutton.h>
 #include <qlayout.h>
 #include <qtooltip.h>
@@ -19,18 +20,8 @@
 #include <qimage.h>
 #include <qclipboard.h>
 #include <qscrollbar.h>
-
-#if (QT_VERSION < 0x040000)
-#include <qpopupmenu.h>
-#else
 #ifndef __APPLE__
 #include <qx11info_x11.h>
-#endif
-#include <q3popupmenu.h>
-#include <q3stylesheet.h>
-#include <q3mimefactory.h>
-#define QStyleSheetItem Q3StyleSheetItem
-#define QMimeSourceFactory Q3MimeSourceFactory
 #endif
 
 #include "debuglog.h"
@@ -75,367 +66,54 @@
     ResList *m_parent;
 };
 
-
-class PlainToRichQtReslist : public PlainToRich {
-public:
-    virtual ~PlainToRichQtReslist() {}
-    virtual string startMatch() {return string("<termtag>");}
-    virtual string endMatch() {return string("</termtag>");}
-};
-static PlainToRichQtReslist g_hiliter;
-
-ResList::ResList(QWidget* parent, const char* name)
-    : QTEXTBROWSER(parent, name)
-{
-    if (!name)
-	setName("resList");
-    setReadOnly(TRUE);
-    setUndoRedoEnabled(FALSE);
-    languageChange();
-
-    setTabChangesFocus(true);
-
-    (void)new HelpClient(this);
-    HelpClient::installMap(this->name(), "RCL.SEARCH.RESLIST");
-
-    // signals and slots connections
-    connect(this, SIGNAL(linkClicked(const QString &, int)), 
-	    this, SLOT(linkWasClicked(const QString &, int)));
 #if 0
-    // See comments in "highlighted
-    connect(this, SIGNAL(highlighted(const QString &)), 
-	    this, SLOT(highlighted(const QString &)));
-#endif
-    connect(this, SIGNAL(headerClicked()), this, SLOT(showQueryDetails()));
-    connect(this, SIGNAL(doubleClicked(int,int)), 
-	    this, SLOT(doubleClicked(int, int)));
-#if (QT_VERSION >= 0x040000)
-    connect(this, SIGNAL(selectionChanged()), this, SLOT(selecChanged()));
-#endif
-    m_curPvDoc = -1;
-    m_lstClckMod = 0;
-    m_listId = 0;
-    m_pager = new QtGuiResListPager(this, prefs.respagesize);
-    m_pager->setHighLighter(&g_hiliter);
-}
-
-ResList::~ResList()
-{
-    // These have to exist somewhere for translations to work
-#ifdef __GNUC__
-    __attribute__((unused))
-#endif
-    static const char* strings[] = {
-	QT_TR_NOOP("<p><b>No results found</b><br>"),
-	QT_TR_NOOP("Documents"),
-	QT_TR_NOOP("out of at least"),
-	QT_TR_NOOP("for"),
-	QT_TR_NOOP("Previous"),
-	QT_TR_NOOP("Next"),
-	QT_TR_NOOP("Unavailable document"),
-	QT_TR_NOOP("Preview"),
-	QT_TR_NOOP("Open"),
-	QT_TR_NOOP("(show query)"),
-        QT_TR_NOOP("<p><i>Alternate spellings (accents suppressed): </i>"),
-    };
-
-}
-
-int ResList::newListId()
-{
-    static int id;
-    return ++id;
-}
-
-extern "C" int XFlush(void *);
-
-void ResList::setDocSource(RefCntr<DocSequence> ndocsource)
-{
-    m_baseDocSource = ndocsource;
-    setDocSource();
-}
-
-// Reapply parameters. Sort params probably changed
-void ResList::setDocSource()
-{
-    if (m_baseDocSource.isNull())
-	return;
-    resetList();
-    m_listId = newListId();
-    m_docSource = m_baseDocSource;
-
-    // Filtering must be done before sorting, (which usually
-    // truncates the original list)
-    if (m_baseDocSource->canFilter()) {
-	m_baseDocSource->setFiltSpec(m_filtspecs);
-    } else {
-	if (m_filtspecs.isNotNull()) {
-	    string title = m_baseDocSource->title() + " (" + 
-		string((const char*)tr("filtered").utf8()) + ")";
-	    m_docSource = 
-		RefCntr<DocSequence>(new DocSeqFiltered(m_docSource,m_filtspecs,
-							title));
-	} 
-    }
-
-    if (m_sortspecs.isNotNull()) {
-	string title = m_baseDocSource->title() + " (" + 
-	    string((const char *)tr("sorted").utf8()) + ")";
-	m_docSource = RefCntr<DocSequence>(new DocSeqSorted(m_docSource, 
-							    m_sortspecs,
-							    title));
-    }
-    // Reset the page size in case the preference was changed
-    m_pager->setPageSize(prefs.respagesize);
-    m_pager->setDocSource(m_docSource);
-    resultPageNext();
-}
-
-void ResList::setSortParams(DocSeqSortSpec spec)
-{
-    LOGDEB(("ResList::setSortParams\n"));
-    m_sortspecs = spec;
-}
-
-void ResList::setFilterParams(const DocSeqFiltSpec& spec)
-{
-    LOGDEB(("ResList::setFilterParams\n"));
-    m_filtspecs = spec;
-}
-
-void ResList::resetList() 
-{
-    m_curPvDoc = -1;
-    // There should be a progress bar for long searches but there isn't 
-    // We really want the old result list to go away, otherwise, for a
-    // 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
-    clear();
-    QTEXTBROWSER::append(".");
-    clear();
-#if (QT_VERSION < 0x040000)
-    XFlush(qt_xdisplay());
+FILE *fp;
+void logdata(const char *data)
+{
+    if (fp == 0)
+	fp = fopen("/tmp/recolltoto.html", "a");
+    if (fp)
+	fprintf(fp, "%s", data);
+}
 #else
-#ifndef __APPLE__
-    XFlush(QX11Info::display());
-#endif
-#endif
-}
-
-bool ResList::displayingHistory()
-{
-    // We want to reset the displayed history if it is currently
-    // shown. Using the title value is an ugly hack
-    string htstring = string((const char *)tr("Document history").utf8());
-    if (m_docSource.isNull() || m_docSource->title().empty())
-	return false;
-    return m_docSource->title().find(htstring) == 0;
-}
-
-void ResList::languageChange()
-{
-    setCaption(tr("Result list"));
-}
-
-bool ResList::getTerms(vector<string>& terms, 
-		       vector<vector<string> >& groups, vector<int>& gslks)
-{
-    return m_baseDocSource->getTerms(terms, groups, gslks);
-}
-
-list<string> ResList::expand(Rcl::Doc& doc)
-{
-    if (m_baseDocSource.isNull())
-	return list<string>();
-    return m_baseDocSource->expand(doc);
-}
-
-// Get document number from paragraph number
-int ResList::docnumfromparnum(int par)
-{
-    if (m_pager->pageNumber() < 0)
-	return -1;
-    std::map<int,int>::iterator it = m_pageParaToReldocnums.find(par);
-    int dn;
-    if (it != m_pageParaToReldocnums.end()) {
-        dn = m_pager->pageNumber() * prefs.respagesize + it->second;
-    } else {
-        dn = -1;
-    }
-    return dn;
-}
-
-// Get paragraph number from document number
-int ResList::parnumfromdocnum(int docnum)
-{
-    if (m_pager->pageNumber() < 0)
-	return -1;
-    int winfirst = m_pager->pageNumber() * prefs.respagesize;
-    if (docnum - winfirst < 0)
-	return -1;
-    docnum -= winfirst;
-    for (std::map<int,int>::iterator it = m_pageParaToReldocnums.begin();
-	 it != m_pageParaToReldocnums.end(); it++) {
-	if (docnum == it->second)
-	    return it->first;
-    }
-    return -1;
-}
-
-// Return doc from current or adjacent result pages. We can get called
-// for a document not in the current page if the user browses through
-// results inside a result window (with shift-arrow). This can only
-// result in a one-page change.
-bool ResList::getDoc(int docnum, Rcl::Doc &doc)
-{
-    LOGDEB(("ResList::getDoc: docnum %d winfirst %d\n", docnum, 
-	    m_pager->pageNumber() * prefs.respagesize));
-    if (docnum < 0)
-	return false;
-    if (m_pager->pageNumber() < 0)
-	return false;
-    int winfirst = m_pager->pageNumber() * prefs.respagesize;
-    // Is docnum in current page ? Then all Ok
-    if (docnum >= winfirst && docnum < winfirst + int(m_curDocs.size())) {
-	doc = m_curDocs[docnum - winfirst];
-	return true;
-    }
-
-    // Else we accept to page down or up but not further
-    if (docnum < winfirst && docnum >= winfirst - prefs.respagesize) {
-	resultPageBack();
-    } else if (docnum < winfirst + int(m_curDocs.size()) + prefs.respagesize) {
-	resultPageNext();
-    }
-    winfirst = m_pager->pageNumber() * prefs.respagesize;
-    if (docnum >= winfirst && docnum < winfirst + int(m_curDocs.size())) {
-	doc = m_curDocs[docnum - winfirst];
-	return true;
-    }
-    return false;
-}
-
-void ResList::keyPressEvent(QKeyEvent * e)
-{
-    if (e->key() == Qt::Key_Q && (e->state() & Qt::ControlButton)) {
-	recollNeedsExit = 1;
-	return;
-    } else if (e->key() == Qt::Key_Prior || e->key() == Qt::Key_Backspace) {
-	resPageUpOrBack();
-	return;
-    } else if (e->key() == Qt::Key_Next || e->key() == Qt::Key_Space) {
-	resPageDownOrNext();
-	return;
-    }
-    QTEXTBROWSER::keyPressEvent(e);
-}
-
-void ResList::contentsMouseReleaseEvent(QMouseEvent *e)
-{
-    m_lstClckMod = 0;
-    if (e->state() & Qt::ControlButton) {
-	m_lstClckMod |= Qt::ControlButton;
-    } 
-    if (e->state() & Qt::ShiftButton) {
-	m_lstClckMod |= Qt::ShiftButton;
-    }
-    QTEXTBROWSER::contentsMouseReleaseEvent(e);
-}
-
-// Return total result list count
-int ResList::getResCnt()
-{
-    if (m_docSource.isNull())
-	return -1;
-    return m_docSource->getResCnt();
-}
-
-void ResList::highlighted(const QString& )
-{
-    // This is supposedly called when a link is preactivated (hover or tab
-    // traversal, but is not actually called for tabs. We would have liked to
-    // give some kind of visual feedback for tab traversal
-}
-
-
-#if 1 || (QT_VERSION < 0x040000)
-#define SCROLLYPOS contentsY()
-#else
-#define SCROLLYPOS verticalScrollBar()->value()
-#endif
-
-// Page Up/Down: we don't try to check if current paragraph is last or
-// first. We just page up/down and check if viewport moved. If it did,
-// fair enough, else we go to next/previous result page.
-void ResList::resPageUpOrBack()
-{
-    int vpos = SCROLLYPOS;
-    moveCursor(QTEXTBROWSER::MovePgUp, false);
-    if (vpos == SCROLLYPOS)
-	resultPageBack();
-}
-
-void ResList::resPageDownOrNext()
-{
-    int vpos = SCROLLYPOS;
-    moveCursor(QTEXTBROWSER::MovePgDown, false);
-    LOGDEB(("ResList::resPageDownOrNext: vpos before %d, after %d\n",
-	    vpos, SCROLLYPOS));
-    if (vpos == SCROLLYPOS) 
-	resultPageNext();
-}
-
-// Show previous page of results. We just set the current number back
-// 2 pages and show next page.
-void ResList::resultPageBack()
-{
-    m_pager->resultPageBack();
-    displayPage();
-}
-
-// Go to the first page
-void ResList::resultPageFirst()
-{
-    // In case the preference was changed
-    m_pager->setPageSize(prefs.respagesize);
-    m_pager->resultPageFirst();
-    displayPage();
-}
-
-void ResList::append(const QString &text)
-{
-    QTEXTBROWSER::append(text);
-#if 0
-    {
-	FILE *fp = fopen("/tmp/debugreslist", "a");
-	fprintf(fp, "%s\n", (const char *)text.utf8());
-	fclose(fp);
-    }
-#endif
-}
-
+#define logdata(X)
+#endif
+
+//////////////////////////////
+// /// QtGuiResListPager methods:
 bool QtGuiResListPager::append(const string& data)
 {
-    LOGDEB1(("QtGuiReslistPager::append: %s\n", data.c_str()));
+    LOGDEB2(("QtGuiReslistPager::appendString   : %s\n", data.c_str()));
+    logdata(data.c_str());
     m_parent->append(QString::fromUtf8(data.c_str()));
     return true;
 }
-bool QtGuiResListPager::append(const string& data, int i, const Rcl::Doc& doc)
-{
-    LOGDEB1(("QtGuiReslistPager::append: %d %s %s\n",
-	     i, doc.url.c_str(), doc.ipath.c_str()));
-    m_parent->setCursorPosition(0,0);
+
+bool QtGuiResListPager::append(const string& data, int docnum, 
+			       const Rcl::Doc& doc)
+{
+    LOGDEB2(("QtGuiReslistPager::appendDoc: blockCount %d, %s\n",
+	    m_parent->document()->blockCount(), data.c_str()));
+    logdata(data.c_str());
+    int blkcnt0 = m_parent->document()->blockCount();
+    m_parent->m_curDocs.push_back(doc);
+    m_parent->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
+    m_parent->textCursor().insertBlock();
+    m_parent->insertHtml(QString::fromUtf8(data.c_str()));
+    m_parent->moveCursor(QTextCursor::Start, QTextCursor::MoveAnchor);
     m_parent->ensureCursorVisible();
-    m_parent->m_pageParaToReldocnums[m_parent->paragraphs()] = i;
-    m_parent->m_curDocs.push_back(doc);
-    return append(data);
+    int blkcnt1 = m_parent->document()->blockCount();
+    for (int block = blkcnt0; block < blkcnt1; block++) {
+	m_parent->m_pageParaToReldocnums[block] = docnum;
+    }
+    return true;
 }
 
 string QtGuiResListPager::trans(const string& in)
 {
     return string((const char*)ResList::tr(in.c_str()).utf8());
 }
+
 string QtGuiResListPager::detailsLink()
 {
     string chunk = "<a href=\"H-1\">";
@@ -443,21 +121,24 @@
     chunk += "</a>";
     return chunk;
 }
+
 const string& QtGuiResListPager::parFormat()
 {
     return prefs.creslistformat;
 }
+
 string QtGuiResListPager::nextUrl()
 {
     return "n-1";
 }
+
 string QtGuiResListPager::prevUrl()
 {
     return "p-1";
 }
+
 string QtGuiResListPager::pageTop() 
 {
-    m_parent->clear();
     return string();
 }
 
@@ -500,6 +181,358 @@
 #endif
 
 }
+/////// /////// End reslistpager methods
+
+class PlainToRichQtReslist : public PlainToRich {
+public:
+    virtual ~PlainToRichQtReslist() {}
+    virtual string startMatch() {
+	return string("<span style='color: ")
+	    + string((const char *)prefs.qtermcolor.ascii()) + string("'>");
+    }
+    virtual string endMatch() {return string("</span>");}
+};
+static PlainToRichQtReslist g_hiliter;
+
+/////////////////////////////////////
+
+ResList::ResList(QWidget* parent, const char* name)
+    : QTextBrowser(parent, name)
+{
+    if (!name)
+	setName("resList");
+    setReadOnly(TRUE);
+    setUndoRedoEnabled(FALSE);
+    setOpenLinks(FALSE);
+    languageChange();
+
+    setTabChangesFocus(true);
+
+    (void)new HelpClient(this);
+    HelpClient::installMap(this->name(), "RCL.SEARCH.RESLIST");
+
+    // signals and slots connections
+    connect(this, SIGNAL(anchorClicked(const QUrl &)), 
+	    this, SLOT(linkWasClicked(const QUrl &)));
+#if 0
+    // See comments in "highlighted
+    connect(this, SIGNAL(highlighted(const QString &)), 
+	    this, SLOT(highlighted(const QString &)));
+#endif
+    connect(this, SIGNAL(headerClicked()), this, SLOT(showQueryDetails()));
+    setContextMenuPolicy(Qt::CustomContextMenu);
+    connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
+	    this, SLOT(createPopupMenu(const QPoint&)));
+
+    m_curPvDoc = -1;
+    m_lstClckMod = 0;
+    m_listId = 0;
+    m_pager = new QtGuiResListPager(this, prefs.respagesize);
+    m_pager->setHighLighter(&g_hiliter);
+}
+
+ResList::~ResList()
+{
+    // These have to exist somewhere for translations to work
+#ifdef __GNUC__
+    __attribute__((unused))
+#endif
+    static const char* strings[] = {
+	QT_TR_NOOP("<p><b>No results found</b><br>"),
+	QT_TR_NOOP("Documents"),
+	QT_TR_NOOP("out of at least"),
+	QT_TR_NOOP("for"),
+	QT_TR_NOOP("Previous"),
+	QT_TR_NOOP("Next"),
+	QT_TR_NOOP("Unavailable document"),
+	QT_TR_NOOP("Preview"),
+	QT_TR_NOOP("Open"),
+	QT_TR_NOOP("(show query)"),
+        QT_TR_NOOP("<p><i>Alternate spellings (accents suppressed): </i>"),
+    };
+}
+
+int ResList::newListId()
+{
+    static int id;
+    return ++id;
+}
+
+extern "C" int XFlush(void *);
+
+void ResList::setDocSource(RefCntr<DocSequence> ndocsource)
+{
+    m_baseDocSource = ndocsource;
+    setDocSource();
+}
+
+// Reapply parameters. Sort params probably changed
+void ResList::setDocSource()
+{
+    if (m_baseDocSource.isNull())
+	return;
+    resetList();
+    m_listId = newListId();
+    m_docSource = m_baseDocSource;
+
+    // Filtering must be done before sorting, (which usually
+    // truncates the original list)
+    if (m_baseDocSource->canFilter()) {
+	m_baseDocSource->setFiltSpec(m_filtspecs);
+    } else {
+	if (m_filtspecs.isNotNull()) {
+	    string title = m_baseDocSource->title() + " (" + 
+		string((const char*)tr("filtered").utf8()) + ")";
+	    m_docSource = 
+		RefCntr<DocSequence>(new DocSeqFiltered(m_docSource,m_filtspecs,
+							title));
+	} 
+    }
+
+    if (m_sortspecs.isNotNull()) {
+	string title = m_baseDocSource->title() + " (" + 
+	    string((const char *)tr("sorted").utf8()) + ")";
+	m_docSource = RefCntr<DocSequence>(new DocSeqSorted(m_docSource, 
+							    m_sortspecs,
+							    title));
+    }
+    // Reset the page size in case the preference was changed
+    m_pager->setPageSize(prefs.respagesize);
+    m_pager->setDocSource(m_docSource);
+    resultPageNext();
+}
+
+void ResList::setSortParams(DocSeqSortSpec spec)
+{
+    LOGDEB(("ResList::setSortParams\n"));
+    m_sortspecs = spec;
+}
+
+void ResList::setFilterParams(const DocSeqFiltSpec& spec)
+{
+    LOGDEB(("ResList::setFilterParams\n"));
+    m_filtspecs = spec;
+}
+
+void ResList::resetList() 
+{
+    m_curPvDoc = -1;
+    // There should be a progress bar for long searches but there isn't 
+    // We really want the old result list to go away, otherwise, for a
+    // 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
+    clear();
+    QTextBrowser::append(".");
+    clear();
+#ifndef __APPLE__
+    XFlush(QX11Info::display());
+#endif
+}
+
+bool ResList::displayingHistory()
+{
+    // We want to reset the displayed history if it is currently
+    // shown. Using the title value is an ugly hack
+    string htstring = string((const char *)tr("Document history").utf8());
+    if (m_docSource.isNull() || m_docSource->title().empty())
+	return false;
+    return m_docSource->title().find(htstring) == 0;
+}
+
+void ResList::languageChange()
+{
+    setCaption(tr("Result list"));
+}
+
+bool ResList::getTerms(vector<string>& terms, 
+		       vector<vector<string> >& groups, vector<int>& gslks)
+{
+    return m_baseDocSource->getTerms(terms, groups, gslks);
+}
+
+list<string> ResList::expand(Rcl::Doc& doc)
+{
+    if (m_baseDocSource.isNull())
+	return list<string>();
+    return m_baseDocSource->expand(doc);
+}
+
+// Get document number from paragraph number
+int ResList::docnumfromparnum(int par)
+{
+    if (m_pager->pageNumber() < 0)
+	return -1;
+    std::map<int,int>::iterator it;
+
+    // Try to find the first number < input and actually in the map
+    // (result blocks can be made of several text blocks)
+    while (par > 0) {
+	it = m_pageParaToReldocnums.find(par);
+	if (it != m_pageParaToReldocnums.end())
+	    break;
+	par--;
+    }
+    int dn;
+    if (it != m_pageParaToReldocnums.end()) {
+        dn = m_pager->pageNumber() * prefs.respagesize + it->second;
+    } else {
+        dn = -1;
+    }
+    return dn;
+}
+
+// Get paragraph number from document number
+pair<int,int> ResList::parnumfromdocnum(int docnum)
+{
+    LOGDEB(("parnumfromdocnum: docnum %d\n", docnum));
+    if (m_pager->pageNumber() < 0) {
+	LOGDEB(("parnumfromdocnum: no page return -1,-1\n"));
+	return pair<int,int>(-1,-1);
+    }
+    int winfirst = m_pager->pageNumber() * prefs.respagesize;
+    if (docnum - winfirst < 0) {
+	LOGDEB(("parnumfromdocnum: not in win return -1,-1\n"));
+	return pair<int,int>(-1,-1);
+    }
+    docnum -= winfirst;
+    for (std::map<int,int>::iterator it = m_pageParaToReldocnums.begin();
+	 it != m_pageParaToReldocnums.end(); it++) {
+	if (docnum == it->second) {
+	    int first = it->first;
+	    int last = first+1;
+	    std::map<int,int>::iterator it1;
+	    while ((it1 = m_pageParaToReldocnums.find(last)) != 
+		   m_pageParaToReldocnums.end() && it1->second == docnum) {
+		last++;
+	    }
+	    LOGDEB(("parnumfromdocnum: return %d,%d\n", first, last));
+	    return pair<int,int>(first, last);
+	}
+    }
+    LOGDEB(("parnumfromdocnum: not found return -1,-1\n"));
+    return pair<int,int>(-1,-1);
+}
+
+// Return doc from current or adjacent result pages. We can get called
+// for a document not in the current page if the user browses through
+// results inside a result window (with shift-arrow). This can only
+// result in a one-page change.
+bool ResList::getDoc(int docnum, Rcl::Doc &doc)
+{
+    LOGDEB(("ResList::getDoc: docnum %d winfirst %d\n", docnum, 
+	    m_pager->pageNumber() * prefs.respagesize));
+    if (docnum < 0)
+	return false;
+    if (m_pager->pageNumber() < 0)
+	return false;
+    int winfirst = m_pager->pageNumber() * prefs.respagesize;
+    // Is docnum in current page ? Then all Ok
+    if (docnum >= winfirst && docnum < winfirst + int(m_curDocs.size())) {
+	doc = m_curDocs[docnum - winfirst];
+	return true;
+    }
+
+    // Else we accept to page down or up but not further
+    if (docnum < winfirst && docnum >= winfirst - prefs.respagesize) {
+	resultPageBack();
+    } else if (docnum < winfirst + int(m_curDocs.size()) + prefs.respagesize) {
+	resultPageNext();
+    }
+    winfirst = m_pager->pageNumber() * prefs.respagesize;
+    if (docnum >= winfirst && docnum < winfirst + int(m_curDocs.size())) {
+	doc = m_curDocs[docnum - winfirst];
+	return true;
+    }
+    return false;
+}
+
+void ResList::keyPressEvent(QKeyEvent * e)
+{
+    if (e->key() == Qt::Key_Q && (e->state() & Qt::ControlButton)) {
+	recollNeedsExit = 1;
+	return;
+    } else if (e->key() == Qt::Key_Prior || e->key() == Qt::Key_Backspace) {
+	resPageUpOrBack();
+	return;
+    } else if (e->key() == Qt::Key_Next || e->key() == Qt::Key_Space) {
+	resPageDownOrNext();
+	return;
+    }
+    QTextBrowser::keyPressEvent(e);
+}
+
+void ResList::mouseReleaseEvent(QMouseEvent *e)
+{
+    m_lstClckMod = 0;
+    if (e->state() & Qt::ControlButton) {
+	m_lstClckMod |= Qt::ControlButton;
+    } 
+    if (e->state() & Qt::ShiftButton) {
+	m_lstClckMod |= Qt::ShiftButton;
+    }
+    QTextBrowser::mouseReleaseEvent(e);
+}
+
+// Return total result list count
+int ResList::getResCnt()
+{
+    if (m_docSource.isNull())
+	return -1;
+    return m_docSource->getResCnt();
+}
+
+void ResList::highlighted(const QString& )
+{
+    // This is supposedly called when a link is preactivated (hover or tab
+    // traversal, but is not actually called for tabs. We would have liked to
+    // give some kind of visual feedback for tab traversal
+}
+
+// Page Up/Down: we don't try to check if current paragraph is last or
+// first. We just page up/down and check if viewport moved. If it did,
+// fair enough, else we go to next/previous result page.
+void ResList::resPageUpOrBack()
+{
+    int vpos = verticalScrollBar()->value();
+    moveCursor(QTextBrowser::MovePgUp, false);
+    if (vpos == verticalScrollBar()->value())
+	resultPageBack();
+}
+
+void ResList::resPageDownOrNext()
+{
+    int vpos = verticalScrollBar()->value();
+    moveCursor(QTextBrowser::MovePgDown, false);
+    LOGDEB(("ResList::resPageDownOrNext: vpos before %d, after %d\n",
+	    vpos, verticalScrollBar()->value()));
+    if (vpos == verticalScrollBar()->value()) 
+	resultPageNext();
+}
+
+// Show previous page of results. We just set the current number back
+// 2 pages and show next page.
+void ResList::resultPageBack()
+{
+    m_pager->resultPageBack();
+    displayPage();
+}
+
+// Go to the first page
+void ResList::resultPageFirst()
+{
+    // In case the preference was changed
+    m_pager->setPageSize(prefs.respagesize);
+    m_pager->resultPageFirst();
+    displayPage();
+}
+
+void ResList::append(const QString &text)
+{
+    LOGDEB2(("QtGuiReslistPager::appendQString  : %s\n", 
+	    (const char*)text.utf8()));
+    QTextBrowser::append(text);
+}
 
 // Fill up result list window with next screen of hits
 void ResList::resultPageNext()
@@ -510,19 +543,10 @@
 
 void ResList::displayPage()
 {
-    // Query term colorization
-    static QStyleSheetItem *item;
-    if (!item) {
-	item = new QStyleSheetItem(styleSheet(), "termtag" );
-    }
-    if (item)
-	item->setColor(prefs.qtermcolor);
-
     m_curDocs.clear();
     m_pageParaToReldocnums.clear();
-
+    clear();
     m_pager->displayPage();
-
     LOGDEB0(("ResList::resultPageNext: hasNext %d hasPrev %d\n",
 	    m_pager->hasPrev(), m_pager->hasNext()));
     emit prevPageAvailable(m_pager->hasPrev());
@@ -538,61 +562,56 @@
     LOGDEB(("ResList::previewExposed: doc %d\n", docnum));
 
     // Possibly erase old one to white
-    int par;
-    if (m_curPvDoc != -1 && (par = parnumfromdocnum(m_curPvDoc)) != -1) {
-	QColor color("white");
-	setParagraphBackgroundColor(par, color);
+    pair<int,int> blockrange;
+    if (m_curPvDoc != -1) {
+	blockrange = parnumfromdocnum(m_curPvDoc);
+	if (blockrange.first != -1) {
+	    for (int blockn = blockrange.first;
+		 blockn < blockrange.second; blockn++) {
+		QTextBlock block = document()->findBlockByNumber(blockn);
+		QTextCursor cursor(block);
+		QTextBlockFormat format = cursor.blockFormat();
+		format.clearBackground();
+		cursor.setBlockFormat(format);
+	    }
+	}
 	m_curPvDoc = -1;
     }
+
+    // Set background for active preview's doc entry
     m_curPvDoc = docnum;
-    par = parnumfromdocnum(docnum);
+    blockrange = parnumfromdocnum(docnum);
+
     // Maybe docnum is -1 or not in this window, 
-    if (par < 0)
+    if (blockrange.first < 0)
 	return;
-
-    setCursorPosition(par, 1);
-    ensureCursorVisible();
 
     // Color the new active paragraph
     QColor color("LightBlue");
-    setParagraphBackgroundColor(par, color);
-}
-
-// SELECTION BEWARE: these emit simple text if the list format is
-// Auto. If we get back to using Rich for some reason, there will be
-// need to extract the simple text here.
-
-#if (QT_VERSION >= 0x040000)
-// I have absolutely no idea why this nonsense is needed to get
-// q3textedit to copy to x11 primary selection when text is
-// selected. This used to be automatic, and, looking at the code, it
-// should happen inside q3textedit (the code here is copied from the
-// private copyToClipboard method). To be checked again with a later
-// qt version. Seems to be needed at least up to 4.2.3
-void ResList::selecChanged()
-{
-    QClipboard *clipboard = QApplication::clipboard();
-    if (hasSelectedText()) {
-        disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), 
-		   this, 0);
-	clipboard->setText(selectedText(), QClipboard::Selection);
-        connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
-		this, SLOT(clipboardChanged()));
-    }
-}
-#else
-void ResList::selecChanged(){}
-#endif
+    for (int blockn = blockrange.first+1;
+	 blockn < blockrange.second; blockn++) {
+	QTextBlock block = document()->findBlockByNumber(blockn);
+	QTextCursor cursor(block);
+	QTextBlockFormat format;
+	format.setBackground(QBrush(color));
+	cursor.mergeBlockFormat(format);
+	setTextCursor(cursor);
+	ensureCursorVisible();
+    }
+}
+
 
 // Double click in res list: add selection to simple search
-void ResList::doubleClicked(int, int)
-{
+void ResList::mouseDoubleClickEvent(QMouseEvent *event)
+{
+    QTextBrowser::mouseDoubleClickEvent(event);
     if (hasSelectedText())
 	emit(wordSelect(selectedText()));
 }
 
-void ResList::linkWasClicked(const QString &s, int clkmod)
-{
+void ResList::linkWasClicked(const QUrl &url)
+{
+    QString s = url.toString();
     LOGDEB(("ResList::linkWasClicked: [%s]\n", s.ascii()));
     int i = atoi(s.ascii()+1) -1;
     int what = s.ascii()[0];
@@ -601,7 +620,7 @@
 	emit headerClicked(); 
 	break;
     case 'P': 
-	emit docPreviewClicked(i, clkmod); 
+	emit docPreviewClicked(i, m_lstClckMod);
 	break;
     case 'E': 
 	emit docEditClicked(i);
@@ -616,44 +635,32 @@
     }
 }
 
-RCLPOPUP *ResList::createPopupMenu(const QPoint& pos)
-{
-    LOGDEB2(("ResList::createPopupMenu(%d, %d)\n", pos.x(), pos.y()));
-    int para = paragraphAt(pos);
-    LOGDEB2(("ResList::createPopupMenu(): para %d\n", para));
-    if (para == 0) {
-        // There is a bug in qt3 paragraphAt() when the click is inside
-        // a table.  paragraphAt() calls textcursor::place() which
-        // fails because of a positioning problem inside the paragraph
-        // (the paragraph is found but place() returns an error
-        // anyway). Try to find the paragraph myself:
-        for (;para < paragraphs(); para++) {
-            QRect rect = paragraphRect(para);
-            if (pos.y() >= rect.y() && pos.y() < rect.y() + rect.height())
-                break;
-        }
-        if (para == paragraphs())
-            para = 0;
-    }
-    m_popDoc = docnumfromparnum(para);
+void ResList::createPopupMenu(const QPoint& pos)
+{
+    LOGDEB(("ResList::createPopupMenu(%d, %d)\n", pos.x(), pos.y()));
+    QTextCursor cursor = cursorForPosition(pos);
+    int blocknum = cursor.blockNumber();
+    LOGDEB(("ResList::createPopupMenu(): block %d\n", blocknum));
+    m_popDoc = docnumfromparnum(blocknum);
+
     if (m_popDoc < 0) 
-	return 0;
-    RCLPOPUP *popup = new RCLPOPUP(this);
-    popup->insertItem(tr("&Preview"), this, SLOT(menuPreview()));
-    popup->insertItem(tr("&Open"), this, SLOT(menuEdit()));
-    popup->insertItem(tr("Copy &File Name"), this, SLOT(menuCopyFN()));
-    popup->insertItem(tr("Copy &URL"), this, SLOT(menuCopyURL()));
+	return;
+    QMenu *popup = new QMenu(this);
+    popup->addAction(tr("&Preview"), this, SLOT(menuPreview()));
+    popup->addAction(tr("&Open"), this, SLOT(menuEdit()));
+    popup->addAction(tr("Copy &File Name"), this, SLOT(menuCopyFN()));
+    popup->addAction(tr("Copy &URL"), this, SLOT(menuCopyURL()));
     Rcl::Doc doc;
     if (getDoc(m_popDoc, doc) && !doc.ipath.empty()) {
-	popup->insertItem(tr("&Write to File"), this, SLOT(menuSaveToFile()));
-    }
-
-    popup->insertItem(tr("Find &similar documents"), this, SLOT(menuExpand()));
-    popup->insertItem(tr("Preview P&arent document/folder"), 
+	popup->addAction(tr("&Write to File"), this, SLOT(menuSaveToFile()));
+    }
+
+    popup->addAction(tr("Find &similar documents"), this, SLOT(menuExpand()));
+    popup->addAction(tr("Preview P&arent document/folder"), 
 		      this, SLOT(menuPreviewParent()));
-    popup->insertItem(tr("&Open Parent document/folder"), 
+    popup->addAction(tr("&Open Parent document/folder"), 
 		      this, SLOT(menuOpenParent()));
-    return popup;
+    popup->popup(mapToGlobal(pos));
 }
 
 void ResList::menuPreview()