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):
search = self.get_global_search_string()
results = scope.props.global_results_model
# print "Global search changed to: '%s'" % search
self._update_results_model (search, results)
def _update_results_model (self, search_string, model):
if search_string:
self._do_search (search_string, model)
else:
self._do_browse (model)
def _do_browse (self, model):
if self.timeout_id is not None:
GObject.source_remove(self.timeout_id)
model.clear ()
if model is self.props.results_model:
self.search_finished()
else:
self.global_search_finished()
def _on_timeout(self, search_string, model): # Connect to the index
if self.timeout_id is not None: self.db = recoll.connect()
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()
def _do_search (self, search_string, model): self.db.setAbstractParams(maxchars=200,
if TYPING_TIMEOUT == 0: contextwords=4)
self._really_do_search(search_string, model)
return True self.timeout_id = None
if self.timeout_id is not None: def get_search_string (self):
GObject.source_remove(self.timeout_id) search = self.props.active_search
self.timeout_id = \ return search.props.search_string if search else None
GObject.timeout_add(TYPING_TIMEOUT, self._on_timeout,
search_string, model) 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)
if Unity._version == "4.0":
def _on_search_changed (self, scope, param_spec=None):
search_string = self.get_search_string()
results = scope.props.results_model
# print "Search 4.0 changed to: '%s'" % search_string
self._update_results_model (search_string, results)
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
self._update_results_model (search_string, results)
def _on_global_search_changed (self, scope, param_spec):
search = self.get_global_search_string()
results = scope.props.global_results_model
# print "Global search changed to: '%s'" % search
self._update_results_model (search, results)
def _update_results_model (self, search_string, model):
if search_string:
self._do_search (search_string, model)
else:
self._do_browse (model)
def _do_browse (self, model):
if self.timeout_id is not None:
GObject.source_remove(self.timeout_id)
model.clear ()
def _really_do_search(self, search_string, model): if model is self.props.results_model:
# print "really_do_search:[", search_string, "]" self.search_finished()
else:
self.global_search_finished()
model.clear () def _on_timeout(self, search_string, model):
if search_string == "": if self.timeout_id is not None:
return True 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()
fcat = self.get_filter("rclcat") def _do_search (self, search_string, model):
for option in fcat.options: if TYPING_TIMEOUT == 0:
if option.props.active: self._really_do_search(search_string, model)
search_string += " rclcat:" + option.props.id return True
if self.timeout_id is not None:
GObject.source_remove(self.timeout_id)
self.timeout_id = \
GObject.timeout_add(TYPING_TIMEOUT, self._on_timeout,
search_string, model)
# Do the recoll thing def _really_do_search(self, search_string, model):
query = self.db.query() # print "really_do_search:[", search_string, "]"
try:
nres = query.execute(search_string)
except:
return
actual_results = 0 model.clear ()
while query.next >= 0 and query.next < nres: if search_string == "":
try: return True
doc = query.fetchone()
except:
break
# No sense in returning unusable results (until fcat = self.get_filter("rclcat")
# I get an idea of what to do with them) for option in fcat.options:
if doc.ipath != "": if option.props.active:
continue search_string += " rclcat:" + option.props.id
titleorfilename = doc.title # Do the recoll thing
if titleorfilename == "": query = self.db.query()
titleorfilename = doc.filename try:
nres = query.execute(search_string)
except:
return
icon = Gio.content_type_get_icon(doc.mimetype) actual_results = 0
if icon: while query.next >= 0 and query.next < nres:
iconname = icon.get_names()[0] try:
doc = query.fetchone()
except:
break
abstract = self.db.makeDocAbstract(doc, query).encode('utf-8') # 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
model.append (doc.url, print "Recoll Lens: Using MIMETYPE", mimetype, \
iconname, " URL", url
CATEGORY_ALL,
doc.mimetype,
titleorfilename,
abstract,
doc.url)
actual_results += 1 titleorfilename = doc.title
if actual_results >= 20: if titleorfilename == "":
break 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")

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);