~ubuntu-branches/ubuntu/raring/gedit-plugins/raring

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: