diff --git a/src/qtgui/preview_load.cpp b/src/qtgui/preview_load.cpp new file mode 100644 index 00000000..1dd1f8ba --- /dev/null +++ b/src/qtgui/preview_load.cpp @@ -0,0 +1,75 @@ +/* Copyright (C) 2014 J.F.Dockes + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include + +#include "debuglog.h" +#include "preview_load.h" +#include "internfile.h" +#include "rcldoc.h" +#include "pathut.h" +#include "cancelcheck.h" +#include "rclconfig.h" + +LoadThread::LoadThread(RclConfig *config, const Rcl::Doc& idc, + bool pvhtm, QObject *parent) + : QThread(parent), status(1), m_idoc(idc), m_previewHtml(pvhtm), + m_config(*config) +{ + // Save log level while we're running in the calling thread. + m_loglevel = DebugLog::getdbl()->getlevel(); +} + +void LoadThread::run() +{ + DebugLog::getdbl()->setloglevel(m_loglevel); + + FileInterner interner(m_idoc, &m_config, FileInterner::FIF_forPreview); + FIMissingStore mst; + interner.setMissingStore(&mst); + + // Even when previewHtml is set, we don't set the interner's + // target mtype to html because we do want the html filter to + // do its work: we won't use the text/plain, but we want the + // text/html to be converted to utf-8 (for highlight processing) + try { + string ipath = m_idoc.ipath; + FileInterner::Status ret = interner.internfile(fdoc, ipath); + if (ret == FileInterner::FIDone || ret == FileInterner::FIAgain) { + // FIAgain is actually not nice here. It means that the record + // for the *file* of a multidoc was selected. Actually this + // shouldn't have had a preview link at all, but we don't know + // how to handle it now. Better to show the first doc than + // a mysterious error. Happens when the file name matches a + // a search term. + status = 0; + // If we prefer HTML and it is available, replace the + // text/plain document text + if (m_previewHtml && !interner.get_html().empty()) { + fdoc.text = interner.get_html(); + fdoc.mimetype = "text/html"; + } + tmpimg = interner.get_imgtmp(); + } else { + fdoc.mimetype = interner.getMimetype(); + mst.getMissingExternal(missing); + status = -1; + } + } catch (CancelExcept) { + LOGDEB(("LoadThread: cancelled\n")); + status = -1; + } +} diff --git a/src/qtgui/preview_load.h b/src/qtgui/preview_load.h new file mode 100644 index 00000000..d1e8dad4 --- /dev/null +++ b/src/qtgui/preview_load.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2015 J.F.Dockes + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _PVW_LOAD_H_INCLUDED_ +#define _PVW_LOAD_H_INCLUDED_ + +#include + +#include + +#include "rcldoc.h" +#include "pathut.h" +#include "rclconfig.h" + +/* + * A thread to perform the file reading / format conversion work for preview + */ +class LoadThread : public QThread { + + Q_OBJECT; + +public: + LoadThread(RclConfig *conf, + const Rcl::Doc& idoc, bool pvhtml, QObject *parent = 0); + + virtual ~LoadThread() { + } + + virtual void run(); + +public: + // The results are returned through public members. + int status; + Rcl::Doc fdoc; + TempFile tmpimg; + std::string missing; + +private: + Rcl::Doc m_idoc; + int m_loglevel; + bool m_previewHtml; + RclConfig m_config; +}; + + +#endif /* _PVW_LOAD_H_INCLUDED_ */ diff --git a/src/qtgui/preview_plaintorich.cpp b/src/qtgui/preview_plaintorich.cpp new file mode 100644 index 00000000..86469f0b --- /dev/null +++ b/src/qtgui/preview_plaintorich.cpp @@ -0,0 +1,187 @@ +/* Copyright (C) 2014 J.F.Dockes + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include "autoconfig.h" + +#include + +#include + +#include +#include + +#include "preview_plaintorich.h" + +#include "plaintorich.h" +#include "debuglog.h" +#include "guiutils.h" +#include "cancelcheck.h" + +using namespace std; + +PlainToRichQtPreview::PlainToRichQtPreview() +{ + clear(); +} + +void PlainToRichQtPreview::clear() +{ + m_curanchor = 1; + m_lastanchor = 0; + m_groupanchors.clear(); + m_groupcuranchors.clear(); +} + +bool PlainToRichQtPreview::haveAnchors() +{ + return m_lastanchor != 0; +} + +string PlainToRichQtPreview::PlainToRichQtPreview::header() +{ + if (!m_inputhtml) { + switch (prefs.previewPlainPre) { + case PrefsPack::PP_BR: + m_eolbr = true; + return ""; + case PrefsPack::PP_PRE: + m_eolbr = false; + return "
";
+        case PrefsPack::PP_PREWRAP:
+            m_eolbr = false;
+            return ""
+                "
";
+        }
+    }
+    return cstr_null;
+}
+
+string PlainToRichQtPreview::startMatch(unsigned int grpidx)
+{
+    LOGDEB2(("startMatch, grpidx %u\n", grpidx));
+    grpidx = m_hdata->grpsugidx[grpidx];
+    LOGDEB2(("startMatch, ugrpidx %u\n", grpidx));
+    m_groupanchors[grpidx].push_back(++m_lastanchor);
+    m_groupcuranchors[grpidx] = 0; 
+    return string("").
+        append("");
+}
+
+string  PlainToRichQtPreview::endMatch()
+{
+    return string("");
+}
+
+string  PlainToRichQtPreview::termAnchorName(int i) const
+{
+    static const char *termAnchorNameBase = "TRM";
+    char acname[sizeof(termAnchorNameBase) + 20];
+    sprintf(acname, "%s%d", termAnchorNameBase, i);
+    return string(acname);
+}
+
+string  PlainToRichQtPreview::startChunk()
+{
+    return "
";
+}
+
+int  PlainToRichQtPreview::nextAnchorNum(int grpidx)
+{
+    LOGDEB2(("nextAnchorNum: group %d\n", grpidx));
+    map::iterator curit = 
+        m_groupcuranchors.find(grpidx);
+    map >::iterator vecit = 
+        m_groupanchors.find(grpidx);
+    if (grpidx == -1 || curit == m_groupcuranchors.end() ||
+        vecit == m_groupanchors.end()) {
+        if (m_curanchor >= m_lastanchor)
+            m_curanchor = 1;
+        else
+            m_curanchor++;
+    } else {
+        if (curit->second >= vecit->second.size() -1)
+            m_groupcuranchors[grpidx] = 0;
+        else 
+            m_groupcuranchors[grpidx]++;
+        m_curanchor = vecit->second[m_groupcuranchors[grpidx]];
+        LOGDEB2(("nextAnchorNum: curanchor now %d\n", m_curanchor));
+    }
+    return m_curanchor;
+}
+
+int  PlainToRichQtPreview::prevAnchorNum(int grpidx)
+{
+    map::iterator curit = 
+        m_groupcuranchors.find(grpidx);
+    map >::iterator vecit = 
+        m_groupanchors.find(grpidx);
+    if (grpidx == -1 || curit == m_groupcuranchors.end() ||
+        vecit == m_groupanchors.end()) {
+        if (m_curanchor <= 1)
+            m_curanchor = m_lastanchor;
+        else
+            m_curanchor--;
+    } else {
+        if (curit->second <= 0)
+            m_groupcuranchors[grpidx] = vecit->second.size() -1;
+        else 
+            m_groupcuranchors[grpidx]--;
+        m_curanchor = vecit->second[m_groupcuranchors[grpidx]];
+    }
+    return m_curanchor;
+}
+
+QString  PlainToRichQtPreview::curAnchorName() const
+{
+    return QString::fromUtf8(termAnchorName(m_curanchor).c_str());
+}
+
+
+ToRichThread::ToRichThread(const string &i, const HighlightData& hd,
+                           STD_SHARED_PTR ptr,
+                           QStringList& qrichlist,
+                           QObject *parent)
+    : QThread(parent), m_input(i), m_hdata(hd), m_ptr(ptr), m_output(qrichlist)
+{
+    m_loglevel = DebugLog::getdbl()->getlevel();
+}
+
+// Insert into editor by chunks so that the top becomes visible
+// earlier for big texts. This provokes some artifacts (adds empty line),
+// so we can't set it too low.
+#define CHUNKL 500*1000
+
+void ToRichThread::run()
+{
+    DebugLog::getdbl()->setloglevel(m_loglevel);
+    list out;
+    try {
+        m_ptr->plaintorich(m_input, out, m_hdata, CHUNKL);
+    } catch (CancelExcept) {
+        return;
+    }
+
+    // Convert C++ string list to QString list
+    for (list::iterator it = out.begin(); 
+         it != out.end(); it++) {
+        m_output.push_back(QString::fromUtf8(it->c_str(), it->length()));
+    }
+}
diff --git a/src/qtgui/preview_plaintorich.h b/src/qtgui/preview_plaintorich.h
new file mode 100644
index 00000000..55bd29bd
--- /dev/null
+++ b/src/qtgui/preview_plaintorich.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2015 J.F.Dockes
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#ifndef _PREVIEW_PLAINTORICH_H_INCLUDED_
+#define _PREVIEW_PLAINTORICH_H_INCLUDED_
+#include "autoconfig.h"
+
+#include 
+#include 
+#include 
+#include MEMORY_INCLUDE
+
+#include 
+#include 
+
+#include "plaintorich.h"
+
+/** Preview text highlighter */
+class PlainToRichQtPreview : public PlainToRich {
+public:
+    PlainToRichQtPreview();
+    void clear();
+    bool haveAnchors();
+    virtual string header();
+    virtual string startMatch(unsigned int grpidx);
+    virtual string endMatch();
+    virtual string termAnchorName(int i) const;
+    virtual string startChunk();
+    int nextAnchorNum(int grpidx);
+    int prevAnchorNum(int grpidx);
+    QString curAnchorName() const;
+
+private:
+    int m_curanchor;
+    int m_lastanchor;
+    // Lists of anchor numbers (match locations) for the term (groups)
+    // in the query (the map key is and index into HighlightData.groups).
+    std::map > m_groupanchors;
+    std::map m_groupcuranchors;
+};
+
+/* A thread to convert to rich text (mark search terms) */
+class ToRichThread : public QThread {
+    Q_OBJECT;
+    
+public:
+    ToRichThread(const string &i, const HighlightData& hd,
+                 STD_SHARED_PTR ptr,
+                 QStringList& qrichlst, // Output
+                 QObject *parent = 0);
+    virtual void run();
+
+private:
+    int m_loglevel;
+    const string &m_input;
+    const HighlightData &m_hdata;
+    STD_SHARED_PTR m_ptr;
+    QStringList &m_output;
+};
+
+#endif /* _PREVIEW_PLAINTORICH_H_INCLUDED_ */
diff --git a/src/qtgui/preview_w.cpp b/src/qtgui/preview_w.cpp
index 7b34ccc8..5aeb1aec 100644
--- a/src/qtgui/preview_w.cpp
+++ b/src/qtgui/preview_w.cpp
@@ -49,7 +49,6 @@
 #include "pathut.h"
 #include "internfile.h"
 #include "recoll.h"
-#include "plaintorich.h"
 #include "smallut.h"
 #include "chrono.h"
 #include "wipedir.h"
@@ -58,6 +57,8 @@
 #include "guiutils.h"
 #include "docseqhist.h"
 #include "rclhelp.h"
+#include "preview_load.h"
+#include "preview_plaintorich.h"
 
 static const QKeySequence closeKS(Qt::Key_Escape);
 static const QKeySequence nextDocInTabKS(Qt::ShiftModifier+Qt::Key_Down);
@@ -65,129 +66,6 @@ static const QKeySequence prevDocInTabKS(Qt::ShiftModifier+Qt::Key_Up);
 static const QKeySequence closeTabKS(Qt::ControlModifier+Qt::Key_W);
 static const QKeySequence printTabKS(Qt::ControlModifier+Qt::Key_P);
 
-// Subclass plainToRich to add s and anchors to the preview text
-class PlainToRichQtPreview : public PlainToRich {
-public:
-
-    PlainToRichQtPreview()
-        : m_curanchor(1), m_lastanchor(0) {
-    }    
-    void clear() {
-        m_curanchor = 1; 
-        m_lastanchor = 0;
-        m_groupanchors.clear();
-        m_groupcuranchors.clear();
-    }
-
-    bool haveAnchors() {
-        return m_lastanchor != 0;
-    }
-
-    virtual string header() {
-        if (!m_inputhtml) {
-            switch (prefs.previewPlainPre) {
-            case PrefsPack::PP_BR:
-                m_eolbr = true;
-                return "";
-            case PrefsPack::PP_PRE:
-                m_eolbr = false;
-                return "
";
-            case PrefsPack::PP_PREWRAP:
-                m_eolbr = false;
-                return ""
-                    "
";
-            }
-        }
-        return cstr_null;
-    }
-
-    virtual string startMatch(unsigned int grpidx) {
-        LOGDEB2(("startMatch, grpidx %u\n", grpidx));
-        grpidx = m_hdata->grpsugidx[grpidx];
-        LOGDEB2(("startMatch, ugrpidx %u\n", grpidx));
-        m_groupanchors[grpidx].push_back(++m_lastanchor);
-        m_groupcuranchors[grpidx] = 0; 
-        return string("").
-            append("");
-    }
-
-    virtual string endMatch() {
-        return string("");
-    }
-
-    virtual string termAnchorName(int i) const {
-        static const char *termAnchorNameBase = "TRM";
-        char acname[sizeof(termAnchorNameBase) + 20];
-        sprintf(acname, "%s%d", termAnchorNameBase, i);
-        return string(acname);
-    }
-
-    virtual string startChunk() { 
-        return "
";
-    }
-
-    int nextAnchorNum(int grpidx) {
-        LOGDEB2(("nextAnchorNum: group %d\n", grpidx));
-        map::iterator curit = 
-            m_groupcuranchors.find(grpidx);
-        map >::iterator vecit = 
-            m_groupanchors.find(grpidx);
-        if (grpidx == -1 || curit == m_groupcuranchors.end() ||
-            vecit == m_groupanchors.end()) {
-            if (m_curanchor >= m_lastanchor)
-                m_curanchor = 1;
-            else
-                m_curanchor++;
-        } else {
-            if (curit->second >= vecit->second.size() -1)
-                m_groupcuranchors[grpidx] = 0;
-            else 
-                m_groupcuranchors[grpidx]++;
-            m_curanchor = vecit->second[m_groupcuranchors[grpidx]];
-            LOGDEB2(("nextAnchorNum: curanchor now %d\n", m_curanchor));
-        }
-        return m_curanchor;
-    }
-
-    int prevAnchorNum(int grpidx) {
-        map::iterator curit = 
-            m_groupcuranchors.find(grpidx);
-        map >::iterator vecit = 
-            m_groupanchors.find(grpidx);
-        if (grpidx == -1 || curit == m_groupcuranchors.end() ||
-            vecit == m_groupanchors.end()) {
-            if (m_curanchor <= 1)
-                m_curanchor = m_lastanchor;
-            else
-                m_curanchor--;
-        } else {
-            if (curit->second <= 0)
-                m_groupcuranchors[grpidx] = vecit->second.size() -1;
-            else 
-                m_groupcuranchors[grpidx]--;
-            m_curanchor = vecit->second[m_groupcuranchors[grpidx]];
-        }
-        return m_curanchor;
-    }
-
-    QString curAnchorName() const {
-        return QString::fromUtf8(termAnchorName(m_curanchor).c_str());
-    }
-
-private:
-    int m_curanchor;
-    int m_lastanchor;
-    // Lists of anchor numbers (match locations) for the term (groups)
-    // in the query (the map key is and index into HighlightData.groups).
-    map > m_groupanchors;
-    map m_groupcuranchors;
-};
-
 void Preview::init()
 {
     setObjectName("Preview");
@@ -713,91 +591,15 @@ void Preview::emitWordSelect(QString word)
   beginning of the text displayed faster
 */
 
-/* A thread to to the file reading / format conversion */
-class LoadThread : public QThread {
-    int *statusp;
-    Rcl::Doc& out;
-    const Rcl::Doc& idoc;
-    int loglevel;
-public: 
-    string missing;
-    TempFile imgtmp;
-
-    LoadThread(int *stp, Rcl::Doc& odoc, const Rcl::Doc& idc) 
-        : statusp(stp), out(odoc), idoc(idc)
-        {
-            loglevel = DebugLog::getdbl()->getlevel();
-        }
-    ~LoadThread() {
-    }
-    virtual void run() {
-        DebugLog::getdbl()->setloglevel(loglevel);
-        FileInterner interner(idoc, theconfig, FileInterner::FIF_forPreview);
-        FIMissingStore mst;
-        interner.setMissingStore(&mst);
-        // Even when previewHtml is set, we don't set the interner's
-        // target mtype to html because we do want the html filter to
-        // do its work: we won't use the text/plain, but we want the
-        // text/html to be converted to utf-8 (for highlight processing)
-        try {
-            string ipath = idoc.ipath;
-            FileInterner::Status ret = interner.internfile(out, ipath);
-            if (ret == FileInterner::FIDone || ret == FileInterner::FIAgain) {
-                // FIAgain is actually not nice here. It means that the record
-                // for the *file* of a multidoc was selected. Actually this
-                // shouldn't have had a preview link at all, but we don't know
-                // how to handle it now. Better to show the first doc than
-                // a mysterious error. Happens when the file name matches a
-                // a search term.
-                *statusp = 0;
-                // If we prefer html and it is available, replace the
-                // text/plain document text
-                if (prefs.previewHtml && !interner.get_html().empty()) {
-                    out.text = interner.get_html();
-                    out.mimetype = "text/html";
-                }
-                imgtmp = interner.get_imgtmp();
-            } else {
-                out.mimetype = interner.getMimetype();
-                mst.getMissingExternal(missing);
-                *statusp = -1;
-            }
-        } catch (CancelExcept) {
-            *statusp = -1;
-        }
-    }
-};
-
 
 // Insert into editor by chunks so that the top becomes visible
 // earlier for big texts. This provokes some artifacts (adds empty line),
 // so we can't set it too low.
 #define CHUNKL 500*1000
 
-/* A thread to convert to rich text (mark search terms) */
-class ToRichThread : public QThread {
-    string ∈
-    const HighlightData &hdata;
-    list &out;
-    int loglevel;
-    PlainToRichQtPreview *ptr;
-public:
-    ToRichThread(string &i, const HighlightData& hd, list &o, 
-                 PlainToRichQtPreview *_ptr)
-        : in(i), hdata(hd), out(o), ptr(_ptr)
-        {
-            loglevel = DebugLog::getdbl()->getlevel();
-        }
-    virtual void run()
-        {
-            DebugLog::getdbl()->setloglevel(loglevel);
-            try {
-                ptr->plaintorich(in, out, hdata, CHUNKL);
-            } catch (CancelExcept) {
-            }
-        }
-};
-
+// Make sure we don't ever reenter loadDocInCurrentTab: note that I
+// don't think it's actually possible, this must be the result of a
+// misguided debug session.
 class LoadGuard {
     bool *m_bp;
 public:
@@ -829,9 +631,7 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
     // Load and convert document
     // idoc came out of the index data (main text and some fields missing). 
     // fdoc is the complete one what we are going to extract from storage.
-    Rcl::Doc fdoc;
-    int status = 1;
-    LoadThread lthr(&status, fdoc, idoc);
+    LoadThread lthr(theconfig, idoc, prefs.previewHtml, this);
     connect(<hr, SIGNAL(finished()), &loop, SLOT(quit()));
 
     lthr.start();
@@ -843,17 +643,18 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
         if (progress.wasCanceled()) {
             CancelCheck::instance().setCancel();
         }
-        if (i == 2)
+        if (i == 1)
             progress.show();
     }
 
     LOGDEB(("loadDocInCurrentTab: after file load: cancel %d status %d"
             " text length %d\n", 
-            CancelCheck::instance().cancelState(), status, fdoc.text.length()));
+            CancelCheck::instance().cancelState(), lthr.status,
+            lthr.fdoc.text.length()));
 
     if (CancelCheck::instance().cancelState())
         return false;
-    if (status != 0) {
+    if (lthr.status != 0) {
         progress.close();
         QString explain;
         if (!lthr.missing.empty()) {
@@ -863,10 +664,14 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
             QMessageBox::warning(0, "Recoll",
                                  tr("Can't turn doc into internal "
                                     "representation for ") +
-                                 fdoc.mimetype.c_str() + explain);
+                                 lthr.fdoc.mimetype.c_str() + explain);
         } else {
-            QMessageBox::warning(0, "Recoll", 
-                                 tr("Error while loading file"));
+            if (progress.wasCanceled()) {
+                //QMessageBox::warning(0, "Recoll", tr("Canceled"));
+            } else {
+                QMessageBox::warning(0, "Recoll", 
+                                     tr("Error while loading file"));
+            }
         }
 
         return false;
@@ -879,12 +684,11 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
     // We don't do the highlighting for very big texts: too long. We
     // should at least do special char escaping, in case a '&' or '<'
     // somehow slipped through previous processing.
-    bool highlightTerms = fdoc.text.length() < 
+    bool highlightTerms = lthr.fdoc.text.length() < 
         (unsigned long)prefs.maxhltextmbs * 1024 * 1024;
 
     // Final text is produced in chunks so that we can display the top
     // while still inserting at bottom
-    list qrichlst;
     PreviewTextEdit *editor = currentEditor();
 
     editor->m_plaintorich->clear();
@@ -906,33 +710,24 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
 
     editor->setHtml("");
     editor->m_format = Qt::RichText;
-    bool inputishtml = !fdoc.mimetype.compare("text/html");
-
-#if 0
-    // For testing qtextedit bugs...
-    highlightTerms = true;
-    const char *textlist[] =
-        {
-            "Du plain text avec un\n termtag fin de ligne:",
-            "texte apres le tag\n",
-        };
-    const int listl = sizeof(textlist) / sizeof(char*);
-    for (int i = 0 ; i < listl ; i++)
-        qrichlst.push_back(QString::fromUtf8(textlist[i]));
-#else
+    bool inputishtml = !lthr.fdoc.mimetype.compare("text/html");
+    QStringList qrichlst;
+    
+#if 1
     if (highlightTerms) {
         progress.setLabelText(tr("Creating preview text"));
         qApp->processEvents();
 
         if (inputishtml) {
-            LOGDEB1(("Preview: got html %s\n", fdoc.text.c_str()));
+            LOGDEB1(("Preview: got html %s\n", lthr.fdoc.text.c_str()));
             editor->m_plaintorich->set_inputhtml(true);
         } else {
-            LOGDEB1(("Preview: got plain %s\n", fdoc.text.c_str()));
+            LOGDEB1(("Preview: got plain %s\n", lthr.fdoc.text.c_str()));
             editor->m_plaintorich->set_inputhtml(false);
         }
-        list richlst;
-        ToRichThread rthr(fdoc.text, m_hData, richlst, editor->m_plaintorich);
+
+        ToRichThread rthr(lthr.fdoc.text, m_hData, editor->m_plaintorich,
+                          qrichlst, this);
         connect(&rthr, SIGNAL(finished()), &loop, SLOT(quit()));
         rthr.start();
 
@@ -948,22 +743,18 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
 
         // Conversion to rich text done
         if (CancelCheck::instance().cancelState()) {
-            if (richlst.size() == 0 || richlst.front().length() == 0) {
+            if (qrichlst.size() == 0 || qrichlst.front().size() == 0) {
                 // We can't call closeCurrentTab here as it might delete
                 // the object which would be a nasty surprise to our
                 // caller.
                 return false;
             } else {
-                richlst.back() += "Cancelled !";
+                qrichlst.back() += "Cancelled !";
             }
         }
-        // Convert C++ string list to QString list
-        for (list::iterator it = richlst.begin(); 
-             it != richlst.end(); it++) {
-            qrichlst.push_back(QString::fromUtf8(it->c_str(), it->length()));
-        }
     } else {
-        LOGDEB(("Preview: no hilighting\n"));
+        LOGDEB(("Preview: no hilighting, loading %d bytes\n",
+                int(lthr.fdoc.text.size())));
         // No plaintorich() call.  In this case, either the text is
         // html and the html quoting is hopefully correct, or it's
         // plain-text and there is no need to escape special
@@ -971,7 +762,8 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
         // top is displayed faster), but we must not cut tags, and
         // it's too difficult on html. For text we do the splitting on
         // a QString to avoid utf8 issues.
-        QString qr = QString::fromUtf8(fdoc.text.c_str(), fdoc.text.length());
+        QString qr = QString::fromUtf8(lthr.fdoc.text.c_str(),
+                                       lthr.fdoc.text.length());
         int l = 0;
         if (inputishtml) {
             qrichlst.push_back(qr);
@@ -984,6 +776,16 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
             }
         }
     }
+#else // For testing qtextedit bugs...
+    highlightTerms = true;
+    const char *textlist[] =
+        {
+            "Du plain text avec un\n termtag fin de ligne:",
+            "texte apres le tag\n",
+        };
+    const int listl = sizeof(textlist) / sizeof(char*);
+    for (int i = 0 ; i < listl ; i++)
+        qrichlst.push_back(QString::fromUtf8(textlist[i]));
 #endif
 
 
@@ -991,7 +793,7 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
     // Load text into editor window.
     progress.setLabelText(tr("Loading preview text into editor"));
     qApp->processEvents();
-    for (list::iterator it = qrichlst.begin(); 
+    for (QStringList::iterator it = qrichlst.begin();
          it != qrichlst.end(); it++) {
         qApp->processEvents();
 
@@ -1016,10 +818,10 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
     // Maybe the text was actually empty ? Switch to fields then. Else free-up 
     // the text memory in the loaded document. We still have a copy of the text
     // in editor->m_richtxt
-    bool textempty = fdoc.text.empty();
+    bool textempty = lthr.fdoc.text.empty();
     if (!textempty)
-        fdoc.text.clear(); 
-    editor->m_fdoc = fdoc;
+        lthr.fdoc.text.clear(); 
+    editor->m_fdoc = lthr.fdoc;
     editor->m_dbdoc = idoc;
     if (textempty)
         editor->displayFields();
@@ -1032,7 +834,7 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
         // there is an ipath that it won't understand, we need a temp file:
         theconfig->setKeyDir(path_getfather(fn));
         if (fn.empty() || !idoc.ipath.empty()) {
-            TempFile temp = lthr.imgtmp;
+            TempFile temp = lthr.tmpimg;
             if (temp) {
                 LOGDEB1(("Preview: load: got temp file from internfile\n"));
             } else if (!FileInterner::idocToFile(temp, string(), 
@@ -1089,8 +891,8 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
 }
 
 PreviewTextEdit::PreviewTextEdit(QWidget* parent, const char* nm, Preview *pv) 
-    : QTextBrowser(parent), m_preview(pv), 
-      m_plaintorich(new PlainToRichQtPreview()), 
+    : QTextBrowser(parent), m_preview(pv),
+      m_plaintorich(new PlainToRichQtPreview()),
       m_dspflds(false), m_docnum(-1) 
 {
     setContextMenuPolicy(Qt::CustomContextMenu);
@@ -1101,11 +903,6 @@ PreviewTextEdit::PreviewTextEdit(QWidget* parent, const char* nm, Preview *pv)
     setOpenLinks(false);
 }
 
-PreviewTextEdit::~PreviewTextEdit()
-{
-    delete m_plaintorich;
-}
-
 void PreviewTextEdit::createPopupMenu(const QPoint& pos)
 {
     LOGDEB1(("PreviewTextEdit::createPopupMenu()\n"));
diff --git a/src/qtgui/preview_w.h b/src/qtgui/preview_w.h
index 008dd5e1..de682e38 100644
--- a/src/qtgui/preview_w.h
+++ b/src/qtgui/preview_w.h
@@ -56,7 +56,6 @@ class PreviewTextEdit : public PREVIEW_PARENTCLASS {
     Q_OBJECT;
 public:
     PreviewTextEdit(QWidget* parent, const char* name, Preview *pv);
-    virtual ~PreviewTextEdit();
     void moveToAnchor(const QString& name);
     enum DspType {PTE_DSPTXT, PTE_DSPFLDS, PTE_DSPIMG};
 
@@ -74,7 +73,7 @@ protected:
 
 private:
     Preview *m_preview;
-    PlainToRichQtPreview *m_plaintorich;
+    STD_SHARED_PTR m_plaintorich;
     
     bool   m_dspflds;
     string m_url; // filename for this tab
diff --git a/src/qtgui/recoll.pro.in b/src/qtgui/recoll.pro.in
index aaea607b..56f688f0 100644
--- a/src/qtgui/recoll.pro.in
+++ b/src/qtgui/recoll.pro.in
@@ -26,6 +26,8 @@ HEADERS += \
         idxsched.h \
         listdialog.h \
         preview_w.h \
+        preview_load.h \
+        preview_plaintorich.h \
         ptrans_w.h \
         rclhelp.h \
         rclmain_w.h \
@@ -52,6 +54,8 @@ SOURCES += \
         main.cpp \
         multisave.cpp \
         preview_w.cpp \
+        preview_load.cpp \
+        preview_plaintorich.cpp \
         ptrans_w.cpp \
         rclhelp.cpp \
         rclmain_w.cpp \