1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
1 |
# -*- coding: utf-8 -*-
|
2 |
||
3 |
# synctex.py - Synctex support with Gedit and Evince.
|
|
4 |
#
|
|
5 |
# Copyright (C) 2010 - José Aliste <jose.aliste@gmail.com>
|
|
6 |
#
|
|
7 |
# This program is free software; you can redistribute it and/or modify
|
|
8 |
# it under the terms of the GNU General Public License as published by
|
|
9 |
# the Free Software Foundation; either version 2 of the License, or
|
|
10 |
# (at your option) any later version.
|
|
11 |
#
|
|
12 |
# This program is distributed in the hope that it will be useful,
|
|
13 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 |
# GNU General Public License for more details.
|
|
16 |
#
|
|
17 |
# You should have received a copy of the GNU General Public License
|
|
18 |
# along with this program; if not, write to the Free Software
|
|
1.4.10
by Robert Ancell
Import upstream version 3.5.2 |
19 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
20 |
# Boston, MA 02110-1301, USA.
|
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
21 |
|
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
22 |
from gi.repository import GObject, Pango, Gtk, Gedit, Peas, PeasGtk, Gio, Gdk |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
23 |
from evince_dbus import EvinceWindowProxy |
24 |
import dbus.mainloop.glib |
|
25 |
import logging |
|
26 |
import gettext |
|
1.4.1
by Michael Biebl
Import upstream version 3.0.6 |
27 |
import os |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
28 |
from gpdefs import * |
29 |
||
30 |
try: |
|
31 |
gettext.bindtextdomain(GETTEXT_PACKAGE, GP_LOCALEDIR) |
|
32 |
_ = lambda s: gettext.dgettext(GETTEXT_PACKAGE, s); |
|
33 |
except: |
|
34 |
_ = lambda s: s |
|
35 |
||
36 |
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |
|
37 |
||
38 |
ui_str = """<ui> |
|
39 |
<menubar name="MenuBar">
|
|
40 |
<menu name="ToolsMenu" action="Tools">
|
|
41 |
<placeholder name="ToolsOps_2">
|
|
42 |
<separator/>
|
|
43 |
<menuitem name="Synctex" action="SynctexForwardSearch"/>
|
|
44 |
</placeholder>
|
|
45 |
</menu>
|
|
46 |
</menubar>
|
|
47 |
</ui>
|
|
48 |
"""
|
|
49 |
||
50 |
_logger = logging.getLogger("SynctexPlugin") |
|
51 |
||
52 |
def apply_style (style, tag): |
|
53 |
def apply_style_prop(tag, style, prop): |
|
54 |
if style.get_property(prop + "-set"): |
|
55 |
tag.set_property(prop, style.get_property(prop)) |
|
56 |
else: |
|
57 |
tag.set_property(prop, None) |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
58 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
59 |
def apply_style_prop_bool(tag, style, prop, whentrue, whenfalse): |
60 |
if style.get_property(prop + "-set"): |
|
61 |
prop_value = whentrue if style.get_property(prop) else whenfalse |
|
62 |
tag.set_property(prop, prop_value) |
|
63 |
||
64 |
apply_style_prop(tag, style, "foreground") |
|
65 |
apply_style_prop(tag, style, "background") |
|
66 |
||
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
67 |
apply_style_prop_bool(tag, style, "bold", Pango.Weight.BOLD, Pango.Weight.NORMAL) |
68 |
apply_style_prop_bool(tag, style, "italic", Pango.Style.ITALIC, Pango.Style.NORMAL) |
|
69 |
apply_style_prop_bool(tag, style, "underline", Pango.Underline.SINGLE, |
|
70 |
Pango.Underline.NONE) |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
71 |
apply_style_prop(tag, style, "strikethrough") |
72 |
||
73 |
def parse_modeline(line): |
|
74 |
index = line.find("mainfile:") |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
75 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
76 |
if line.startswith("%") and index > 0: |
77 |
# Parse the modeline.
|
|
78 |
return line[index + len("mainfile:"):].strip() |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
79 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
80 |
return None |
81 |
||
82 |
class SynctexViewHelper: |
|
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
83 |
def __init__(self, view, window, plugin): |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
84 |
self._view = view |
85 |
self._window = window |
|
86 |
self._plugin = plugin |
|
87 |
self._doc = view.get_buffer() |
|
88 |
self.window_proxy = None |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
89 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
90 |
self._handlers = [ |
91 |
self._doc.connect('saved', self.on_saved_or_loaded), |
|
92 |
self._doc.connect('loaded', self.on_saved_or_loaded) |
|
93 |
]
|
|
94 |
||
95 |
self._highlight_tag = self._doc.create_tag() |
|
96 |
self.active = False |
|
97 |
self.last_iters = None |
|
98 |
self.gfile = None |
|
99 |
self.update_location() |
|
100 |
||
101 |
def on_notify_style_scheme(self, doc, param_object): |
|
102 |
apply_style (doc.get_style_scheme().get_style('search-match'), self._highlight_tag) |
|
103 |
||
104 |
def on_button_release(self, view, event): |
|
105 |
modifier_mask = Gtk.accelerator_get_default_mod_mask() |
|
106 |
event_state = event.state & modifier_mask |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
107 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
108 |
if event.button == 1 and event_state == Gdk.ModifierType.CONTROL_MASK: |
109 |
self.sync_view(event.time) |
|
110 |
||
111 |
def on_saved_or_loaded(self, doc, data): |
|
112 |
self.update_location() |
|
113 |
||
114 |
def get_output_file(self): |
|
115 |
file_output = None |
|
116 |
line_count = self._doc.get_line_count() |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
117 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
118 |
for i in range(min(3,line_count)) + range(max(0,line_count - 3), line_count): |
119 |
start = self._doc.get_iter_at_line(i) |
|
120 |
end = start.copy() |
|
121 |
end.forward_to_line_end() |
|
122 |
file_output = parse_modeline(self._doc.get_text(start, end, False)) |
|
123 |
if file_output is not None: |
|
124 |
break
|
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
125 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
126 |
return file_output |
127 |
||
128 |
def on_key_press(self, a, b): |
|
129 |
self._unhighlight() |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
130 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
131 |
def on_cursor_moved(self, cur): |
132 |
self._unhighlight() |
|
133 |
||
134 |
def deactivate(self): |
|
135 |
self._unhighlight() |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
136 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
137 |
for h in self._handlers: |
138 |
self._doc.disconnect(h) |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
139 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
140 |
del self._highlight_tag |
141 |
||
142 |
def update_location(self): |
|
143 |
gfile = self._doc.get_location() |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
144 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
145 |
if gfile is None: |
146 |
return
|
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
147 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
148 |
if self.gfile is None or gfile.get_uri() != self.gfile.get_uri(): |
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
149 |
SynctexWindowActivatable.view_dict[gfile.get_uri()] = self |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
150 |
self.gfile = gfile |
151 |
||
152 |
modeline_output_file = self.get_output_file() |
|
153 |
||
154 |
if modeline_output_file is not None: |
|
155 |
filename = modeline_output_file |
|
156 |
else: |
|
157 |
filename = self.gfile.get_basename() |
|
158 |
||
159 |
out_path = self.gfile.get_parent().get_child(filename).get_path() |
|
160 |
out_path = os.path.splitext(out_path) |
|
161 |
out_gfile = Gio.file_new_for_path(out_path[0] + ".pdf") |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
162 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
163 |
if out_gfile.query_exists(None): |
164 |
self.out_gfile = out_gfile |
|
165 |
else: |
|
166 |
self.out_gfile = None |
|
167 |
||
168 |
self.update_active() |
|
169 |
||
170 |
def _highlight(self): |
|
171 |
iter = self._doc.get_iter_at_mark(self._doc.get_insert()) |
|
172 |
end_iter = iter.copy() |
|
173 |
end_iter.forward_to_line_end() |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
174 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
175 |
self._doc.apply_tag(self._highlight_tag, iter, end_iter) |
176 |
self.last_iters = [iter, end_iter]; |
|
177 |
||
178 |
def _unhighlight(self): |
|
179 |
if self.last_iters is not None: |
|
180 |
self._doc.remove_tag(self._highlight_tag, |
|
181 |
self.last_iters[0], self.last_iters[1]) |
|
182 |
self.last_iters = None |
|
183 |
||
184 |
def goto_line (self, line, time): |
|
185 |
self._doc.goto_line(line) |
|
186 |
self._view.scroll_to_cursor() |
|
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
187 |
self._window.set_active_tab(Gedit.Tab.get_from_document(self._doc)) |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
188 |
self._highlight() |
189 |
self._window.present_with_time (time) |
|
190 |
||
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
191 |
def goto_line_after_load(self, a, line, time): |
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
192 |
GObject.idle_add (lambda : self.goto_line(line, time)) |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
193 |
self._doc.disconnect(self._goto_handler) |
194 |
||
195 |
def sync_view(self, time): |
|
196 |
if self.active: |
|
197 |
cursor_iter = self._doc.get_iter_at_mark(self._doc.get_insert()) |
|
198 |
line = cursor_iter.get_line() + 1 |
|
199 |
col = cursor_iter.get_line_offset() |
|
200 |
self.window_proxy.SyncView(self.gfile.get_path(), (line, col), time) |
|
201 |
||
202 |
def update_active(self): |
|
203 |
# Activate the plugin only if the doc is a LaTeX file.
|
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
204 |
lang = self._doc.get_language() |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
205 |
self.active = (lang is not None and lang.get_id() == 'latex' and |
206 |
self.out_gfile is not None) |
|
207 |
||
208 |
if self.active and self.window_proxy is None: |
|
209 |
self._doc_active_handlers = [ |
|
210 |
self._doc.connect('cursor-moved', self.on_cursor_moved), |
|
211 |
self._doc.connect('notify::style-scheme', self.on_notify_style_scheme)] |
|
212 |
self._view_active_handlers = [ |
|
213 |
self._view.connect('key-press-event', self.on_key_press), |
|
214 |
self._view.connect('button-release-event', self.on_button_release)] |
|
215 |
||
216 |
||
217 |
style = self._doc.get_style_scheme().get_style('search-match') |
|
218 |
apply_style(style, self._highlight_tag) |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
219 |
self._plugin._action_group.set_sensitive(True) |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
220 |
self.window_proxy = self._plugin.ref_evince_proxy(self.out_gfile, self._window) |
221 |
||
222 |
elif not self.active and self.window_proxy is not None: |
|
223 |
#destroy the evince window proxy.
|
|
224 |
for handler in self._doc_active_handlers: |
|
225 |
self._doc.disconnect(handler) |
|
226 |
for handler in self._view_active_handlers: |
|
227 |
self._view.disconnect(handler) |
|
228 |
||
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
229 |
self._plugin._action_group.set_sensitive(False) |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
230 |
self._plugin.unref_evince_proxy(self.out_gfile) |
231 |
self.window_proxy = None |
|
232 |
||
233 |
||
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
234 |
class SynctexWindowActivatable(GObject.Object, Gedit.WindowActivatable): |
235 |
__gtype_name__ = "SynctexWindowActivatable" |
|
236 |
||
237 |
window = GObject.property(type=Gedit.Window) |
|
238 |
view_dict = {} |
|
239 |
_proxy_dict = {} |
|
240 |
||
241 |
def __init__(self): |
|
242 |
GObject.Object.__init__(self) |
|
243 |
||
244 |
def do_activate(self): |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
245 |
self._insert_menu() |
246 |
||
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
247 |
for view in self.window.get_views(): |
248 |
self.add_helper(view, self.window) |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
249 |
|
250 |
self.handlers = [ |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
251 |
self.window.connect("tab-added", lambda window, tab: self.add_helper(tab.get_view(), window)), |
252 |
self.window.connect("tab-removed", lambda window, tab: self.remove_helper(tab.get_view())), |
|
253 |
self.window.connect("active-tab-changed", self.on_active_tab_changed) |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
254 |
]
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
255 |
|
256 |
def do_deactivate(self): |
|
257 |
for h in self.handlers: |
|
258 |
self.window.disconnect(h) |
|
259 |
||
260 |
for view in self.window.get_views(): |
|
261 |
self.remove_helper(view) |
|
262 |
||
263 |
self._remove_menu() |
|
264 |
||
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
265 |
def on_active_tab_changed(self, window, tab): |
1.4.9
by Robert Ancell
Import upstream version 3.5.1 |
266 |
view_helper = self.get_helper(tab.get_view()) |
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
267 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
268 |
if view_helper is None: |
269 |
active = False |
|
270 |
else: |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
271 |
active = view_helper.active |
272 |
||
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
273 |
self._action_group.set_sensitive(active) |
274 |
||
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
275 |
def add_helper(self, view, window): |
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
276 |
helper = SynctexViewHelper(view, window, self) |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
277 |
location = view.get_buffer().get_location() |
278 |
||
279 |
if location is not None: |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
280 |
self.view_dict[location.get_uri()] = helper |
1.4.9
by Robert Ancell
Import upstream version 3.5.1 |
281 |
view.synctex_view_helper = helper |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
282 |
|
283 |
def remove_helper(self, view): |
|
1.4.9
by Robert Ancell
Import upstream version 3.5.1 |
284 |
helper = self.get_helper(view) |
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
285 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
286 |
if helper.gfile is not None: |
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
287 |
del self.view_dict[helper.gfile.get_uri()] |
288 |
||
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
289 |
helper.deactivate() |
1.4.9
by Robert Ancell
Import upstream version 3.5.1 |
290 |
del view.synctex_view_helper |
291 |
||
292 |
def get_helper(self, view): |
|
293 |
if not hasattr(view, 'synctex_view_helper'): |
|
294 |
return None |
|
295 |
return view.synctex_view_helper |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
296 |
|
297 |
def _remove_menu(self): |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
298 |
manager = self.window.get_ui_manager() |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
299 |
manager.remove_ui(self._ui_id) |
300 |
manager.remove_action_group(self._action_group) |
|
301 |
manager.ensure_update() |
|
302 |
||
303 |
def _insert_menu(self): |
|
304 |
# Get the GtkUIManager
|
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
305 |
manager = self.window.get_ui_manager() |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
306 |
|
307 |
# Create a new action group
|
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
308 |
self._action_group = Gtk.ActionGroup(name="SynctexWindowActivatableActions") |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
309 |
self._action_group.add_actions([("SynctexForwardSearch", None, |
310 |
_("Forward Search"), "<Ctrl><Alt>F", |
|
311 |
_("Forward Search"), self.forward_search_cb)]) |
|
312 |
||
313 |
# Insert the action group
|
|
314 |
manager.insert_action_group(self._action_group, -1) |
|
315 |
||
316 |
# Merge the UI
|
|
317 |
self._ui_id = manager.add_ui_from_string(ui_str) |
|
318 |
||
1.4.12
by Robert Ancell
Import upstream version 3.6.1 |
319 |
def forward_search_cb(self, action, what=None): |
1.4.9
by Robert Ancell
Import upstream version 3.5.1 |
320 |
self.get_helper(self.window.get_active_view()).sync_view(Gtk.get_current_event_time()) |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
321 |
|
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
322 |
def source_view_handler(self, out_gfile, uri_input, source_link, time): |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
323 |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
324 |
if uri_input not in self.view_dict: |
325 |
window = self._proxy_dict[out_gfile.get_uri()][2] |
|
326 |
||
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
327 |
tab = window.create_tab_from_location(Gio.file_new_for_uri(uri_input), |
328 |
None, source_link[0] - 1, 0, False, True) |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
329 |
|
1.4.9
by Robert Ancell
Import upstream version 3.5.1 |
330 |
helper = self.get_helper(tab.get_view()) |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
331 |
helper._goto_handler = tab.get_document().connect_object("loaded", |
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
332 |
SynctexViewHelper.goto_line_after_load, |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
333 |
helper, source_link[0] - 1, time) |
334 |
else: |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
335 |
self.view_dict[uri_input].goto_line(source_link[0] - 1, time) |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
336 |
|
337 |
def ref_evince_proxy(self, gfile, window): |
|
338 |
uri = gfile.get_uri() |
|
339 |
proxy = None |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
340 |
|
341 |
if uri not in self._proxy_dict: |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
342 |
proxy = EvinceWindowProxy (uri, True, _logger) |
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
343 |
self._proxy_dict[uri] = [1, proxy, window] |
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
344 |
proxy.set_source_handler (lambda i, s, time: self.source_view_handler(gfile, i, s, time)) |
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
345 |
else: |
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
346 |
self._proxy_dict[uri][0]+=1 |
347 |
proxy = self._proxy_dict[uri][1] |
|
348 |
||
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
349 |
return proxy |
350 |
||
351 |
def unref_evince_proxy(self, gfile): |
|
352 |
uri = gfile.get_uri() |
|
1.3.2
by Jeremy Bicha
Import upstream version 3.1.2 |
353 |
|
354 |
if uri in self._proxy_dict: |
|
355 |
self._proxy_dict[uri][0] -= 1 |
|
356 |
if self._proxy_dict[uri][0] == 0: |
|
357 |
del self._proxy_dict[uri] |
|
1.3.1
by Michael Biebl
Import upstream version 3.0.5 |
358 |
|
1.1.26
by Josselin Mouette
Import upstream version 3.0.3 |
359 |
# ex:ts=4:et:
|