37
37
gettext.textdomain(APP_NAME)
38
38
_ = gettext.gettext
40
BUS_NAME = 'com.canonical.Unity.Scope.Help.Texdoc'
41
BUS_PATH = '/com/canonical/unity/scope/help/texdoc'
40
GROUP_NAME = 'com.canonical.Unity.Scope.Help.Texdoc'
41
UNIQUE_PATH = '/com/canonical/unity/scope/help/texdoc'
43
SEARCH_HINT = _('Search Texdoc')
44
NO_RESULTS_HINT = _('Sorry, there are no Texdoc results that match your search.')
45
PROVIDER_CREDITS = _('')
43
46
SVG_DIR = '/usr/share/icons/unity-icon-theme/places/svg/'
44
CAT_0_ICON = Gio.ThemedIcon.new(SVG_DIR + 'group-help.svg')
45
CAT_0_TITLE = _('Texdoc')
47
NO_RESULTS_HINT = _('Sorry, there are no Texdoc results that match your search.')
48
SEARCH_HINT = _('Search Texdoc')
47
PROVIDER_ICON = SVG_DIR + 'service-texdoc.svg'
48
DEFAULT_RESULT_ICON = SVG_DIR + 'result-help.svg'
49
DEFAULT_RESULT_MIMETYPE = 'text/html'
50
DEFAULT_RESULT_TYPE = Unity.ResultType.DEFAULT
51
51
TEXDOC_RE = re.compile('^\s*(\d+)\s+(.+?)$')
56
self.scope = Unity.Scope.new(BUS_PATH, 'texdoc')
57
self.scope.props.search_hint = SEARCH_HINT
58
self.scope.search_in_global = True
60
cats.append(Unity.Category.new(CAT_0_TITLE.lower(),
63
Unity.CategoryRenderer.VERTICAL_TILE))
64
self.scope.props.categories = cats
65
self.preferences = Unity.PreferencesManager.get_default()
66
self.preferences.connect('notify::remote-content-search', self._on_preference_changed)
67
self.scope.connect('search-changed', self.on_search_changed)
70
def _on_preference_changed(self, *_):
71
self.scope.queue_search_changed(Unity.SearchType.DEFAULT)
73
def on_search_changed(self, scope, search, search_type, *_):
74
model = search.props.results_model
76
if self.preferences.props.remote_content_search != Unity.PreferencesManagerRemoteContent.ALL:
79
search_string = search.props.search_string.strip()
80
print('Search changed to \'%s\'' % search_string)
81
self.update_results_model(search_string, model)
82
search.set_reply_hint('no-results-hint',
83
GLib.Variant.new_string(NO_RESULTS_HINT))
86
def update_results_model(self, search, results):
89
if len(search) > 2: # don't run texdoc for strings shorter than 3 chars
90
proc = Popen(["texdoc", "--list", "--nointeract", search], stdout=PIPE)
91
os.waitpid(proc.pid, 0)
92
out = proc.communicate()[0]
93
for line in out.splitlines():
94
m = TEXDOC_RE.match(line)
96
result.append(m.group(2))
98
info = self.get_file_info(res)
100
# Only handle known file types, otherwise we might get
101
# Makefiles and things like that.
102
results.append(uri=info['uri'],
103
icon_hint=info['icon'].to_string(),
105
mimetype=info['mimetype'],
106
title=info['filename'],
107
comment=info['title'],
109
result_type=Unity.ResultType.DEFAULT)
111
def get_file_info(self, filepath):
113
info['uri'] = 'file://' + filepath
114
info['filename'] = os.path.basename(filepath)
115
info['mimetype'] = guess_type(info['uri'])[0] or ''
117
info['icon'] = Gio.content_type_get_icon(info['mimetype'])
118
if info['mimetype'] == 'application/pdf' and PDF_AVAILABLE:
119
info['title'] = self.get_pdf_title(filepath)
120
elif info['mimetype'] == 'text/html':
121
info['title'] = self.get_html_title(filepath)
53
c1 = {'id': 'technical',
54
'name': _('Technical Documents'),
55
'icon': SVG_DIR + 'group-installed.svg',
56
'renderer': Unity.CategoryRenderer.VERTICAL_TILE}
64
def get_file_info(filepath):
66
info['uri'] = 'file://' + filepath
67
info['filename'] = os.path.basename(filepath)
68
info['mimetype'] = guess_type(info['uri'])[0] or ''
70
info['icon'] = Gio.content_type_get_icon(info['mimetype'])
71
if info['mimetype'] == 'application/pdf' and PDF_AVAILABLE:
72
info['title'] = get_pdf_title(filepath)
73
elif info['mimetype'] == 'text/html':
74
info['title'] = get_html_title(filepath)
125
info['icon'] = Gio.ThemedIcon.new('text-x-generic')
126
76
info['title'] = ''
129
def get_pdf_title(self, filepath):
130
with open(filepath, 'rb') as pdffile:
131
reader = PdfFileReader(pdffile)
133
return reader.documentInfo.title or ''
137
def get_html_title(self, filepath):
139
class HTMLTitleParser(HTMLParser):
141
HTMLParser.__init__(self)
78
info['icon'] = Gio.ThemedIcon.new('text-x-generic')
83
def get_pdf_title(filepath):
84
with open(filepath, 'rb') as pdffile:
85
reader = PdfFileReader(pdffile)
87
return reader.documentInfo.title or ''
92
def get_html_title(filepath):
94
class HTMLTitleParser(HTMLParser):
96
HTMLParser.__init__(self)
98
self.title_complete = False
101
def handle_starttag(self, tag, attrs):
105
def handle_data(self, data):
109
def handle_endtag(self, tag):
111
self.title_complete = True
142
112
self.is_title = False
143
self.title_complete = False
146
def handle_starttag(self, tag, attrs):
150
def handle_data(self, data):
154
def handle_endtag(self, tag):
156
self.title_complete = True
157
self.is_title = False
159
with open(filepath, 'r') as htmlfile:
160
parser = HTMLTitleParser()
161
for line in htmlfile:
163
if parser.title_complete:
167
if __name__ == '__main__':
168
daemon = UnityExtras.dbus_own_name(BUS_NAME, Daemon, None)
170
GLib.unix_signal_add(0, 2, lambda x: daemon.quit(), None)
114
with open(filepath, 'r') as htmlfile:
115
parser = HTMLTitleParser()
116
for line in htmlfile:
118
if parser.title_complete:
122
def search(search, filters):
124
Search for help documents matching the search string
128
if len(search) > 2: # don't run texdoc for strings shorter than 3 chars
129
proc = Popen(["texdoc", "--list", "--nointeract", search], stdout=PIPE)
130
os.waitpid(proc.pid, 0)
131
out = proc.communicate()[0]
132
out = out.decode('utf8')
133
for line in out.splitlines():
134
m = TEXDOC_RE.match(line)
136
result.append(m.group(2))
138
info = get_file_info(res)
140
# Only handle known file types, otherwise we might get
141
# Makefiles and things like that.
142
results.append({'uri': info['uri'],
143
'icon': info['icon'].to_string(),
145
'mimetype': info['mimetype'],
146
'title': info['filename'],
147
'comment': info['title']})
151
def activate(scope, uri):
153
Open the url in the default webbrowser
155
uri: The url to be opened
157
uri = uri[:len(uri) - 3]
158
parameters = ["devhelp", "-s", uri]
159
subprocess.Popen(parameters)
160
return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='')
163
# Classes below this point establish communication
164
# with Unity, you probably shouldn't modify them.
167
class MySearch(Unity.ScopeSearchBase):
168
def __init__(self, search_context):
169
super(MySearch, self).__init__()
170
self.set_search_context(search_context)
174
Adds results to the model
177
result_set = self.search_context.result_set
178
for i in search(self.search_context.search_query,
179
self.search_context.filter_state):
180
if not 'uri' in i or not i['uri'] or i['uri'] == '':
182
if not 'icon' in i or not i['icon'] or i['icon'] == '':
183
i['icon'] = DEFAULT_RESULT_ICON
184
if not 'mimetype' in i or not i['mimetype'] or i['mimetype'] == '':
185
i['mimetype'] = DEFAULT_RESULT_MIMETYPE
186
if not 'result_type' in i or not i['result_type'] or i['result_type'] == '':
187
i['result_type'] = DEFAULT_RESULT_TYPE
188
if not 'category' in i or not i['category'] or i['category'] == '':
190
if not 'title' in i or not i['title']:
192
if not 'comment' in i or not i['comment']:
194
if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '':
195
i['dnd_uri'] = i['uri']
199
for m in EXTRA_METADATA:
201
i['metadata'][e] = i[e]
202
i['metadata']['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS)
203
result = Unity.ScopeResult.create(str(i['uri']), str(i['icon']),
204
i['category'], i['result_type'],
205
str(i['mimetype']), str(i['title']),
206
str(i['comment']), str(i['dnd_uri']),
208
result_set.add_result(result)
209
except Exception as error:
213
class Scope(Unity.AbstractScope):
215
Unity.AbstractScope.__init__(self)
217
def do_get_search_hint(self):
220
def do_get_schema(self):
222
Adds specific metadata fields
224
schema = Unity.Schema.new()
226
for m in EXTRA_METADATA:
227
schema.add_field(m['id'], m['type'], m['field'])
228
#FIXME should be REQUIRED for credits
229
schema.add_field('provider_credits', 's', Unity.SchemaFieldType.OPTIONAL)
232
def do_get_categories(self):
236
cs = Unity.CategorySet.new()
239
cat = Unity.Category.new(c['id'], c['name'],
240
Gio.ThemedIcon.new(c['icon']),
245
def do_get_filters(self):
249
fs = Unity.FilterSet.new()
254
def do_get_group_name(self):
257
def do_get_unique_name(self):
260
def do_create_search_for_query(self, search_context):
261
se = MySearch(search_context)