implement cancellation in preview loading
This commit is contained in:
parent
c8213f76d3
commit
3809fdc235
@ -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 <string>
|
||||
#include <utility>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#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<string> *terms; // in: query terms
|
||||
set<string> terms; // in: user query terms
|
||||
list<pair<int, int> > tboffs; // out: begin and end positions of
|
||||
// query terms in text
|
||||
|
||||
myTextSplitCB(const list<string>& terms)
|
||||
: terms(&terms) {
|
||||
myTextSplitCB(const list<string>& its) {
|
||||
for (list<string>::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<string>::const_iterator it = terms->begin();
|
||||
it != terms->end(); it++) {
|
||||
if (!stringlowercmp(*it, dumb)) {
|
||||
tboffs.push_back(pair<int, int>(bts, bte));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (terms.find(dumb) != terms.end())
|
||||
tboffs.push_back(pair<int, int>(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<string>& terms,
|
||||
list<pair<int, int> >&termoffsets)
|
||||
bool plaintorich(const string& in, string& out, const list<string>& terms,
|
||||
list<pair<int, int> >&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<string>& 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 = "<qt><head><title></title></head><body><p>";
|
||||
out = "<qt><head><title></title></head><body><p>";
|
||||
|
||||
// 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<string>& 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<string>& 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<string>& terms,
|
||||
fclose(fp);
|
||||
}
|
||||
#endif
|
||||
return out;
|
||||
LOGDEB(("plaintorich: done %d mS\n", chron.millis()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -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 <string>
|
||||
|
||||
@ -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<string>& terms,
|
||||
list<pair<int, int> >&termoffsets);
|
||||
extern bool plaintorich(const string &in, string &out,
|
||||
const list<string>& terms,
|
||||
list<pair<int, int> >& termoffsets);
|
||||
|
||||
#endif /* _PLAINTORICH_H_INCLUDED_ */
|
||||
|
||||
@ -198,7 +198,7 @@
|
||||
<variable>void *tabData;</variable>
|
||||
</variables>
|
||||
<signals>
|
||||
<signal>previewClosed(Preview *)</signal>
|
||||
<signal>previewClosed(QWidget *)</signal>
|
||||
</signals>
|
||||
<slots>
|
||||
<slot>searchTextLine_textChanged( const QString & text )</slot>
|
||||
@ -208,7 +208,6 @@
|
||||
<slot>currentChanged( QWidget * tw )</slot>
|
||||
<slot>closeCurrentTab()</slot>
|
||||
<slot>setCurTabProps( const string & fn, const Rcl::Doc & doc )</slot>
|
||||
<slot>loadFileInCurrentTab( string fn, size_t sz, const Rcl::Doc & idoc )</slot>
|
||||
</slots>
|
||||
<functions>
|
||||
<function access="private" specifier="non virtual">init()</function>
|
||||
@ -218,6 +217,7 @@
|
||||
<function returnType="QTextEdit *">getCurrentEditor()</function>
|
||||
<function returnType="QTextEdit *">addEditorTab()</function>
|
||||
<function access="private">destroy()</function>
|
||||
<function access="public" returnType="bool">loadFileInCurrentTab( string fn, size_t sz, const Rcl::Doc & idoc )</function>
|
||||
</functions>
|
||||
<layoutdefaults spacing="6" margin="11"/>
|
||||
</UI>
|
||||
|
||||
@ -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<string> terms;
|
||||
rcldb->getQueryTerms(terms);
|
||||
list<pair<int, int> > 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 += "<b>Cancelled !</b>";
|
||||
}
|
||||
}
|
||||
|
||||
// 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: <br>)
|
||||
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("<b>Cancelled !</b>");
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user