diff --git a/3rdparty/evince_sync/README b/3rdparty/evince_sync/README new file mode 100644 index 0000000000..84b3b4ff4e --- /dev/null +++ b/3rdparty/evince_sync/README @@ -0,0 +1,49 @@ +=============================================== +FORWARD AND BACKWARD SEARCH WITH LYX AND EVINCE +=============================================== + +For the forward and backwards (reverse) search feature in general, please refer to +Help > Additional Features, sec. 5.6 and 5.7 in LyX. + + +SETUP +===== + +* Install the files evince_sync_lyx, evince_forward_search and evince_backward_search + in your binary directory (e.g., ~/bin). + +* Assure all three files are executable. + +* In LyX, go to Tools > Preferences ... > File Handling > File Formats > Format, select + the appropriate output format [e.g., PDF (pdflatex)], set "Viewer" to "Custom" and + enter evince_sync_lyx as custom viewer (in the field right to the combo box). + Hit "Apply" and "Save". + +* Go to Tools > Preferences ... > Output > General and enter the following PDF command + to Forward Search: evince_forward_search "$$o" $$n "$$f" + Again, hit "Apply" and "Save". + +Forward and Backward search should work now (backward search from within evince is triggered +by + ). + + +HISTORY +======= + +The scripts have been initially developed by Benjamin Kellermann (2011) as a derivation +of gedit-synctex-plugin by José Aliste (https://github.com/jaliste/gedit-synctex-plugin, +2010) and published on https://ubuntuforums.org/showthread.php?t=1716268. + +The work is based on a further derivation of this work for Sublime Text LaTeX Tools +(https://github.com/SublimeText/LaTeXTools/tree/master/evince). + +Adaptations for the use with LyX have been done by Jürgen Spitzmüller +in 2017. + + +CONTACT +======= + +Please send bug reports and suggestions (related to LyX-evince synchronization) to +lyx-devel@lists.lyx.org. +Usage questions should be addressed at lyx-users@lists.lyx.org. diff --git a/3rdparty/evince_sync/evince_backward_search b/3rdparty/evince_sync/evince_backward_search new file mode 100755 index 0000000000..7d00a106a3 --- /dev/null +++ b/3rdparty/evince_sync/evince_backward_search @@ -0,0 +1,212 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Jose Aliste +# 2011 Benjamin Kellermann +# +# Modified for the use with LyX by Juergen Spitzmueller 2017. +# +# 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 + +# Incorporates fixes from http://ubuntuforums.org/showthread.php?t=1716268 +from __future__ import print_function + +import dbus +import subprocess +import time +import sys +import traceback + +if sys.version_info < (3,): + from urllib import unquote as urllib_unquote + from urllib import quote + + def unquote(s): + # This is to deal with source files with non-ascii names + # We get url-quoted UTF-8 from dbus; convert to url-quoted ascii + # and then unquote. If you don't first convert ot ascii, it fails. + # It's a bit magical, but it seems to work + return urllib_unquote(s.encode('ascii')) +else: + from urllib.parse import unquote + from urllib.parse import quote + +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, editor, spawn=False, logger=None): + self._log = logger + self.uri = uri + self.uri_unquoted = unquote(uri) + self.editor = editor + 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: + traceback.print_exc() + if self._log: + self._log.debug("Could not connect to the Evince Daemon") + + def _on_doc_loaded(self, uri, **keyargs): + if ( + unquote(uri) == self.uri_unquoted 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("SyncSource", self.on_sync_source) + else: + # This should never happen. + if self._log: + self._log.debug("GetWindowList returned empty list") + + def on_sync_source(self, input_file, source_link, _): + input_file = unquote(input_file) + # Remove the "file://" prefix + input_file = input_file[7:] + #print("File: " + input_file + ":" + str(source_link[0])) + cmd = self.editor.replace('%f', input_file) + cmd = cmd.replace('%l', str(source_link[0])) + #print(cmd) + subprocess.call(cmd, shell=True) + if self.source_handler is not None: + self.source_handler(input_file, source_link, time) + + +# This file offers backward search in any editor. +# evince_dbus pdf_file line_source input_file +if __name__ == '__main__': + import dbus.mainloop.glib + import sys + import os + + def print_usage(): + print("""Usage: + evince_backward_search pdf_file "editorcmd %f %l"' + %f ... TeX-file to load + %l ... line to jump to +E.g.: + evince_backward_search somepdf.pdf "gvim --servername somepdf --remote-silent '+%l' %f" + evince_backward_search somepdf.pdf "emacsclient -a emacs --no-wait +%l %f" + evince_backward_search somepdf.pdf "scite %f '-goto:%l'" + evince_backward_search somepdf.pdf "lyxclient -g %f %l" + evince_backward_search somepdf.pdf "kate --use --line %l" + evince_backward_search somepdf.pdf "kile --line %l" """) + sys.exit(1) + + if len(sys.argv) != 3: + print_usage() + + pdf_file = os.path.abspath(sys.argv[1]) + + if not os.path.isfile(pdf_file): + print_usage() + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + EvinceWindowProxy( + # The PDF file name MUST be URI-encoded + # RFC 1738: unreserved = alpha | digit | safe | extra + # safe = "$" | "-" | "_" | "." | "+" + # extra = "!" | "*" | "'" | "(" | ")" | "," + 'file://' + quote(pdf_file, "/$+!*'(),@=~"), + sys.argv[2], + True + ) + + try: + import gobject + loop = gobject.MainLoop() + except ImportError: + from gi.repository import GLib + loop = GLib.MainLoop() + loop.run() +# ex:ts=4:et: diff --git a/3rdparty/evince_sync/evince_forward_search b/3rdparty/evince_sync/evince_forward_search new file mode 100755 index 0000000000..bee4f2d2c1 --- /dev/null +++ b/3rdparty/evince_sync/evince_forward_search @@ -0,0 +1,72 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 Jose Aliste +# 2011 Benjamin Kellermann +# +# 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 + +# This file offers forward search for evince. +from __future__ import print_function + +import dbus +import sys +import os +import traceback + +if sys.version_info < (3,): + from urllib import quote +else: + from urllib.parse import quote + +if __name__ == '__main__': + def print_usage(): + print('Usage: evince_forward_search pdf_file line_number tex_file') + sys.exit(1) + + if len(sys.argv) != 4: + print_usage() + try: + line_number = int(sys.argv[2]) + except ValueError: + print_usage() + + pdf_file = os.path.abspath(sys.argv[1]) + line = int(sys.argv[2]) + tex_file = os.path.abspath(sys.argv[3]) + + try: + bus = dbus.SessionBus() + daemon = bus.get_object( + 'org.gnome.evince.Daemon', '/org/gnome/evince/Daemon' + ) + dbus_name = daemon.FindDocument( + # The PDF file name MUST be URI-encoded + # RFC 1738: unreserved = alpha | digit | safe | extra + # safe = "$" | "-" | "_" | "." | "+" + # extra = "!" | "*" | "'" | "(" | ")" | "," + 'file://' + quote(pdf_file, "/$+!*'(),@=~"), + True, + dbus_interface="org.gnome.evince.Daemon" + ) + window = bus.get_object(dbus_name, '/org/gnome/evince/Window/0') + window.SyncView( + tex_file, + (line_number, 1), + 0, # GDK_CURRENT_TIME constant + dbus_interface="org.gnome.evince.Window" + ) + except dbus.DBusException: + traceback.print_exc() diff --git a/3rdparty/evince_sync/evince_sync_lyx b/3rdparty/evince_sync/evince_sync_lyx new file mode 100755 index 0000000000..83b8fefe89 --- /dev/null +++ b/3rdparty/evince_sync/evince_sync_lyx @@ -0,0 +1,54 @@ +#!/usr/bin/python + +# Copyright (C) 2010 Jose Aliste +# 2011 Benjamin Kellermann +# +# Translated from Bash to Python by Juergen Spitzmueller 2017. +# +# 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 sys, os.path +from subprocess import Popen, call + +editor_cmd = "lyxclient -g %f %l" + +def print_usage(): + print("Usage: evince_sync_lyx pdf_file") + sys.exit(1) + +if len(sys.argv) != 2: + print_usage() + +pdf_file = os.path.abspath(sys.argv[1]) + +if not os.path.isfile(pdf_file): + print_usage() + +synctex_file, ext = os.path.splitext(pdf_file) + +synctex_file += ".synctex.gz" + +SyncTeX = False + +if os.path.isfile(synctex_file): + bsproc = Popen(["evince_backward_search", pdf_file, editor_cmd]) + SyncTeX = True + +vproc = Popen(['evince', pdf_file]) +vproc.wait() + +if SyncTeX: + bsproc.terminate() +