diff --git a/src/VERSION b/src/VERSION index feaae22b..105be7fc 100644 --- a/src/VERSION +++ b/src/VERSION @@ -1 +1 @@ -1.13.0 +20091128 diff --git a/src/internfile/mimehandler.cpp b/src/internfile/mimehandler.cpp index 47b05ac3..557e5832 100644 --- a/src/internfile/mimehandler.cpp +++ b/src/internfile/mimehandler.cpp @@ -50,16 +50,24 @@ static Dijon::Filter *mhFactory(const string &mime) { string lmime(mime); stringtolower(lmime); - if ("text/plain" == lmime) + if ("text/plain" == lmime) { return new MimeHandlerText(lmime); - else if ("text/html" == lmime) + } else if ("text/html" == lmime) { return new MimeHandlerHtml(lmime); - else if ("text/x-mail" == lmime) + } else if ("text/x-mail" == lmime) { return new MimeHandlerMbox(lmime); - else if ("message/rfc822" == lmime) + } else if ("message/rfc822" == lmime) { return new MimeHandlerMail(lmime); - else + } else if (lmime.find("text/") == 0) { + // Try to handle unknown text/xx as text/plain. This + // only happen if the text/xx was defined as "internal" in + // mimeconf, not at random. For programs, for example this + // allows indexing and previewing as text/plain (no filter + // exec) but still opening with a specific editor. + return new MimeHandlerText(lmime); + } else { return new MimeHandlerUnknown(lmime); + } } /** diff --git a/src/kde/kioslave/recoll/htmlif.cpp b/src/kde/kioslave/recoll/htmlif.cpp index b122a614..af1e7b16 100644 --- a/src/kde/kioslave/recoll/htmlif.cpp +++ b/src/kde/kioslave/recoll/htmlif.cpp @@ -223,7 +223,7 @@ public: "") + m_name + - string("

"); + string("

");
 	}
     }
     virtual string startMatch() {return string("");}
diff --git a/src/qtgui/preview_w.cpp b/src/qtgui/preview_w.cpp
index 0943d855..cf090b87 100644
--- a/src/qtgui/preview_w.cpp
+++ b/src/qtgui/preview_w.cpp
@@ -99,6 +99,10 @@ using std::pair;
 
 #include "rclhelp.h"
 
+#ifndef MIN
+#define MIN(A,B) ((A)<(B)?(A):(B))
+#endif
+
 // QTextEdit's scrollToAnchor() is supposed to make the anchor visible, but
 // actually, it only moves to the top of the paragraph containing the anchor.
 // As we only have one paragraph, this doesnt' help a lot (qt3 and qt4)
@@ -111,6 +115,7 @@ using std::pair;
 // even installed under qt4. We use a local copy, which is not nice.
 void PreviewTextEdit::moveToAnchor(const QString& name)
 {
+    LOGDEB0(("PreviewTextEdit::moveToAnchor\n"));
     if (name.isEmpty())
 	return;
     sync();
@@ -245,10 +250,11 @@ void Preview::closeEvent(QCloseEvent *e)
 
 bool Preview::eventFilter(QObject *target, QEvent *event)
 {
+    LOGDEB2(("Preview::eventFilter()\n"));
     if (event->type() != QEvent::KeyPress) 
 	return false;
     
-    LOGDEB1(("Preview::eventFilter: keyEvent\n"));
+    LOGDEB2(("Preview::eventFilter: keyEvent\n"));
 
     PreviewTextEdit *edit = currentEditor();
     QKeyEvent *keyEvent = (QKeyEvent *)event;
@@ -309,7 +315,7 @@ bool Preview::eventFilter(QObject *target, QEvent *event)
 
 void Preview::searchTextLine_textChanged(const QString & text)
 {
-    LOGDEB1(("search line text changed. text: '%s'\n", text.ascii()));
+    LOGDEB2(("search line text changed. text: '%s'\n", text.ascii()));
     if (text.isEmpty()) {
 	m_dynSearchActive = false;
 	//	nextButton->setEnabled(false);
@@ -331,6 +337,7 @@ void Preview::searchTextLine_textChanged(const QString & text)
 
 PreviewTextEdit *Preview::currentEditor()
 {
+    LOGDEB2(("Preview::currentEditor()\n"));
     QWidget *tw = pvTab->currentPage();
     PreviewTextEdit *edit = 0;
     if (tw) {
@@ -347,8 +354,8 @@ void Preview::doSearch(const QString &_text, bool next, bool reverse,
 		       bool wordOnly)
 {
     LOGDEB(("Preview::doSearch: text [%s] txtlen %d next %d rev %d word %d\n", 
-	    (const char *)_text.utf8(), _text.length(), int(next), 
-	    int(reverse), int(wordOnly)));
+             (const char *)_text.utf8(), _text.length(), int(next), 
+             int(reverse), int(wordOnly)));
     QString text = _text;
 
     bool matchCase = matchCheck->isChecked();
@@ -393,10 +400,11 @@ void Preview::doSearch(const QString &_text, bool next, bool reverse,
 	LOGDEB(("Preview::doSearch: setting cursor to %d %d\n", ps, is));
 	edit->setCursorPosition(ps, is);
     }
-
+    Chrono chron;
     LOGDEB(("Preview::doSearch: first find call\n"));
     bool found = edit->find(text, matchCase, wordOnly, !reverse, 0, 0);
-    LOGDEB(("Preview::doSearch: first find call return\n"));
+    LOGDEB(("Preview::doSearch: first find call return: %.2f S\n", 
+            chron.secs()));
     // If not found, try to wrap around. 
     if (!found && next) { 
 	LOGDEB(("Preview::doSearch: wrapping around\n"));
@@ -408,8 +416,10 @@ void Preview::doSearch(const QString &_text, bool next, bool reverse,
 	    mspara = msindex = 0;
 	}
 	LOGDEB(("Preview::doSearch: 2nd find call\n"));
+        chron.restart();
 	found = edit->find(text,matchCase, false, !reverse, &mspara, &msindex);
-	LOGDEB(("Preview::doSearch: 2nd find call return\n"));
+	LOGDEB(("Preview::doSearch: 2nd find call return %.2f S\n",
+                chron.secs()));
     }
 
     if (found) {
@@ -424,17 +434,20 @@ void Preview::doSearch(const QString &_text, bool next, bool reverse,
 
 void Preview::nextPressed()
 {
+    LOGDEB2(("PreviewTextEdit::nextPressed\n"));
     doSearch(searchTextLine->text(), true, false);
 }
 
 void Preview::prevPressed()
 {
+    LOGDEB2(("PreviewTextEdit::prevPressed\n"));
     doSearch(searchTextLine->text(), true, true);
 }
 
 // Called when user clicks on tab
 void Preview::currentChanged(QWidget * tw)
 {
+    LOGDEB2(("PreviewTextEdit::currentChanged\n"));
     PreviewTextEdit *edit = 
         dynamic_cast(tw->child("pvEdit"));
     m_currentW = tw;
@@ -471,7 +484,7 @@ void Preview::currentChanged(QWidget * tw)
 // qt version.
 void Preview::selecChanged()
 {
-    LOGDEB1(("Selection changed\n"));
+    LOGDEB1(("Preview::selecChanged\n"));
     if (!m_currentW)
 	return;
     PreviewTextEdit *edit = (PreviewTextEdit*)m_currentW->child("pvEdit");
@@ -511,7 +524,7 @@ void Preview::textDoubleClicked(int, int)
 
 void Preview::closeCurrentTab()
 {
-    LOGDEB(("Preview::closeCurrentTab: m_loading %d\n", m_loading));
+    LOGDEB1(("Preview::closeCurrentTab: m_loading %d\n", m_loading));
     if (m_loading) {
 	CancelCheck::instance().setCancel();
 	return;
@@ -528,6 +541,7 @@ void Preview::closeCurrentTab()
 
 PreviewTextEdit *Preview::addEditorTab()
 {
+    LOGDEB1(("PreviewTextEdit::addEditorTab()\n"));
     QWidget *anon = new QWidget((QWidget *)pvTab);
     QVBoxLayout *anonLayout = new QVBoxLayout(anon, 1, 1, "anonLayout"); 
     PreviewTextEdit *editor = new PreviewTextEdit(anon, "pvEdit", this);
@@ -541,6 +555,7 @@ PreviewTextEdit *Preview::addEditorTab()
 
 void Preview::setCurTabProps(const Rcl::Doc &doc, int docnum)
 {
+    LOGDEB1(("PreviewTextEdit::setCurTabProps\n"));
     QString title;
     map::const_iterator meta_it;
     if ((meta_it = doc.meta.find(Rcl::Doc::keytt)) != doc.meta.end()
@@ -705,6 +720,11 @@ class LoadThread : public QThread {
     }
 };
 
+// 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 ∈
@@ -723,7 +743,7 @@ class ToRichThread : public QThread {
     {
 	DebugLog::getdbl()->setloglevel(loglevel);
 	try {
-	    ptr.plaintorich(in, out, hdata);
+	    ptr.plaintorich(in, out, hdata, CHUNKL);
 	} catch (CancelExcept) {
 	}
     }
@@ -739,10 +759,6 @@ class WaiterThread : public QThread {
     }
 };
 
-#define CHUNKL 50*1000
-#ifndef MIN
-#define MIN(A,B) ((A)<(B)?(A):(B))
-#endif
 class LoadGuard {
     bool *m_bp;
 public:
@@ -752,6 +768,7 @@ public:
 
 bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
 {
+    LOGDEB1(("PreviewTextEdit::loadDocInCurrentTab()\n"));
     if (m_loading) {
 	LOGERR(("ALready loading\n"));
 	return false;
@@ -828,14 +845,36 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
     // Final text is produced in chunks so that we can display the top
     // while still inserting at bottom
     list qrichlst;
+    PreviewTextEdit *editor = currentEditor();
+    editor->setText("");
+    editor->setTextFormat(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
     if (highlightTerms) {
+	QStyleSheetItem *item = 
+	    new QStyleSheetItem(editor->styleSheet(), "termtag" );
+	item->setColor(prefs.qtermcolor);
+	item->setFontWeight(QFont::Bold);
 	progress.setLabelText(tr("Creating preview text"));
 	qApp->processEvents();
+
 	if (inputishtml) {
 	    LOGDEB1(("Preview: got html %s\n", fdoc.text.c_str()));
 	    m_plaintorich.set_inputhtml(true);
 	} else {
+	    LOGDEB1(("Preview: got plain %s\n", fdoc.text.c_str()));
 	    m_plaintorich.set_inputhtml(false);
 	}
 	list richlst;
@@ -885,22 +924,14 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
 	if (inputishtml) {
 	    qrichlst.push_back(qr);
 	} else {
+            editor->setTextFormat(Qt::PlainText);
 	    for (int pos = 0; pos < (int)qr.length(); pos += l) {
 		l = MIN(CHUNKL, qr.length() - pos);
 		qrichlst.push_back(qr.mid(pos, l));
 	    }
 	}
     }
-	    
-    // Load into editor
-    PreviewTextEdit *editor = currentEditor();
-    editor->setText("");
-    if (highlightTerms) {
-	QStyleSheetItem *item = 
-	    new QStyleSheetItem(editor->styleSheet(), "termtag" );
-	item->setColor(prefs.qtermcolor);
-	item->setFontWeight(QFont::Bold);
-    }
+#endif
 
     prog = 2 * nsteps / 3;
     progress.setLabelText(tr("Loading preview text into editor"));
@@ -969,6 +1000,7 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum)
 
 RCLPOPUP *PreviewTextEdit::createPopupMenu(const QPoint&)
 {
+    LOGDEB1(("PreviewTextEdit::createPopupMenu()\n"));
     RCLPOPUP *popup = new RCLPOPUP(this);
     if (!m_dspflds) {
 	popup->insertItem(tr("Show fields"), this, SLOT(toggleFields()));
@@ -982,7 +1014,7 @@ RCLPOPUP *PreviewTextEdit::createPopupMenu(const QPoint&)
 // Either display document fields or main text
 void PreviewTextEdit::toggleFields()
 {
-//    fprintf(stderr, "%s", (const char *)text().ascii());
+    LOGDEB1(("PreviewTextEdit::toggleFields()\n"));
 
     // If currently displaying fields, switch to body text
     if (m_dspflds) {
@@ -1010,6 +1042,7 @@ void PreviewTextEdit::toggleFields()
 
 void PreviewTextEdit::print()
 {
+    LOGDEB1(("PreviewTextEdit::print\n"));
     if (!m_preview)
         return;
 	
diff --git a/src/qtgui/preview_w.h b/src/qtgui/preview_w.h
index 7de6b972..997b8cbb 100644
--- a/src/qtgui/preview_w.h
+++ b/src/qtgui/preview_w.h
@@ -98,7 +98,7 @@ public:
 	if (m_inputhtml) {
 	    return snull;
 	} else {
-	    return string("

"); + return string("

");
 	}
     }
     virtual string startMatch() {return string("");}
@@ -115,6 +115,10 @@ public:
     virtual string startAnchor(int i) {
 	return string("";
     }
+    virtual string endAnchor() {
+	return string("");
+    }
+    virtual string startChunk() { return "
";}
 };
 
 class Preview : public QWidget {
diff --git a/src/query/plaintorich.cpp b/src/query/plaintorich.cpp
index 9e635688..d56351f9 100644
--- a/src/query/plaintorich.cpp
+++ b/src/query/plaintorich.cpp
@@ -327,6 +327,7 @@ bool PlainToRich::plaintorich(const string& in,
 	    sterms += "\n";
 	}
 	LOGDEB0(("  %s", sterms.c_str()));
+        LOGDEB2(("  TEXT:[%s]\n", in.c_str()));
     }
 
     // Compute the positions for the query terms.  We use the text
@@ -364,13 +365,19 @@ bool PlainToRich::plaintorich(const string& in,
 
     // Input character iterator
     Utf8Iter chariter(in);
-    // State variable used to limit the number of consecutive empty lines 
-    int ateol = 0;
+
+    // State variable used to limit the number of consecutive empty lines,
+    // and convert all eol to '\n'
+    int eol = 0;
+    int hadcr = 0;
 
     // Value for numbered anchors at each term match
     int anchoridx = 1;
-    // html state
+    // HTML state
     bool intag = false, inparamvalue = false;
+    // My tag state
+    int inrcltag = 0;
+
     unsigned int headend = 0;
     if (m_inputhtml) {
 	headend = in.find("");
@@ -379,6 +386,7 @@ bool PlainToRich::plaintorich(const string& in,
 	if (headend != string::npos)
 	    headend += 7;
     }
+
     for (string::size_type pos = 0; pos != string::npos; pos = chariter++) {
 	// Check from time to time if we need to stop
 	if ((pos & 0xfff) == 0) {
@@ -395,8 +403,9 @@ bool PlainToRich::plaintorich(const string& in,
 		    *olit += startMatch();
 		}
 		anchoridx++;
+                inrcltag = 1;
 	    } else if (ibyteidx == tPosIt->second) {
-		// Output end or match region tags
+		// Output end of match region tags
 		if (!intag && ibyteidx > (int)headend) {
 		    *olit += endMatch();
 		    *olit += endAnchor();
@@ -405,60 +414,76 @@ bool PlainToRich::plaintorich(const string& in,
 		int crend = tPosIt->second;
 		while (tPosIt != cb.tboffs.end() && tPosIt->first < crend)
 		    tPosIt++;
-
-		// Maybe end this chunk, begin next. Don't do it on html
-		// there is just no way to do it right (qtextedit cant grok
-		// chunks cut in the middle of  for example).
-		if (!m_inputhtml && olit->size() > (unsigned int)chunksize) {
-		    out.push_back("");
-		    olit++;
-		}
+                inrcltag = 0;
 	    }
 	}
+        
+        unsigned int car = *chariter;
+
+        if (car == '\n') {
+            if (!hadcr)
+                eol++;
+            hadcr = 0;
+            continue;
+        } else if (car == '\r') {
+            hadcr++;
+            eol++;
+            continue;
+        } else if (eol) {
+            // Do line break;
+            hadcr = 0;
+            if (eol > 2)
+                eol = 2;
+            while (eol) {
+                *olit += "\n";
+                eol--;
+            }
+            // Maybe end this chunk, begin next. Don't do it on html
+            // there is just no way to do it right (qtextedit cant grok
+            // chunks cut in the middle of  for example).
+            if (!m_inputhtml && !inrcltag && 
+                olit->size() > (unsigned int)chunksize) {
+                out.push_back(string(startChunk()));
+                olit++;
+            }
+        }
+
+        switch (car) {
+        case '<':
+            if (m_inputhtml) {
+                if (!inparamvalue)
+                    intag = true;
+                chariter.appendchartostring(*olit);    
+            } else {
+                *olit += "<";
+            }
+            break;
+        case '>':
+            if (m_inputhtml) {
+                if (!inparamvalue)
+                    intag = false;
+            }
+            chariter.appendchartostring(*olit);    
+            break;
+        case '&':
+            if (m_inputhtml) {
+                chariter.appendchartostring(*olit);
+            } else {
+                *olit += "&";
+            }
+            break;
+        case '"':
+            if (m_inputhtml && intag) {
+                inparamvalue = !inparamvalue;
+            }
+            chariter.appendchartostring(*olit);
+            break;
+        default:
+            chariter.appendchartostring(*olit);
+        }
+
+    } // End chariter loop
 
-	if (m_inputhtml) {
-	    switch (*chariter) {
-	    case '<':
-		if (!inparamvalue)
-		    intag = true;
-		break;
-	    case '>':
-		if (!inparamvalue)
-		    intag = false;
-		break;
-	    case '"':
-		if (intag) {
-		    inparamvalue = !inparamvalue;
-		}
-		break;
-	    }
-	    chariter.appendchartostring(*olit);
-	} else switch (*chariter) {
-	    case '\n':
-		if (ateol < 2) {
-		    *olit += "
\n"; - ateol++; - } - break; - case '\r': - break; - case '<': - ateol = 0; - *olit += "<"; - break; - case '&': - ateol = 0; - *olit += "&"; - break; - default: - // We don't change the eol status for whitespace, want - // a real line - if (!(*chariter == ' ' || *chariter == '\t')) { - ateol = 0; - } - chariter.appendchartostring(*olit); - } - } #if 0 { FILE *fp = fopen("/tmp/debugplaintorich", "a"); diff --git a/src/query/plaintorich.h b/src/query/plaintorich.h index 63e9b361..9354dd1a 100644 --- a/src/query/plaintorich.h +++ b/src/query/plaintorich.h @@ -78,6 +78,7 @@ public: virtual string endMatch() {return snull;} virtual string startAnchor(int) {return snull;} virtual string endAnchor() {return snull;} + virtual string startChunk() {return snull;} protected: static const string snull; diff --git a/src/sampleconf/mimeconf b/src/sampleconf/mimeconf index da4d59b1..8618472b 100644 --- a/src/sampleconf/mimeconf +++ b/src/sampleconf/mimeconf @@ -65,14 +65,14 @@ application/vnd.sun.xml.writer.global = exec rclsoff application/vnd.sun.xml.writer.template = exec rclsoff application/vnd.wordperfect = exec wpd2html;mimetype=text/html application/x-abiword = exec rclabw -application/x-awk = exec rcltext +application/x-awk = internal application/x-dvi = exec rcldvi application/x-flac = exec rclflac application/x-kword = exec rclkwd application/x-lyx = exec rcllyx -application/x-perl = exec rcltext +application/x-perl = internal application/x-scribus = exec rclscribus -application/x-shellscript = exec rcltext +application/x-shellscript = internal application/x-tex = exec rcltex application/x-chm = execm rclchm application/zip = execm rclzip @@ -88,15 +88,14 @@ text/calendar = execm rclics;mimetype=text/plain;charset=utf-8 text/html = internal text/plain = internal text/rtf = exec unrtf --nopict --html;charset=iso-8859-1;mimetype=text/html -text/x-c = exec rcltext -text/x-c++ = exec rcltext +text/x-c = internal text/x-gaim-log = exec rclgaim text/x-html-sidux-man = exec rclsiduxman text/x-mail = internal text/x-man = exec rclman text/x-purple-log = exec rclpurple text/x-python = exec rclpython -text/x-shellscript = exec rcltext +text/x-shellscript = internal ## ############################################# # Icons to be used in the result list if required by gui config