]> git.lyx.org Git - lyx.git/blob - 3rdparty/evince_sync/evince_backward_search
Fix text direction of references with XeTeX/bidi
[lyx.git] / 3rdparty / evince_sync / evince_backward_search
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # Copyright (C) 2010 Jose Aliste <jose.aliste@gmail.com>
5 #               2011 Benjamin Kellermann <Benjamin.Kellermann@tu-dresden.de>
6 #
7 # Modified for the use with LyX by Juergen Spitzmueller <spitz@lyx.org> 2017.
8 #
9 # This program is free software; you can redistribute it and/or modify it under
10 # the terms of the GNU General Public Licence as published by the Free Software
11 # Foundation; either version 2 of the Licence, or (at your option) any later
12 # version.
13 #
14 # This program is distributed in the hope that it will be useful, but WITHOUT
15 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE.  See the GNU General Public Licence for more
17 # details.
18 #
19 # You should have received a copy of the GNU General Public Licence along with
20 # this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
21 # Street, Fifth Floor, Boston, MA  02110-1301, USA
22
23 # Incorporates fixes from http://ubuntuforums.org/showthread.php?t=1716268
24 from __future__ import print_function
25
26 import dbus
27 import subprocess
28 import time
29 import sys
30 import traceback
31
32 if sys.version_info < (3,):
33     from urllib import unquote as urllib_unquote
34     from urllib import quote
35
36     def unquote(s):
37         # This is to deal with source files with non-ascii names
38         # We get url-quoted UTF-8 from dbus; convert to url-quoted ascii
39         # and then unquote. If you don't first convert ot ascii, it fails.
40         # It's a bit magical, but it seems to work
41         return urllib_unquote(s.encode('ascii'))
42 else:
43     from urllib.parse import unquote
44     from urllib.parse import quote
45
46 RUNNING, CLOSED = range(2)
47
48 EV_DAEMON_PATH = "/org/gnome/evince/Daemon"
49 EV_DAEMON_NAME = "org.gnome.evince.Daemon"
50 EV_DAEMON_IFACE = "org.gnome.evince.Daemon"
51
52 EVINCE_PATH = "/org/gnome/evince/Evince"
53 EVINCE_IFACE = "org.gnome.evince.Application"
54
55 EV_WINDOW_IFACE = "org.gnome.evince.Window"
56
57
58 class EvinceWindowProxy:
59
60     """A DBus proxy for an Evince Window."""
61     daemon = None
62     bus = None
63
64     def __init__(self, uri, editor, spawn=False, logger=None):
65         self._log = logger
66         self.uri = uri
67         self.uri_unquoted = unquote(uri)
68         self.editor = editor
69         self.status = CLOSED
70         self.source_handler = None
71         self.dbus_name = ''
72         self._handler = None
73
74         try:
75             if EvinceWindowProxy.bus is None:
76                 EvinceWindowProxy.bus = dbus.SessionBus()
77
78             if EvinceWindowProxy.daemon is None:
79                 EvinceWindowProxy.daemon = \
80                     EvinceWindowProxy.bus.get_object(
81                         EV_DAEMON_NAME,
82                         EV_DAEMON_PATH,
83                         follow_name_owner_changes=True
84                     )
85
86             EvinceWindowProxy.bus.add_signal_receiver(
87                 self._on_doc_loaded,
88                 signal_name='DocumentLoaded',
89                 dbus_interface=EV_WINDOW_IFACE,
90                 sender_keyword='sender'
91             )
92             self._get_dbus_name(False)
93         except dbus.DBusException:
94             traceback.print_exc()
95             if self._log:
96                 self._log.debug("Could not connect to the Evince Daemon")
97
98     def _on_doc_loaded(self, uri, **keyargs):
99         if (
100             unquote(uri) == self.uri_unquoted and
101             self._handler is None
102         ):
103             self.handle_find_document_reply(keyargs['sender'])
104
105     def _get_dbus_name(self, spawn):
106         EvinceWindowProxy.daemon.FindDocument(
107             self.uri,
108             spawn,
109             reply_handler=self.handle_find_document_reply,
110             error_handler=self.handle_find_document_error,
111             dbus_interface=EV_DAEMON_IFACE
112         )
113
114     def handle_find_document_error(self, error):
115         if self._log:
116             self._log.debug("FindDocument DBus call has failed")
117
118     def handle_find_document_reply(self, evince_name):
119         if self._handler is not None:
120             handler = self._handler
121         else:
122             handler = self.handle_get_window_list_reply
123         if evince_name != '':
124             self.dbus_name = evince_name
125             self.status = RUNNING
126             self.evince = EvinceWindowProxy.bus.get_object(
127                 self.dbus_name, EVINCE_PATH
128             )
129
130             self.evince.GetWindowList(
131                 dbus_interface=EVINCE_IFACE,
132                 reply_handler=handler,
133                 error_handler=self.handle_get_window_list_error
134             )
135
136     def handle_get_window_list_error(self, e):
137         if self._log:
138             self._log.debug("GetWindowList DBus call has failed")
139
140     def handle_get_window_list_reply(self, window_list):
141         if len(window_list) > 0:
142             window_obj = EvinceWindowProxy.bus.get_object(
143                 self.dbus_name, window_list[0]
144             )
145             self.window = dbus.Interface(window_obj, EV_WINDOW_IFACE)
146             self.window.connect_to_signal("SyncSource", self.on_sync_source)
147         else:
148             # This should never happen.
149             if self._log:
150                 self._log.debug("GetWindowList returned empty list")
151
152     def on_sync_source(self, input_file, source_link, _):
153         input_file = unquote(input_file)
154         # Remove the "file://" prefix
155         input_file = input_file[7:]
156         #print("File: " + input_file + ":" + str(source_link[0]))
157         cmd = self.editor.replace('%f', input_file)
158         cmd = cmd.replace('%l', str(source_link[0]))
159         #print(cmd)
160         subprocess.call(cmd, shell=True)
161         if self.source_handler is not None:
162             self.source_handler(input_file, source_link, time)
163
164
165 # This file offers backward search in any editor.
166 #  evince_dbus pdf_file line_source input_file
167 if __name__ == '__main__':
168     import dbus.mainloop.glib
169     import sys
170     import os
171
172     def print_usage():
173         print("""Usage:
174   evince_backward_search pdf_file "editorcmd %f %l"'
175     %f ... TeX-file to load
176     %l ... line to jump to
177 E.g.:
178   evince_backward_search somepdf.pdf "gvim --servername somepdf --remote-silent '+%l<Enter>' %f"
179   evince_backward_search somepdf.pdf "emacsclient -a emacs --no-wait +%l %f"
180   evince_backward_search somepdf.pdf "scite %f '-goto:%l'"
181   evince_backward_search somepdf.pdf "lyxclient -g %f %l"
182   evince_backward_search somepdf.pdf "kate --use --line %l"
183   evince_backward_search somepdf.pdf "kile --line %l" """)
184         sys.exit(1)
185
186     if len(sys.argv) != 3:
187         print_usage()
188
189     pdf_file = os.path.abspath(sys.argv[1])
190
191     if not os.path.isfile(pdf_file):
192         print_usage()
193
194     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
195     EvinceWindowProxy(
196         # The PDF file name MUST be URI-encoded
197         # RFC 1738: unreserved = alpha | digit | safe | extra
198         #           safe       = "$" | "-" | "_" | "." | "+"
199         #           extra      = "!" | "*" | "'" | "(" | ")" | ","
200         'file://' + quote(pdf_file, "/$+!*'(),@=~"),
201         sys.argv[2],
202         True
203     )
204
205     try:
206         import gobject
207         loop = gobject.MainLoop()
208     except ImportError:
209         from gi.repository import GLib
210         loop = GLib.MainLoop()
211     loop.run()
212 # ex:ts=4:et: