1st version for recoll as proxy for lens open of embedded documents

This commit is contained in:
Jean-Francois Dockes 2012-03-27 11:39:17 +02:00
parent 9f3abe0466
commit f144edfb83
6 changed files with 270 additions and 151 deletions

View File

@ -11,6 +11,13 @@ src/common/autoconfig.h
src/common/rclversion.h src/common/rclversion.h
src/config.log src/config.log
src/config.status src/config.status
src/desktop/unity-lens-recoll/Makefile
src/desktop/unity-lens-recoll/autom4te.cache
src/desktop/unity-lens-recoll/bin/unity-recoll-daemon
src/desktop/unity-lens-recoll/config.log
src/desktop/unity-lens-recoll/config.status
src/desktop/unity-lens-recoll/data/recoll.lens
src/desktop/unity-lens-recoll/data/unity-lens-recoll.service
src/doc/user/HTML.manifest src/doc/user/HTML.manifest
src/doc/user/index.html src/doc/user/index.html
src/doc/user/rcl.indexing.beaglequeue.html src/doc/user/rcl.indexing.beaglequeue.html

View File

@ -1 +1 @@
1.17.0 1.17.1

View File

@ -1,5 +1,7 @@
import sys import sys
import subprocess
from gi.repository import GLib, GObject, Gio from gi.repository import GLib, GObject, Gio
from gi.repository import Dee from gi.repository import Dee
from gi.repository import Unity from gi.repository import Unity
@ -20,168 +22,195 @@ TYPING_TIMEOUT = 0
class Scope (Unity.Scope): class Scope (Unity.Scope):
def __init__ (self): def __init__ (self):
Unity.Scope.__init__ (self, dbus_path=BUS_PATH) Unity.Scope.__init__ (self, dbus_path=BUS_PATH)
# Listen for changes and requests
if Unity._version == "4.0":
#print "Setting up for Unity 4.0"
self.connect("notify::active-search",
self._on_search_changed)
self.connect("notify::active-global-search",
self._on_global_search_changed)
self.connect("filters-changed",
self._on_search_changed);
else:
#print "Setting up for Unity 5.0+"
self.connect ("search-changed", self._on_search_changed)
self.connect ("filters-changed",
self._on_filters_changed)
# Connect to the index
self.db = recoll.connect()
self.db.setAbstractParams(maxchars=200,
contextwords=4)
self.timeout_id = None
def get_search_string (self):
search = self.props.active_search
return search.props.search_string if search else None
def get_global_search_string (self):
search = self.props.active_global_search
return search.props.search_string if search else None
def search_finished (self):
search = self.props.active_search
if search:
search.emit("finished")
def global_search_finished (self):
search = self.props.active_global_search
if search:
search.emit("finished")
def reset (self):
self._do_browse (self.props.results_model)
self._do_browse (self.props.global_results_model)
def _on_filters_changed (self, scope):
# print "_on_filters_changed()"
self.queue_search_changed(Unity.SearchType.DEFAULT)
# Listen for changes and requests
self.connect ("activate-uri", self.activate_uri)
if Unity._version == "4.0": if Unity._version == "4.0":
def _on_search_changed (self, scope, param_spec=None): #print "Setting up for Unity 4.0"
search_string = self.get_search_string() self.connect("notify::active-search",
results = scope.props.results_model self._on_search_changed)
# print "Search 4.0 changed to: '%s'" % search_string self.connect("notify::active-global-search",
self._on_global_search_changed)
self._update_results_model (search_string, results) self.connect("filters-changed",
self._on_search_changed);
else: else:
def _on_search_changed (self, scope, search, search_type, cancellable): #print "Setting up for Unity 5.0+"
search_string = search.props.search_string self.connect ("search-changed", self._on_search_changed)
results = search.props.results_model self.connect ("filters-changed",
# print "Search 5.0 changed to: '%s'" % search_string self._on_filters_changed)
self._update_results_model (search_string, results)
def _on_global_search_changed (self, scope, param_spec): # Connect to the index
search = self.get_global_search_string() self.db = recoll.connect()
results = scope.props.global_results_model
# print "Global search changed to: '%s'" % search self.db.setAbstractParams(maxchars=200,
contextwords=4)
self._update_results_model (search, results) self.timeout_id = None
def _update_results_model (self, search_string, model): def get_search_string (self):
if search_string: search = self.props.active_search
self._do_search (search_string, model) return search.props.search_string if search else None
else:
self._do_browse (model)
def _do_browse (self, model): def get_global_search_string (self):
if self.timeout_id is not None: search = self.props.active_global_search
GObject.source_remove(self.timeout_id) return search.props.search_string if search else None
model.clear ()
if model is self.props.results_model: def search_finished (self):
self.search_finished() search = self.props.active_search
else: if search:
self.global_search_finished() search.emit("finished")
def _on_timeout(self, search_string, model): def global_search_finished (self):
if self.timeout_id is not None: search = self.props.active_global_search
GObject.source_remove(self.timeout_id) if search:
self.timeout_id = None search.emit("finished")
self._really_do_search(search_string, model) def reset (self):
if model is self.props.results_model: self._do_browse (self.props.results_model)
self.search_finished() self._do_browse (self.props.global_results_model)
else:
self.global_search_finished()
def _do_search (self, search_string, model): def _on_filters_changed (self, scope):
if TYPING_TIMEOUT == 0: # print "_on_filters_changed()"
self._really_do_search(search_string, model) self.queue_search_changed(Unity.SearchType.DEFAULT)
return True
if self.timeout_id is not None: if Unity._version == "4.0":
GObject.source_remove(self.timeout_id) def _on_search_changed (self, scope, param_spec=None):
self.timeout_id = \ search_string = self.get_search_string()
GObject.timeout_add(TYPING_TIMEOUT, self._on_timeout, results = scope.props.results_model
search_string, model) # print "Search 4.0 changed to: '%s'" % search_string
def _really_do_search(self, search_string, model): self._update_results_model (search_string, results)
# print "really_do_search:[", search_string, "]" else:
def _on_search_changed (self, scope, search, search_type, cancellable):
search_string = search.props.search_string
results = search.props.results_model
# print "Search 5.0 changed to: '%s'" % search_string
model.clear () self._update_results_model (search_string, results)
if search_string == "":
return True
fcat = self.get_filter("rclcat") def _on_global_search_changed (self, scope, param_spec):
for option in fcat.options: search = self.get_global_search_string()
if option.props.active: results = scope.props.global_results_model
search_string += " rclcat:" + option.props.id
# Do the recoll thing # print "Global search changed to: '%s'" % search
query = self.db.query()
try:
nres = query.execute(search_string)
except:
return
actual_results = 0 self._update_results_model (search, results)
while query.next >= 0 and query.next < nres:
try:
doc = query.fetchone()
except:
break
# No sense in returning unusable results (until def _update_results_model (self, search_string, model):
# I get an idea of what to do with them) if search_string:
if doc.ipath != "": self._do_search (search_string, model)
continue else:
self._do_browse (model)
titleorfilename = doc.title def _do_browse (self, model):
if titleorfilename == "": if self.timeout_id is not None:
titleorfilename = doc.filename GObject.source_remove(self.timeout_id)
model.clear ()
icon = Gio.content_type_get_icon(doc.mimetype) if model is self.props.results_model:
if icon: self.search_finished()
iconname = icon.get_names()[0] else:
self.global_search_finished()
abstract = self.db.makeDocAbstract(doc, query).encode('utf-8') def _on_timeout(self, search_string, model):
if self.timeout_id is not None:
GObject.source_remove(self.timeout_id)
self.timeout_id = None
self._really_do_search(search_string, model)
if model is self.props.results_model:
self.search_finished()
else:
self.global_search_finished()
model.append (doc.url, def _do_search (self, search_string, model):
iconname, if TYPING_TIMEOUT == 0:
CATEGORY_ALL, self._really_do_search(search_string, model)
doc.mimetype, return True
titleorfilename,
abstract, if self.timeout_id is not None:
doc.url) GObject.source_remove(self.timeout_id)
self.timeout_id = \
GObject.timeout_add(TYPING_TIMEOUT, self._on_timeout,
search_string, model)
def _really_do_search(self, search_string, model):
# print "really_do_search:[", search_string, "]"
model.clear ()
if search_string == "":
return True
fcat = self.get_filter("rclcat")
for option in fcat.options:
if option.props.active:
search_string += " rclcat:" + option.props.id
# Do the recoll thing
query = self.db.query()
try:
nres = query.execute(search_string)
except:
return
actual_results = 0
while query.next >= 0 and query.next < nres:
try:
doc = query.fetchone()
except:
break
# No sense in returning unusable results (until
# I get an idea of what to do with them)
if doc.ipath != "":
mimetype = "application/x-recoll"
url = doc.url + "#" + doc.ipath
else:
mimetype = doc.mimetype
url = doc.url
print "Recoll Lens: Using MIMETYPE", mimetype, \
" URL", url
titleorfilename = doc.title
if titleorfilename == "":
titleorfilename = doc.filename
icon = Gio.content_type_get_icon(doc.mimetype)
if icon:
iconname = icon.get_names()[0]
abstract = self.db.makeDocAbstract(doc, query).encode('utf-8')
model.append (url,
iconname,
CATEGORY_ALL,
mimetype,
titleorfilename,
abstract,
doc.url)
actual_results += 1
if actual_results >= 20:
break
def activate_uri (self, scope, uri):
"""Activation handler for uri"""
print "Activate: %s" % uri
# Pass all uri without fragments to the desktop handler
if uri.find("#") == -1:
# Reset browsing state when an app is launched
self.reset ()
return Unity.ActivationResponse.new(Unity.HandledType.NOT_HANDLED,
uri)
# Pass all others to recoll
proc = subprocess.Popen(["recoll", uri])
print "Subprocess returned, going back to unity"
return Unity.ActivationResponse.new(Unity.HandledType.HIDE_DASH, "baduri")
actual_results += 1
if actual_results >= 20:
break

View File

@ -203,7 +203,7 @@ static int op_flags;
static const char usage [] = static const char usage [] =
"\n" "\n"
"recoll [-h] [-c <configdir>] [-q options]\n" "recoll [-h] [-c <configdir>] [-q query]\n"
" -h : Print help and exit\n" " -h : Print help and exit\n"
" -c <configdir> : specify config directory, overriding $RECOLL_CONFDIR\n" " -c <configdir> : specify config directory, overriding $RECOLL_CONFDIR\n"
" [-o|l|f|a] [-t] -q 'query' : search query to be executed as if entered\n" " [-o|l|f|a] [-t] -q 'query' : search query to be executed as if entered\n"
@ -219,6 +219,9 @@ static const char usage [] =
" explicitly as -t (not ie, -at), and -q <query> MUST\n" " explicitly as -t (not ie, -at), and -q <query> MUST\n"
" be last on the command line if this is used.\n" " be last on the command line if this is used.\n"
" Use -t -h to see the additional non-gui options\n" " Use -t -h to see the additional non-gui options\n"
"recoll <url>\n"
" This is used to open a recoll url (including an ipath), and called\n"
" typically from another search interface like the Unity Dash\n"
; ;
static void static void
Usage(void) Usage(void)
@ -245,6 +248,7 @@ int main(int argc, char **argv)
string a_config; string a_config;
string question; string question;
string urltoview;
thisprog = argv[0]; thisprog = argv[0];
argc--; argv++; argc--; argv++;
@ -276,11 +280,22 @@ int main(int argc, char **argv)
// to the query. This is for the common case recoll -q x y z to // to the query. This is for the common case recoll -q x y z to
// avoid needing quoting "x y z" // avoid needing quoting "x y z"
if (op_flags & OPT_q) if (op_flags & OPT_q)
while (argc--) { while (argc > 0) {
question += " "; question += " ";
question += *argv++; question += *argv++;
argc--;
} }
// Else the remaining argument should be an URL to be opened
if (argc == 1) {
urltoview = *argv++;argc--;
if (urltoview.compare(0, 7, cstr_fileu)) {
Usage();
}
} else if (argc > 0)
Usage();
// Translation file for Qt // Translation file for Qt
QString slang = QLocale::system().name().left(2); QString slang = QLocale::system().name().left(2);
QTranslator qt(0); QTranslator qt(0);
@ -375,6 +390,9 @@ int main(int argc, char **argv)
mainWindow->sSearch->searchTypCMB->setCurrentIndex(int(stype)); mainWindow->sSearch->searchTypCMB->setCurrentIndex(int(stype));
mainWindow-> mainWindow->
sSearch->setSearchString(QString::fromLocal8Bit(question.c_str())); sSearch->setSearchString(QString::fromLocal8Bit(question.c_str()));
} else if (!urltoview.empty()) {
LOGDEB(("MAIN: got urltoview [%s]\n", urltoview.c_str()));
mainWindow->setUrlToView(QString::fromLocal8Bit(urltoview.c_str()));
} }
return app.exec(); return app.exec();
} }

View File

@ -19,6 +19,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <regex.h> #include <regex.h>
@ -78,6 +79,7 @@ using std::pair;
#include "rtitool.h" #include "rtitool.h"
#include "indexer.h" #include "indexer.h"
#include "rclzg.h" #include "rclzg.h"
#include "fileudi.h"
using namespace confgui; using namespace confgui;
@ -389,6 +391,59 @@ void RclMain::initDbOpen()
// command line argument // command line argument
if (!nodb && sSearch->hasSearchString()) if (!nodb && sSearch->hasSearchString())
QTimer::singleShot(0, sSearch, SLOT(startSimpleSearch())); QTimer::singleShot(0, sSearch, SLOT(startSimpleSearch()));
if (!m_urltoview.isEmpty())
viewUrl();
}
// Start native viewer or preview for input Doc. This is used allow
// the using Recoll result docs with an ipath in another app. We act
// as a proxy to extract the data and start a viewer.
// The Url are encoded as file://path#ipath
void RclMain::viewUrl()
{
if (m_urltoview.isEmpty() || !rcldb)
return;
QUrl qurl(m_urltoview);
LOGDEB(("RclMain::viewUrl: Path [%s] fragment [%s]\n",
(const char *)qurl.path().toLocal8Bit(),
(const char *)qurl.fragment().toLocal8Bit()));
/* In theory, the url might not be for a file managed by the fs
indexer so that the make_udi() call here would be
wrong(). When/if this happens we'll have to hide this part
inside internfile and have some url magic to indicate the
appropriate indexer/identification scheme */
string udi;
make_udi((const char *)qurl.path().toLocal8Bit(),
(const char *)qurl.fragment().toLocal8Bit(), udi);
Rcl::Doc doc;
if (!rcldb->getDoc(udi, doc) || doc.pc == -1)
return;
// Start a native viewer if the mimetype has one defined, else a
// preview.
string apptag;
doc.getmeta(Rcl::Doc::keyapptg, &apptag);
string viewer = theconfig->getMimeViewerDef(doc.mimetype, apptag);
if (viewer.empty()) {
startPreview(doc);
} else {
startNativeViewer(doc);
// We have a problem here because xdg-open will exit
// immediately after starting the command instead of waiting
// for it, so we can't wait either and we don't know when we
// can exit (deleting the temp file). As a bad workaround we
// sleep some time then exit. The alternative would be to just
// prevent the temp file deletion completely, leaving it
// around forever. Better to let the user save a copy if he
// wants I think.
hide();
sleep(10);
fileExit();
}
} }
void RclMain::focusToSearch() void RclMain::focusToSearch()

View File

@ -79,6 +79,12 @@ public:
virtual bool eventFilter(QObject *target, QEvent *event); virtual bool eventFilter(QObject *target, QEvent *event);
QString getQueryDescription(); QString getQueryDescription();
/** This is only called from main() to set an URL to be displayed (using
recoll as a doc extracter for embedded docs */
virtual void setUrlToView(const QString& u) {m_urltoview = u;}
/** Same usage: actually display the current urltoview */
virtual void viewUrl();
public slots: public slots:
virtual bool close(); virtual bool close();
virtual void fileExit(); virtual void fileExit();
@ -169,6 +175,10 @@ private:
RefCntr<DocSequence> m_source; RefCntr<DocSequence> m_source;
IndexerState m_indexerState; IndexerState m_indexerState;
// If set on init, will be displayed either through ext app, or
// preview (if no ext app set)
QString m_urltoview;
virtual void init(); virtual void init();
virtual void previewPrevOrNextInTab(Preview *, int sid, int docnum, virtual void previewPrevOrNextInTab(Preview *, int sid, int docnum,
bool next); bool next);