From 3809fdc235437fdeb7c574176f632945b34be925 Mon Sep 17 00:00:00 2001 From: dockes Date: Fri, 27 Jan 2006 13:42:03 +0000 Subject: [PATCH] implement cancellation in preview loading --- src/qtgui/plaintorich.cpp | 50 +++++++------ src/qtgui/plaintorich.h | 8 +- src/qtgui/preview/preview.ui | 4 +- src/qtgui/preview/preview.ui.h | 131 ++++++++++++++++++++++++--------- src/qtgui/rclmain.cpp | 40 ++++++++-- 5 files changed, 165 insertions(+), 68 deletions(-) diff --git a/src/qtgui/plaintorich.cpp b/src/qtgui/plaintorich.cpp index 4bea31d5..a8462910 100644 --- a/src/qtgui/plaintorich.cpp +++ b/src/qtgui/plaintorich.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: plaintorich.cpp,v 1.8 2006-01-23 13:32:05 dockes Exp $ (C) 2005 J.F.Dockes"; +static char rcsid[] = "@(#$Id: plaintorich.cpp,v 1.9 2006-01-27 13:42:02 dockes Exp $ (C) 2005 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -22,9 +22,11 @@ static char rcsid[] = "@(#$Id: plaintorich.cpp,v 1.8 2006-01-23 13:32:05 dockes #include #include #include +#include #ifndef NO_NAMESPACES using std::list; using std::pair; +using std::set; #endif /* NO_NAMESPACES */ #include "rcldb.h" @@ -34,17 +36,24 @@ using std::pair; #include "utf8iter.h" #include "transcode.h" #include "smallut.h" +#include "plaintorich.h" +#include "cancelcheck.h" // Text splitter callback used to take note of the position of query terms // inside the result text. This is then used to post highlight tags. class myTextSplitCB : public TextSplitCB { public: - const list *terms; // in: query terms + set terms; // in: user query terms list > tboffs; // out: begin and end positions of // query terms in text - myTextSplitCB(const list& terms) - : terms(&terms) { + myTextSplitCB(const list& its) { + for (list::const_iterator it = its.begin(); it != its.end(); + it++) { + string s; + Rcl::dumb_string(*it, s); + terms.insert(s); + } } // Callback called by the text-to-words breaker for each word @@ -53,14 +62,9 @@ class myTextSplitCB : public TextSplitCB { Rcl::dumb_string(term, dumb); //LOGDEB(("Input dumbbed term: '%s' %d %d %d\n", dumb.c_str(), // pos, bts, bte)); - for (list::const_iterator it = terms->begin(); - it != terms->end(); it++) { - if (!stringlowercmp(*it, dumb)) { - tboffs.push_back(pair(bts, bte)); - break; - } - } - + if (terms.find(dumb) != terms.end()) + tboffs.push_back(pair(bts, bte)); + CancelCheck::instance().checkCancel(); return true; } }; @@ -72,12 +76,13 @@ class myTextSplitCB : public TextSplitCB { // duplicate whitespace etc...). This is tricky business and it might // be better to insert the text char by char, taking note of where qt // thinks it is at each term. -string plaintorich(const string &in, const list& terms, - list >&termoffsets) +bool plaintorich(const string& in, string& out, const list& terms, + list >&termoffsets) { + Chrono chron; LOGDEB(("plaintorich: terms: %s\n", stringlistdisp(terms).c_str())); - + out.erase(); termoffsets.erase(termoffsets.begin(), termoffsets.end()); // We first use the text splitter to break the text into words, @@ -89,11 +94,10 @@ string plaintorich(const string &in, const list& terms, // character offset splitter.text_to_words(in); - LOGDEB(("Split done\n")); - + LOGDEB(("plaintorich: split done %d mS\n", chron.millis())); // Rich text output - string out = "

"; + out = "

"; // Iterator for the list of input term positions. We use it to // output highlight tags and to compute term positions in the @@ -112,7 +116,10 @@ string plaintorich(const string &in, const list& terms, // consecutive blank chars int atblank = 0; for (string::size_type pos = 0; pos != string::npos; pos = chariter++) { - // If we still have terms, check (byte) position + if (pos && (pos % 1000) == 0) { + CancelCheck::instance().checkCancel(); + } + // If we still have terms positions, check (byte) position if (it != cb.tboffs.end()) { int ibyteidx = chariter.getBpos(); if (ibyteidx == it->first) { @@ -148,7 +155,7 @@ string plaintorich(const string &in, const list& terms, break; default: // We don't change the eol status for whitespace, want a real line - if (*chariter == ' ' || *chariter == ' ') { + if (*chariter == ' ' || *chariter == '\t') { if (!atblank) outcpos++; atblank = 1; @@ -167,5 +174,6 @@ string plaintorich(const string &in, const list& terms, fclose(fp); } #endif - return out; + LOGDEB(("plaintorich: done %d mS\n", chron.millis())); + return true; } diff --git a/src/qtgui/plaintorich.h b/src/qtgui/plaintorich.h index 4e238d00..f86eb602 100644 --- a/src/qtgui/plaintorich.h +++ b/src/qtgui/plaintorich.h @@ -1,6 +1,6 @@ #ifndef _PLAINTORICH_H_INCLUDED_ #define _PLAINTORICH_H_INCLUDED_ -/* @(#$Id: plaintorich.h,v 1.3 2006-01-19 12:01:43 dockes Exp $ (C) 2004 J.F.Dockes */ +/* @(#$Id: plaintorich.h,v 1.4 2006-01-27 13:42:02 dockes Exp $ (C) 2004 J.F.Dockes */ #include @@ -14,8 +14,8 @@ * @param terms list of query terms * @param termoffsets output: character offsets where we find terms. */ -extern string plaintorich(const string &in, - const list& terms, - list >&termoffsets); +extern bool plaintorich(const string &in, string &out, + const list& terms, + list >& termoffsets); #endif /* _PLAINTORICH_H_INCLUDED_ */ diff --git a/src/qtgui/preview/preview.ui b/src/qtgui/preview/preview.ui index 0896e5ee..95519cb3 100644 --- a/src/qtgui/preview/preview.ui +++ b/src/qtgui/preview/preview.ui @@ -198,7 +198,7 @@ void *tabData; - previewClosed(Preview *) + previewClosed(QWidget *) searchTextLine_textChanged( const QString & text ) @@ -208,7 +208,6 @@ currentChanged( QWidget * tw ) closeCurrentTab() setCurTabProps( const string & fn, const Rcl::Doc & doc ) - loadFileInCurrentTab( string fn, size_t sz, const Rcl::Doc & idoc ) init() @@ -218,6 +217,7 @@ getCurrentEditor() addEditorTab() destroy() + loadFileInCurrentTab( string fn, size_t sz, const Rcl::Doc & idoc ) diff --git a/src/qtgui/preview/preview.ui.h b/src/qtgui/preview/preview.ui.h index 3b53a098..d5f1bf1f 100644 --- a/src/qtgui/preview/preview.ui.h +++ b/src/qtgui/preview/preview.ui.h @@ -29,6 +29,7 @@ using std::pair; #include "plaintorich.h" #include "smallut.h" #include "wipedir.h" +#include "cancelcheck.h" // We keep a list of data associated to each tab class TabData { @@ -59,7 +60,7 @@ void Preview::destroy() void Preview::closeEvent(QCloseEvent *e) { - emit previewClosed(this); + emit previewClosed((QWidget *)this); QWidget::closeEvent(e); } @@ -201,15 +202,15 @@ void Preview::prevPressed() void Preview::currentChanged(QWidget * tw) { - QObject *o = tw->child("pvEdit"); - LOGDEB1(("Preview::currentChanged(). Edit %p\n", o)); + QWidget *edit = (QWidget *)tw->child("pvEdit"); + LOGDEB1(("Preview::currentChanged(). Editor: %p\n", edit)); - if (o == 0) { + if (edit == 0) { LOGERR(("Editor child not found\n")); } else { tw->installEventFilter(this); - o->installEventFilter(this); - ((QWidget*)o)->setFocus(); + edit->installEventFilter(this); + edit->setFocus(); } } @@ -317,7 +318,9 @@ bool Preview::makeDocCurrent(const string &fn, const Rcl::Doc &doc) be even more complicated, and we probably don't want the user to click on things during this time anyway. - No cancel button is implemented, but this could conceivably be done + It might be possible, but complicated (need modifications in + handler) to implement a kind of bucket brigade, to have the + beginning of the text displayed faster */ /* A thread to to the file reading / format conversion */ @@ -348,10 +351,14 @@ class LoadThread : public QThread { return; } FileInterner interner(filename, rclconfig, tmpdir, mtype); - if (interner.internfile(*out, ipath) != FileInterner::FIDone) { + try { + if (interner.internfile(*out, ipath) != FileInterner::FIDone) { + *statusp = -1; + } else { + *statusp = 0; + } + } catch (CancelExcept) { *statusp = -1; - } else { - *statusp = 0; } } }; @@ -370,7 +377,11 @@ class ToRichThread : public QThread { virtual void run() { DebugLog::getdbl()->setloglevel(DEBDEB1); - string rich = plaintorich(in, terms, termoffsets); + string rich; + try { + plaintorich(in, rich, terms, termoffsets); + } catch (CancelExcept) { + } out = QString::fromUtf8(rich.c_str(), rich.length()); } }; @@ -385,9 +396,15 @@ class WaiterThread : public QThread { } }; -void Preview::loadFileInCurrentTab(string fn, size_t sz, const Rcl::Doc &idoc) +#define CHUNKL 50*1000 +#ifndef MIN +#define MIN(A,B) ((A)<(B)?(A):(B)) +#endif + +bool Preview::loadFileInCurrentTab(string fn, size_t sz, const Rcl::Doc &idoc) { Rcl::Doc doc = idoc; + bool cancel = false; if (doc.title.empty()) doc.title = path_getsimple(doc.url); @@ -401,11 +418,8 @@ void Preview::loadFileInCurrentTab(string fn, size_t sz, const Rcl::Doc &idoc) .arg(csz); // Create progress dialog and aux objects - const int nsteps = 10; - QProgressDialog progress( msg, "", nsteps, this, "Loading", TRUE ); - QPushButton *pb = new QPushButton("", this); - pb->setEnabled(false); - progress.setCancelButton(pb); + const int nsteps = 20; + QProgressDialog progress(msg, tr("Cancel"), nsteps, this, "Loading", FALSE); progress.setMinimumDuration(1000); WaiterThread waiter(100); @@ -416,57 +430,103 @@ void Preview::loadFileInCurrentTab(string fn, size_t sz, const Rcl::Doc &idoc) int status = 1; LoadThread lthr(&status, &fdoc, fn, doc.ipath, &doc.mimetype); lthr.start(); - int i; - for (i = 1;;i++) { + int prog; + for (prog = 1;;prog++) { waiter.start(); waiter.wait(); if (lthr.finished()) break; - progress.setProgress(i , i <= nsteps-1 ? nsteps : i+1); + progress.setProgress(prog , prog <= nsteps-1 ? nsteps : prog+1); qApp->processEvents(); - if (i >= 5) + if (progress.wasCanceled()) { + CancelCheck::instance().setCancel(); + cancel = true; + } + if (prog >= 5) sleep(1); } + if (cancel) + return false; if (status != 0) { QMessageBox::warning(0, "Recoll", tr("Can't turn doc into internal rep for ") + doc.mimetype.c_str()); - return; + return false; } // Reset config just in case. rclconfig->setKeyDir(""); - // Highlight search terms: + // Create preview text: highlight search terms: progress.setLabelText(tr("Creating preview text")); list terms; rcldb->getQueryTerms(terms); list > termoffsets; - QString str; - ToRichThread rthr(fdoc.text, terms, termoffsets, str); + QString richTxt; + ToRichThread rthr(fdoc.text, terms, termoffsets, richTxt); rthr.start(); - for (;;i++) { - waiter.start(); - waiter.wait(); + for (;;prog++) { + waiter.start(); waiter.wait(); if (rthr.finished()) break; - progress.setProgress(i , i <= nsteps-1 ? nsteps : i+1); + progress.setProgress(prog , prog <= nsteps-1 ? nsteps : prog+1); qApp->processEvents(); - if (i >= 5) + if (progress.wasCanceled()) { + CancelCheck::instance().setCancel(); + cancel = true; + } + if (prog >= 5) sleep(1); } - LOGDEB(("Plaintorich done\n")); - + if (cancel) { + if (richTxt.length() == 0) { + // We cant call closeCurrentTab here as it might delete + // the object which would be a nasty surprise to our + // caller. + return false; + } else { + richTxt += "Cancelled !"; + } + } + // Load into editor QTextEdit *editor = getCurrentEditor(); QStyleSheetItem *item = new QStyleSheetItem(editor->styleSheet(), "termtag" ); item->setColor("blue"); item->setFontWeight(QFont::Bold); - + + prog = 2 * nsteps / 3; progress.setLabelText(tr("Loading preview text into editor")); qApp->processEvents(); - editor->setText(str); + // Do it in several chunks + int l = 0; + for (unsigned int pos = 0; pos < richTxt.length(); pos += l, prog++) { + progress.setProgress(prog , prog <= nsteps-1 ? nsteps : prog+1); + qApp->processEvents(); + + l = MIN(CHUNKL, richTxt.length() - pos); + // Avoid breaking inside a tag. Our tags are short (ie:
) + for (int i = -4; i < 0; i++) { + if (richTxt[pos+l+i] == '<') { + l = l+i; + break; + } + } + + editor->append(richTxt.mid(pos, l)); + // Stay at top + if (pos < 5) { + editor->setCursorPosition(0,0); + editor->ensureCursorVisible(); + } + if (progress.wasCanceled()) { + cancel = true; + editor->append("Cancelled !"); + LOGDEB(("Cancelled\n")); + break; + } + } int para = 0, index = 1; if (!termoffsets.empty()) { index = (termoffsets.begin())->first; @@ -479,6 +539,5 @@ void Preview::loadFileInCurrentTab(string fn, size_t sz, const Rcl::Doc &idoc) LOGDEB(("PREVIEW len %d paragraphs: %d. Cpos: %d %d\n", editor->length(), editor->paragraphs(), para, index)); + return true; } - - diff --git a/src/qtgui/rclmain.cpp b/src/qtgui/rclmain.cpp index 1b73c915..7397668f 100644 --- a/src/qtgui/rclmain.cpp +++ b/src/qtgui/rclmain.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: rclmain.cpp,v 1.9 2006-01-26 14:02:01 dockes Exp $ (C) 2005 J.F.Dockes"; +static char rcsid[] = "@(#$Id: rclmain.cpp,v 1.10 2006-01-27 13:42:02 dockes Exp $ (C) 2005 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -53,7 +53,6 @@ using std::pair; #include "mimehandler.h" #include "pathut.h" #include "smallut.h" -#include "plaintorich.h" #include "advsearch.h" #include "rclversion.h" #include "sortseq.h" @@ -435,7 +434,8 @@ void RclMain::startPreview(int docnum) (void)curPreview->addEditorTab(); } m_history->enterDocument(fn, doc.ipath); - curPreview->loadFileInCurrentTab(fn, st.st_size, doc); + if (!curPreview->loadFileInCurrentTab(fn, st.st_size, doc)) + curPreview->closeCurrentTab(); } void RclMain::startNativeViewer(int docnum) @@ -556,10 +556,40 @@ void RclMain::enablePrevPage(bool yesno) prevPageAction->setEnabled(yesno); } +/** Show detailed expansion of a query */ void RclMain::showQueryDetails() { - // Bad number: must have clicked on header. Show details of query + // Break query into lines of reasonable length, avoid cutting words! + const int ll = 80; + string query = currentQueryData.description; + string oq; + while (query.length() > 0) { + string ss = query.substr(0, ll); + if (ss.length() == ll) { + string::size_type pos = ss.find_last_of(" "); + if (pos == string::npos) { + pos = query.find_first_of(" "); + if (pos != string::npos) + ss = query.substr(0, pos+1); + else + ss = query; + } else { + ss = ss.substr(0, pos+1); + } + } + // This cant happen, but anyway. Be very sure to avoid an infinite loop + if (ss.length() == 0) { + LOGDEB(("showQueryDetails: Internal error!\n")); + oq = query; + break; + } + oq += ss + "\n"; + query= query.substr(ss.length()); + LOGDEB1(("oq [%s]\n, query [%s]\n, ss [%s]\n", + oq.c_str(), query.c_str(), ss.c_str())); + } + QString desc = tr("Query details") + ": " + - QString::fromUtf8(currentQueryData.description.c_str()); + QString::fromUtf8(oq.c_str()); QMessageBox::information(this, tr("Query details"), desc); }