|
a |
|
b/src/desktop/unity-lens-recoll/recollscope/rclsearch.py |
|
|
1 |
|
|
|
2 |
import sys
|
|
|
3 |
from gi.repository import GLib, GObject, Gio
|
|
|
4 |
from gi.repository import Dee
|
|
|
5 |
from gi.repository import Unity
|
|
|
6 |
|
|
|
7 |
import recoll
|
|
|
8 |
|
|
|
9 |
# These category ids must match the order in which we add them to the lens
|
|
|
10 |
CATEGORY_ALL = 0
|
|
|
11 |
|
|
|
12 |
# typing timeout: we don't want to start a search for every
|
|
|
13 |
# char? Unity does batch on its side, but we may want more control ?
|
|
|
14 |
# Or not ? I'm not sure this does any good on a moderate size index.
|
|
|
15 |
# Set to 0 to not use it (default). Kept around because this still might be
|
|
|
16 |
# useful with a very big index ?
|
|
|
17 |
TYPING_TIMEOUT = 0
|
|
|
18 |
|
|
|
19 |
class Scope (Unity.Scope):
|
|
|
20 |
|
|
|
21 |
def __init__ (self):
|
|
|
22 |
Unity.Scope.__init__ (self, dbus_path="/org/recoll/unitylensrecoll/scope/main")
|
|
|
23 |
|
|
|
24 |
# Listen for changes and requests
|
|
|
25 |
self.connect ("notify::active-search", self._on_search_changed)
|
|
|
26 |
self.connect ("notify::active-global-search", self._on_global_search_changed)
|
|
|
27 |
|
|
|
28 |
# Bliss loaded the apps_tree menu here, let's connect to
|
|
|
29 |
# the index
|
|
|
30 |
self.db = recoll.connect()
|
|
|
31 |
|
|
|
32 |
self.db.setAbstractParams(maxchars=200,
|
|
|
33 |
contextwords=4)
|
|
|
34 |
|
|
|
35 |
self.timeout_id = None
|
|
|
36 |
|
|
|
37 |
def get_search_string (self):
|
|
|
38 |
search = self.props.active_search
|
|
|
39 |
return search.props.search_string if search else None
|
|
|
40 |
|
|
|
41 |
def get_global_search_string (self):
|
|
|
42 |
search = self.props.active_global_search
|
|
|
43 |
return search.props.search_string if search else None
|
|
|
44 |
|
|
|
45 |
def search_finished (self):
|
|
|
46 |
search = self.props.active_search
|
|
|
47 |
if search:
|
|
|
48 |
search.emit("finished")
|
|
|
49 |
|
|
|
50 |
def global_search_finished (self):
|
|
|
51 |
search = self.props.active_global_search
|
|
|
52 |
if search:
|
|
|
53 |
search.emit("finished")
|
|
|
54 |
def reset (self):
|
|
|
55 |
self._do_browse (self.props.results_model)
|
|
|
56 |
self._do_browse (self.props.global_results_model)
|
|
|
57 |
|
|
|
58 |
def _on_search_changed (self, scope, param_spec):
|
|
|
59 |
search = self.get_search_string()
|
|
|
60 |
results = scope.props.results_model
|
|
|
61 |
|
|
|
62 |
# print "Search changed to: '%s'" % search
|
|
|
63 |
|
|
|
64 |
self._update_results_model (search, results)
|
|
|
65 |
|
|
|
66 |
def _on_global_search_changed (self, scope, param_spec):
|
|
|
67 |
search = self.get_global_search_string()
|
|
|
68 |
results = scope.props.global_results_model
|
|
|
69 |
|
|
|
70 |
# print "Global search changed to: '%s'" % search
|
|
|
71 |
|
|
|
72 |
self._update_results_model (search, results)
|
|
|
73 |
|
|
|
74 |
def _update_results_model (self, search_string, model):
|
|
|
75 |
if search_string:
|
|
|
76 |
self._do_search (search_string, model)
|
|
|
77 |
else:
|
|
|
78 |
self._do_browse (model)
|
|
|
79 |
|
|
|
80 |
def _do_browse (self, model):
|
|
|
81 |
if self.timeout_id is not None:
|
|
|
82 |
GObject.source_remove(self.timeout_id)
|
|
|
83 |
model.clear ()
|
|
|
84 |
|
|
|
85 |
if model is self.props.results_model:
|
|
|
86 |
self.search_finished()
|
|
|
87 |
else:
|
|
|
88 |
self.global_search_finished()
|
|
|
89 |
|
|
|
90 |
def _on_timeout(self, search_string, model):
|
|
|
91 |
if self.timeout_id is not None:
|
|
|
92 |
GObject.source_remove(self.timeout_id)
|
|
|
93 |
self.timeout_id = None
|
|
|
94 |
self._really_do_search(search_string, model)
|
|
|
95 |
if model is self.props.results_model:
|
|
|
96 |
self.search_finished()
|
|
|
97 |
else:
|
|
|
98 |
self.global_search_finished()
|
|
|
99 |
|
|
|
100 |
def _do_search (self, search_string, model):
|
|
|
101 |
if TYPING_TIMEOUT == 0:
|
|
|
102 |
self._really_do_search(search_string, model)
|
|
|
103 |
return True
|
|
|
104 |
|
|
|
105 |
if self.timeout_id is not None:
|
|
|
106 |
GObject.source_remove(self.timeout_id)
|
|
|
107 |
self.timeout_id = \
|
|
|
108 |
GObject.timeout_add(TYPING_TIMEOUT, self._on_timeout,
|
|
|
109 |
search_string, model)
|
|
|
110 |
|
|
|
111 |
def _really_do_search(self, search_string, model):
|
|
|
112 |
# print "really_do_search:[", search_string, "]"
|
|
|
113 |
|
|
|
114 |
model.clear ()
|
|
|
115 |
if search_string == "":
|
|
|
116 |
return True
|
|
|
117 |
|
|
|
118 |
# Do the recoll thing
|
|
|
119 |
query = self.db.query()
|
|
|
120 |
try:
|
|
|
121 |
nres = query.execute(search_string)
|
|
|
122 |
except:
|
|
|
123 |
return
|
|
|
124 |
|
|
|
125 |
actual_results = 0
|
|
|
126 |
while query.next >= 0 and query.next < nres:
|
|
|
127 |
try:
|
|
|
128 |
doc = query.fetchone()
|
|
|
129 |
except:
|
|
|
130 |
break
|
|
|
131 |
|
|
|
132 |
# No sense in returning unusable results (until
|
|
|
133 |
# I get an idea of what to do with them)
|
|
|
134 |
if doc.ipath != "":
|
|
|
135 |
continue
|
|
|
136 |
|
|
|
137 |
titleorfilename = doc.title
|
|
|
138 |
if titleorfilename == "":
|
|
|
139 |
titleorfilename = doc.filename
|
|
|
140 |
|
|
|
141 |
icon = Gio.content_type_get_icon(doc.mimetype)
|
|
|
142 |
if icon:
|
|
|
143 |
iconname = icon.get_names()[0]
|
|
|
144 |
|
|
|
145 |
abstract = self.db.makeDocAbstract(doc, query).encode('utf-8')
|
|
|
146 |
|
|
|
147 |
model.append (doc.url,
|
|
|
148 |
iconname,
|
|
|
149 |
CATEGORY_ALL,
|
|
|
150 |
doc.mimetype,
|
|
|
151 |
titleorfilename,
|
|
|
152 |
abstract,
|
|
|
153 |
doc.url)
|
|
|
154 |
|
|
|
155 |
actual_results += 1
|
|
|
156 |
if actual_results >= 20:
|
|
|
157 |
break
|
|
|
158 |
|