[gedit-plugins] Revert "Remove synctex plugin"



commit 3f135d1719f1e6418fcbf175fdac1b48ca086eb5
Author: Random User <random237849 gmx at>
Date:   Fri Sep 20 17:15:53 2019 +0000

    Revert "Remove synctex plugin"
    
    This reverts commit 9f59fd8292e1c0cc88824010a61b32a049444632.

 help/C/synctex.page                           |  56 +++++
 help/meson.build                              |   1 +
 meson.build                                   |   1 +
 meson_options.txt                             |   1 +
 plugins/synctex/gedit-synctex.metainfo.xml.in |  16 ++
 plugins/synctex/meson.build                   |  48 ++++
 plugins/synctex/synctex.plugin.desktop.in.in  |  10 +
 plugins/synctex/synctex/__init__.py           |   7 +
 plugins/synctex/synctex/evince_dbus.py        | 187 ++++++++++++++
 plugins/synctex/synctex/synctex.py            | 349 ++++++++++++++++++++++++++
 po/POTFILES.in                                |   3 +
 11 files changed, 679 insertions(+)
---
diff --git a/help/C/synctex.page b/help/C/synctex.page
new file mode 100644
index 0000000..5657531
--- /dev/null
+++ b/help/C/synctex.page
@@ -0,0 +1,56 @@
+<page xmlns="http://projectmallard.org/1.0/";
+      xmlns:its="http://www.w3.org/2005/11/its";
+      type="topic" style="task"
+      id="plugin-synctex">
+
+  <info>
+    <link type="guide" xref="gedit-plugin-guide#gedit-additional-plugins"/>
+    <!-- seealso links to different help pages do not work?! -->
+    <!-- <link type="seealso" xref="help:evince#synctex"/> -->
+    <!-- <link type="seealso" xref="help:gedit-latex#synctex"/> -->
+    <revision pkgversion="3.34" date="2019-09-16" status="review"/>
+
+    <credit type="author">
+      <name></name>
+      <email its:translate="no"></email>
+    </credit>
+
+    <include href="legal-plugins.xml" xmlns="http://www.w3.org/2001/XInclude"/>
+
+    <desc>Quickly switch between TeX source files and rendered PDF documents
+    in evince.</desc>
+  </info>
+
+  <title>SyncTeX</title>
+
+  <p>SyncTeX is a method that enables synchronization between a TeX source file
+  and the PDF output generated by a TeX/LaTeX compiler. This plugin allows to
+  use SyncTeX in <app>gedit</app> to quickly switch from an opened TeX source
+  file to a PDF document opened in <app>evince</app> and vice versa.</p>
+
+  <note style="warning">
+  <p>To be useful, this plugin requires a TeX/LaTeX compiler and SyncTeX to be
+  installed. Both are provided by TeX distributions such as TeX Live.
+  Also <app>evince</app> must be installed.</p>
+  </note>
+
+  <list>
+    <item>
+      <p>Some basic information on setting up SyncTeX and compiling LaTeX
+      documents can be found in the
+      <link type="guide" xref="help:evince#synctex"><app>evince</app>-SyncTeX
+      documentation</link>.</p>
+    </item>
+    <item>
+      <p>Key shortcuts and mouse controls are also documented in the
+      <link type="guide" xref="help:evince/synctex-search">
+      <app>evince</app>-SyncTeX documentation</link>.</p>
+    </item>
+    <item>
+      <p><link type="guide" xref="help:gnome-latex">
+      <app>Gnome-LaTeX</app></link> is an alternative text editor specifically
+      designed for writing TeX code.</p>
+    </item>
+  </list>
+
+</page>
diff --git a/help/meson.build b/help/meson.build
index bf6b967..204c28b 100644
--- a/help/meson.build
+++ b/help/meson.build
@@ -11,6 +11,7 @@ gedit_help_sources = [
   'legal-plugins.xml',
   'multi-edit.page',
   'session-saver.page',
+  'synctex.page',
   'terminal.page',
   'text-size.page',
   'translate.page',
diff --git a/meson.build b/meson.build
index b782657..9f965e7 100644
--- a/meson.build
+++ b/meson.build
@@ -93,6 +93,7 @@ all_plugins = {
   'multiedit': {'language': 'python'},
   'sessionsaver': {'language': 'python'},
   'smartspaces': {'language': 'python'},
+  'synctex': {'language': 'python'},
   'terminal': {'language': 'python'},
   'textsize': {'language': 'python'},
   'translate': {'language': 'python'},
diff --git a/meson_options.txt b/meson_options.txt
index a7ec489..d5fab80 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -12,6 +12,7 @@ option('plugin_joinlines', type: 'boolean')
 option('plugin_multiedit', type: 'boolean')
 option('plugin_sessionsaver', type: 'boolean')
 option('plugin_smartspaces', type: 'boolean')
+option('plugin_synctex', type: 'boolean')
 option('plugin_terminal', type: 'boolean')
 option('plugin_textsize', type: 'boolean')
 option('plugin_translate', type: 'boolean')
diff --git a/plugins/synctex/gedit-synctex.metainfo.xml.in b/plugins/synctex/gedit-synctex.metainfo.xml.in
new file mode 100644
index 0000000..0557df9
--- /dev/null
+++ b/plugins/synctex/gedit-synctex.metainfo.xml.in
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2014 Igor Gnatenko <i gnatenko brain gmail com> -->
+<component type="addon">
+  <id>gedit-synctex</id>
+  <extends>org.gnome.gedit.desktop</extends>
+  <name>SyncTeX</name>
+  <summary>Synchronize between LaTeX and PDF with gedit and evince</summary>
+  <url type="homepage">https://wiki.gnome.org/Apps/Gedit/ShippedPlugins</url>
+  <url type="bugtracker">https://gitlab.gnome.org/GNOME/gedit-plugins/issues</url>
+  <suggests>
+    <id>evince.desktop</id>
+  </suggests>
+  <metadata_license>CC0-1.0</metadata_license>
+  <project_license>GPL-2.0+</project_license>
+  <update_contact>i gnatenko brain gmail com</update_contact>
+</component>
diff --git a/plugins/synctex/meson.build b/plugins/synctex/meson.build
new file mode 100644
index 0000000..c6be01d
--- /dev/null
+++ b/plugins/synctex/meson.build
@@ -0,0 +1,48 @@
+install_subdir(
+  'synctex',
+  install_dir: join_paths(
+    pkglibdir,
+    'plugins',
+  )
+)
+
+synctex_plugin_in = configure_file(
+  input: 'synctex.plugin.desktop.in.in',
+  output: 'synctex.plugin.desktop.in',
+  configuration: plugin_in,
+  install: false,
+)
+
+synctex_plugin = custom_target(
+  'synctex.plugin',
+  input: synctex_plugin_in,
+  output: 'synctex.plugin',
+  command: msgfmt_plugin_cmd,
+  install: true,
+  install_dir: join_paths(
+    pkglibdir,
+    'plugins',
+  )
+)
+
+synctex_metainfo = i18n.merge_file(
+  'gedit-synctex.metainfo.xml',
+  input: 'gedit-synctex.metainfo.xml.in',
+  output: 'gedit-synctex.metainfo.xml',
+  po_dir: join_paths(srcdir, 'po'),
+  type: 'xml',
+  install: true,
+  install_dir: appstreamdir,
+)
+
+if appstream_util.found()
+  test(
+    'validate-gedit-synctex.metainfo.xml',
+    appstream_util,
+    args: [
+      'validate-relax',
+      '--nonet',
+      synctex_metainfo.full_path(),
+    ]
+  )
+endif
diff --git a/plugins/synctex/synctex.plugin.desktop.in.in b/plugins/synctex/synctex.plugin.desktop.in.in
new file mode 100644
index 0000000..c2881f2
--- /dev/null
+++ b/plugins/synctex/synctex.plugin.desktop.in.in
@@ -0,0 +1,10 @@
+[Plugin]
+Loader=python3
+Module=synctex
+IAge=3
+Name=SyncTeX
+Description=Synchronize between LaTeX and PDF with gedit and evince.
+Authors=José Aliste <jaliste src gnome org>
+Copyright=Copyright © 2010 José Aliste
+Website=http://www.gedit.org
+Version=@VERSION@
diff --git a/plugins/synctex/synctex/__init__.py b/plugins/synctex/synctex/__init__.py
new file mode 100644
index 0000000..77f59d0
--- /dev/null
+++ b/plugins/synctex/synctex/__init__.py
@@ -0,0 +1,7 @@
+import gi
+gi.require_version('Gedit', '3.0')
+gi.require_version('Gtk', '3.0')
+gi.require_version('Peas', '1.0')
+gi.require_version('PeasGtk', '1.0')
+
+from .synctex import SynctexAppActivatable, SynctexWindowActivatable
diff --git a/plugins/synctex/synctex/evince_dbus.py b/plugins/synctex/synctex/evince_dbus.py
new file mode 100755
index 0000000..d8b3978
--- /dev/null
+++ b/plugins/synctex/synctex/evince_dbus.py
@@ -0,0 +1,187 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# This file is part of the Gedit Synctex plugin.
+#
+# Copyright (C) 2010 Jose Aliste <jose aliste gmail com>
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public Licence as published by the Free Software
+# Foundation; either version 2 of the Licence, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public Licence for more
+# details.
+#
+# You should have received a copy of the GNU General Public Licence along with
+# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+# Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+import dbus, subprocess
+
+RUNNING, CLOSED = range(2)
+
+EV_DAEMON_PATH = "/org/gnome/evince/Daemon"
+EV_DAEMON_NAME = "org.gnome.evince.Daemon"
+EV_DAEMON_IFACE = "org.gnome.evince.Daemon"
+
+EVINCE_PATH = "/org/gnome/evince/Evince"
+EVINCE_IFACE = "org.gnome.evince.Application"
+
+EV_WINDOW_IFACE = "org.gnome.evince.Window"
+
+
+
+class EvinceWindowProxy:
+    """A DBUS proxy for an Evince Window."""
+    daemon = None
+    bus = None
+
+    def __init__(self, uri, spawn = False, logger = None):
+        self._log = logger
+        self.uri = uri
+        self.spawn = spawn
+        self.status = CLOSED
+        self.source_handler = None
+        self.dbus_name = ''
+        self._handler = None
+        try:
+            if EvinceWindowProxy.bus is None:
+                EvinceWindowProxy.bus = dbus.SessionBus()
+
+            if EvinceWindowProxy.daemon is None:
+                EvinceWindowProxy.daemon = EvinceWindowProxy.bus.get_object(EV_DAEMON_NAME,
+                                                EV_DAEMON_PATH,
+                                                follow_name_owner_changes=True)
+            EvinceWindowProxy.bus.add_signal_receiver(self._on_doc_loaded, signal_name="DocumentLoaded", 
+                                                      dbus_interface = EV_WINDOW_IFACE, 
+                                                      sender_keyword='sender')
+            self._get_dbus_name(False)
+
+        except dbus.DBusException:
+            if self._log:
+                self._log.debug("Could not connect to the Evince Daemon")
+
+    def _on_doc_loaded(self, uri, **keyargs):
+        if uri == self.uri and self._handler is None:
+            self.handle_find_document_reply(keyargs['sender'])
+        
+    def _get_dbus_name(self, spawn):
+        EvinceWindowProxy.daemon.FindDocument(self.uri,spawn,
+                     reply_handler=self.handle_find_document_reply,
+                     error_handler=self.handle_find_document_error,
+                     dbus_interface = EV_DAEMON_IFACE)
+
+    def handle_find_document_error(self, error):
+        if self._log:
+            self._log.debug("FindDocument DBus call has failed")
+
+    def handle_find_document_reply(self, evince_name):
+        if self._handler is not None:
+            handler = self._handler
+        else:
+            handler = self.handle_get_window_list_reply
+        if evince_name != '':
+            self.dbus_name = evince_name
+            self.status = RUNNING
+            self.evince = EvinceWindowProxy.bus.get_object(self.dbus_name, EVINCE_PATH)
+            self.evince.GetWindowList(dbus_interface = EVINCE_IFACE,
+                          reply_handler = handler,
+                          error_handler = self.handle_get_window_list_error)
+
+    def handle_get_window_list_error (self, e):
+        if self._log:
+            self._log.debug("GetWindowList DBus call has failed")
+
+    def handle_get_window_list_reply (self, window_list):
+        if len(window_list) > 0:
+            window_obj = EvinceWindowProxy.bus.get_object(self.dbus_name, window_list[0])
+            self.window = dbus.Interface(window_obj,EV_WINDOW_IFACE)
+            self.window.connect_to_signal("Closed", self.on_window_close)
+            self.window.connect_to_signal("SyncSource", self.on_sync_source)
+        else:
+            #That should never happen. 
+            if self._log:
+                self._log.debug("GetWindowList returned empty list")
+
+
+    def set_source_handler (self, source_handler):
+        self.source_handler = source_handler
+
+    def on_window_close(self):
+        self.window = None
+        self.status = CLOSED
+
+    def on_sync_source(self, input_file, source_link, timestamp):
+        if self.source_handler is not None:
+            self.source_handler(input_file, source_link, timestamp)
+
+    def SyncView(self, input_file, data, time):
+        if self.status == CLOSED:
+            if self.spawn:
+                self._tmp_syncview = [input_file, data, time];
+                self._handler = self._syncview_handler
+                self._get_dbus_name(True)
+        else:
+            self.window.SyncView(input_file, data, time,  dbus_interface = "org.gnome.evince.Window")
+
+    def _syncview_handler(self, window_list):
+        self.handle_get_window_list_reply(window_list)
+
+        if self.status == CLOSED: 
+            return False
+        self.window.SyncView(self._tmp_syncview[0],self._tmp_syncview[1], self._tmp_syncview[2], 
dbus_interface="org.gnome.evince.Window")
+        del self._tmp_syncview
+        self._handler = None
+        return True
+
+## This file can be used as a script to support forward search and backward search in vim.
+## It should be easy to adapt to other editors. 
+##  evince_dbus  pdf_file  line_source input_file
+if __name__ == '__main__':
+    import dbus.mainloop.glib, sys, os, logging
+    from gi.repository import GObject
+
+    def print_usage():
+        print('''
+The usage is evince_dbus output_file line_number input_file from the directory of output_file.
+''')
+        sys.exit(1)
+
+    if len(sys.argv)!=4:
+        print_usage()
+    try:
+        line_number = int(sys.argv[2])
+    except ValueError:
+        print_usage()
+
+    output_file = sys.argv[1]
+    input_file  = sys.argv[3]
+    path_output  = os.getcwd() + '/' + output_file
+    path_input   = os.getcwd() + '/' + input_file
+
+    if not os.path.isfile(path_output):
+        print_usage()
+
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+    logger = logging.getLogger("evince_dbus")
+    logger.setLevel(logging.DEBUG)
+    ch = logging.StreamHandler()
+    ch.setLevel(logging.DEBUG)
+
+    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+
+    ch.setFormatter(formatter)
+
+    logger.addHandler(ch)    
+    a = EvinceWindowProxy('file://' + path_output, True,logger=logger)
+    
+    def sync_view(ev_window, path_input, line_number):
+        ev_window.SyncView (path_input, (line_number, 1),0)
+
+    GObject.timeout_add(400, sync_view, a, path_input, line_number)
+    loop = GObject.MainLoop()
+    loop.run() 
+# ex:ts=4:et:
diff --git a/plugins/synctex/synctex/synctex.py b/plugins/synctex/synctex/synctex.py
new file mode 100644
index 0000000..1badf51
--- /dev/null
+++ b/plugins/synctex/synctex/synctex.py
@@ -0,0 +1,349 @@
+# -*- coding: utf-8 -*-   
+
+#  synctex.py - Synctex support with Gedit and Evince.
+#  
+#  Copyright (C) 2010 - José Aliste <jose aliste gmail com>
+#  Copyright (C) 2015 - Germán Poo-Caamaño <gpoo gnome org>
+#  
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#   
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#   
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor,
+#  Boston, MA 02110-1301, USA.
+
+from gi.repository import GObject, Pango, Gtk, Gedit, Peas, PeasGtk, Gio, Gdk
+from .evince_dbus import EvinceWindowProxy
+import dbus.mainloop.glib
+import logging
+import os
+import re
+
+try:
+    import gettext
+    gettext.bindtextdomain('gedit-plugins')
+    gettext.textdomain('gedit-plugins')
+    _ = gettext.gettext
+except:
+    _ = lambda s: s
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+_logger = logging.getLogger("SynctexPlugin")
+
+def apply_style (style, tag):
+    def apply_style_prop(tag, style, prop):
+        if style.get_property(prop + "-set"):
+            tag.set_property(prop, style.get_property(prop))
+        else:
+            tag.set_property(prop, None)
+
+    def apply_style_prop_bool(tag, style, prop, whentrue, whenfalse):
+        if style.get_property(prop + "-set"):
+            prop_value = whentrue if style.get_property(prop) else whenfalse
+            tag.set_property(prop, prop_value)
+
+    apply_style_prop(tag, style, "foreground")
+    apply_style_prop(tag, style, "background")
+
+    apply_style_prop_bool(tag, style, "bold", Pango.Weight.BOLD, Pango.Weight.NORMAL)
+    apply_style_prop_bool(tag, style, "italic", Pango.Style.ITALIC, Pango.Style.NORMAL)
+    apply_style_prop_bool(tag, style, "underline", Pango.Underline.SINGLE,
+                          Pango.Underline.NONE)
+    apply_style_prop(tag, style, "strikethrough")
+
+def parse_modeline(text):
+    gedit_r = re.search(r'%+\s*mainfile:\s*(.*)$', text,
+                        re.IGNORECASE)
+    auctex_r = re.search(r'%+\s*TeX-master:\s*"(.*)"$', text,
+                         re.IGNORECASE)
+    if gedit_r:
+        return gedit_r.group(1)
+    elif auctex_r:
+        return auctex_r.group(1)
+    else:
+        return None
+
+
+class SynctexViewHelper:
+    def __init__(self, view, window, plugin):
+        self._view = view
+        self._window = window
+        self._plugin = plugin
+        self._doc = view.get_buffer()
+        self.window_proxy = None
+
+        self._handlers = [
+            self._doc.connect('saved', self.on_saved_or_loaded),
+            self._doc.connect('loaded', self.on_saved_or_loaded)
+        ]
+
+        self._highlight_tag = self._doc.create_tag()
+        self.active = False
+        self.last_iters = None
+        self.gfile = None
+        self.update_location()
+
+    def on_notify_style_scheme(self, doc, param_object):
+        apply_style (doc.get_style_scheme().get_style('search-match'), self._highlight_tag)
+
+    def on_button_release(self, view, event):
+        modifier_mask = Gtk.accelerator_get_default_mod_mask()
+        event_state = event.state & modifier_mask
+
+        if event.button == 1 and event_state == Gdk.ModifierType.CONTROL_MASK:
+            self.sync_view(event.time)
+
+    def on_saved_or_loaded(self, doc):
+        self.update_location()
+
+    def get_output_file(self):
+        file_output = None
+        line_count = self._doc.get_line_count()
+
+        for i in list(range(min(3,line_count))) + list(range(max(0,line_count - 3), line_count)):
+            start = self._doc.get_iter_at_line(i)
+            end = start.copy()
+            end.forward_to_line_end()
+            file_output = parse_modeline(self._doc.get_text(start, end, False))
+            if file_output is not None:
+                break
+
+        return file_output
+
+    def on_key_press(self, a, b):
+        self._unhighlight()
+
+    def on_cursor_moved(self, cur):
+        self._unhighlight()
+
+    def deactivate(self):
+        self._unhighlight()
+
+        for h in self._handlers:
+            self._doc.disconnect(h)
+
+        del self._highlight_tag
+
+    def update_location(self):
+        gfile = self._doc.get_file().get_location()
+
+        if gfile is None:
+            return
+
+        if self.gfile is None or gfile.get_uri() != self.gfile.get_uri():
+            SynctexWindowActivatable.view_dict[gfile.get_uri()] = self
+            self.gfile = gfile
+
+        modeline_output_file = self.get_output_file()
+
+        if modeline_output_file is not None:
+            filename = modeline_output_file
+        else:
+            filename = self.gfile.get_basename()
+
+        out_path = self.gfile.get_parent().get_child(filename).get_path()
+        out_path = os.path.splitext(out_path)
+        out_gfile = Gio.file_new_for_path(out_path[0] + ".pdf")
+
+        if out_gfile.query_exists(None):
+            self.out_gfile = out_gfile
+        else:
+            self.out_gfile = None
+
+        self.update_active()
+
+    def _highlight(self):
+        iter = self._doc.get_iter_at_mark(self._doc.get_insert())
+        end_iter = iter.copy()
+        end_iter.forward_to_line_end()
+
+        self._doc.apply_tag(self._highlight_tag, iter, end_iter)
+        self.last_iters = [iter, end_iter];
+
+    def _unhighlight(self):
+        if self.last_iters is not None:
+            self._doc.remove_tag(self._highlight_tag,
+                                 self.last_iters[0], self.last_iters[1])
+        self.last_iters = None
+
+    def goto_line (self, line, time):
+        self._doc.goto_line(line) 
+        self._view.scroll_to_cursor()
+        self._window.set_active_tab(Gedit.Tab.get_from_document(self._doc))
+        self._highlight()
+        self._window.present_with_time (time)
+
+    def goto_line_after_load(self, line, time):
+        GObject.idle_add (lambda : self.goto_line(line, time))
+        self._doc.disconnect(self._goto_handler)
+
+    def sync_view(self, time):
+        if self.active:
+            cursor_iter =  self._doc.get_iter_at_mark(self._doc.get_insert())
+            line = cursor_iter.get_line() + 1
+            col = cursor_iter.get_line_offset()
+            self.window_proxy.SyncView(self.gfile.get_path(), (line, col), time)
+
+    def update_active(self):
+        # Activate the plugin only if the doc is a LaTeX file.
+        lang = self._doc.get_language()
+        self.active = (lang is not None and lang.get_id() == 'latex' and
+                        self.out_gfile is not None)
+
+        if self.active and self.window_proxy is None:
+            self._doc_active_handlers = [
+                        self._doc.connect('cursor-moved', self.on_cursor_moved),
+                        self._doc.connect('notify::style-scheme', self.on_notify_style_scheme)]
+            self._view_active_handlers = [
+                        self._view.connect('key-press-event', self.on_key_press),
+                        self._view.connect('button-release-event', self.on_button_release)]
+
+
+            style = self._doc.get_style_scheme().get_style('search-match')
+            apply_style(style, self._highlight_tag)
+            self._window.lookup_action("synctex").set_enabled(True)
+            self.window_proxy = self._plugin.ref_evince_proxy(self.out_gfile, self._window)
+
+        elif not self.active and self.window_proxy is not None:
+            #destroy the evince window proxy.
+            for handler in self._doc_active_handlers:
+                self._doc.disconnect(handler)
+            for handler in self._view_active_handlers:
+                self._view.disconnect(handler)
+
+            self._window.lookup_action("synctex").set_enabled(False)
+            self._plugin.unref_evince_proxy(self.out_gfile)
+            self.window_proxy = None
+
+
+class SynctexWindowActivatable(GObject.Object, Gedit.WindowActivatable):
+    __gtype_name__ = "SynctexWindowActivatable"
+
+    window = GObject.Property(type=Gedit.Window)
+    view_dict = {}
+    _proxy_dict = {}
+
+    def __init__(self):
+        GObject.Object.__init__(self)
+
+    def do_activate(self):
+        action = Gio.SimpleAction(name="synctex")
+        action.connect('activate', self.forward_search_cb)
+        self.window.add_action(action)
+
+        for view in self.window.get_views():
+            self.add_helper(view, self.window)
+
+        self.handlers = [
+            self.window.connect("tab-added", lambda window, tab: self.add_helper(tab.get_view(), window)),
+            self.window.connect("tab-removed", lambda window, tab: self.remove_helper(tab.get_view())),
+        ]
+
+    def do_deactivate(self):
+        for h in self.handlers:
+            self.window.disconnect(h)
+
+        for view in self.window.get_views():
+            self.remove_helper(view)
+
+        self.window.remove_action("synctex")
+
+    def do_update_state(self):
+        view_helper = self.get_helper(self.window.get_active_view())
+
+        active = False
+        if view_helper is not None:
+            active = view_helper.active
+
+        self.window.lookup_action("synctex").set_enabled(active)
+
+    def add_helper(self, view, window):
+        helper = SynctexViewHelper(view, window, self)
+        location = view.get_buffer().get_file().get_location()
+
+        if location is not None:
+            self.view_dict[location.get_uri()] = helper
+        view.synctex_view_helper = helper
+
+    def remove_helper(self, view):
+        helper = self.get_helper(view)
+
+        if helper.gfile is not None:
+            del self.view_dict[helper.gfile.get_uri()]
+
+        helper.deactivate()
+        del view.synctex_view_helper
+
+    def get_helper(self, view):
+        if not hasattr(view, 'synctex_view_helper'):
+            return None
+        return view.synctex_view_helper
+
+    def forward_search_cb(self, action, parameter, user_data=None):
+        self.get_helper(self.window.get_active_view()).sync_view(Gtk.get_current_event_time())
+
+    def source_view_handler(self, out_gfile, uri_input, source_link, time):
+
+        if uri_input not in self.view_dict:
+            window = self._proxy_dict[out_gfile.get_uri()][2]
+
+            tab = window.create_tab_from_location(Gio.file_new_for_uri(uri_input),
+                                                  None, source_link[0] - 1, 0, False, True)
+
+            helper = self.get_helper(tab.get_view())
+            helper._goto_handler = tab.get_document().connect_object("loaded", 
+                                                SynctexViewHelper.goto_line_after_load,
+                                                helper, source_link[0] - 1, time)
+        else:
+            self.view_dict[uri_input].goto_line(source_link[0] - 1, time)
+
+    def ref_evince_proxy(self, gfile, window):
+        uri = gfile.get_uri()
+        proxy = None
+
+        if uri not in self._proxy_dict:
+            proxy = EvinceWindowProxy (uri, True, _logger)
+            self._proxy_dict[uri] = [1, proxy, window]
+            proxy.set_source_handler (lambda i, s, time: self.source_view_handler(gfile, i, s, time))
+        else:
+            self._proxy_dict[uri][0]+=1
+            proxy = self._proxy_dict[uri][1]
+
+        return proxy
+
+    def unref_evince_proxy(self, gfile):
+        uri = gfile.get_uri()
+
+        if uri in self._proxy_dict:
+            self._proxy_dict[uri][0] -= 1
+            if self._proxy_dict[uri][0] == 0:
+                del self._proxy_dict[uri]
+
+class SynctexAppActivatable(GObject.Object, Gedit.AppActivatable):
+
+    app = GObject.Property(type=Gedit.App)
+
+    def __init__(self):
+        GObject.Object.__init__(self)
+
+    def do_activate(self):
+        self.app.add_accelerator("<Primary><Alt>F", "win.synctex", None)
+
+        self.menu_ext = self.extend_menu("tools-section")
+        item = Gio.MenuItem.new(_("Forward Search"), "win.synctex")
+        self.menu_ext.append_menu_item(item)
+
+    def do_deactivate(self):
+        self.app.remove_accelerator("win.synctex", None)
+        self.menu_ext = None
+
+# ex:ts=4:et:
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2068bbf..344e034 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -49,6 +49,9 @@ plugins/sessionsaver/sessionsaver/ui/sessionsaver.ui
 plugins/sessionsaver/sessionsaver/windowactivable.py
 plugins/smartspaces/gedit-smartspaces.metainfo.xml.in
 plugins/smartspaces/smartspaces.plugin.desktop.in.in
+plugins/synctex/gedit-synctex.metainfo.xml.in
+plugins/synctex/synctex.plugin.desktop.in.in
+plugins/synctex/synctex/synctex.py
 plugins/terminal/gedit-terminal.metainfo.xml.in
 plugins/terminal/org.gnome.gedit.plugins.terminal.gschema.xml
 plugins/terminal/terminal.plugin.desktop.in.in


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]