[gedit-plugins/gnome-2-32] Add synctex plugin.
- From: Jose Aliste <jaliste src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gedit-plugins/gnome-2-32] Add synctex plugin.
- Date: Tue, 10 Aug 2010 00:31:38 +0000 (UTC)
commit 857e328cd5d8305c1b0610ead5e000088989ae83
Author: Jose Aliste <jaliste src gnome org>
Date: Sun Aug 8 12:32:33 2010 -0400
Add synctex plugin.
README | 3 +-
configure.ac | 9 +-
plugins/synctex/Makefile.am | 15 ++
plugins/synctex/synctex.gedit-plugin.desktop.in.in | 11 +
plugins/synctex/synctex/Makefile.am | 12 +
plugins/synctex/synctex/__init__.py | 1 +
plugins/synctex/synctex/evince_dbus.py | 117 ++++++++++
plugins/synctex/synctex/synctex.py | 230 ++++++++++++++++++++
8 files changed, 394 insertions(+), 4 deletions(-)
---
diff --git a/README b/README
index 7a642ee..ef61d15 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
General Information
===================
-This is version 2.30.0 of gedit-plugins. gedit-plugins are a set of plugins
+This is version 2.31.1 of gedit-plugins. gedit-plugins are a set of plugins
for gedit.
Installation
@@ -31,5 +31,6 @@ showtabbar Add a menu entry to show/hide the tabbar.
smartspaces Forget you're not using tabulations.
terminal Embed a terminal in the bottom pane.
wordcompletion Word completion using the completion framework.
+synctex SyncTeX synchronization of TeX files and PDF output.
all All of the above plugins
diff --git a/configure.ac b/configure.ac
index 04e1a98..6e07dc0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -89,9 +89,9 @@ ALL_PLUGINS="bookmarks showtabbar charmap drawspaces wordcompletion"
USEFUL_PLUGINS="bookmarks showtabbar charmap drawspaces wordcompletion"
DEFAULT_PLUGINS="bookmarks showtabbar charmap drawspaces wordcompletion"
-PYTHON_ALL_PLUGINS="bracketcompletion codecomment colorpicker commander joinlines multiedit sessionsaver smartspaces terminal"
-PYTHON_USEFUL_PLUGINS="bracketcompletion codecomment colorpicker commander joinlines multiedit sessionsaver smartspaces terminal"
-PYTHON_DEFAULT_PLUGINS="bracketcompletion codecomment colorpicker commander joinlines multiedit sessionsaver smartspaces terminal"
+PYTHON_ALL_PLUGINS="bracketcompletion codecomment colorpicker commander joinlines multiedit sessionsaver smartspaces terminal synctex"
+PYTHON_USEFUL_PLUGINS="bracketcompletion codecomment colorpicker commander joinlines multiedit sessionsaver smartspaces terminal synctex"
+PYTHON_DEFAULT_PLUGINS="bracketcompletion codecomment colorpicker commander joinlines multiedit sessionsaver smartspaces terminal synctex"
DIST_PLUGINS="$ALL_PLUGINS $PYTHON_ALL_PLUGINS"
@@ -446,6 +446,9 @@ plugins/terminal/Makefile
plugins/terminal/terminal.gedit-plugin.desktop.in
plugins/wordcompletion/Makefile
plugins/wordcompletion/wordcompletion.gedit-plugin.desktop.in
+plugins/synctex/Makefile
+plugins/synctex/synctex/Makefile
+plugins/synctex/synctex.gedit-plugin.desktop.in
po/Makefile.in])
AC_OUTPUT
diff --git a/plugins/synctex/Makefile.am b/plugins/synctex/Makefile.am
new file mode 100644
index 0000000..fbf28bb
--- /dev/null
+++ b/plugins/synctex/Makefile.am
@@ -0,0 +1,15 @@
+# SyncTeX Plugin
+SUBDIRS = synctex
+plugindir = $(GEDIT_PLUGINS_LIBS_DIR)
+
+plugin_in_files = synctex.gedit-plugin.desktop.in
+%.gedit-plugin: %.gedit-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+plugin_DATA = $(plugin_in_files:.gedit-plugin.desktop.in=.gedit-plugin)
+
+EXTRA_DIST = $(plugin_in_files)
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/synctex/synctex.gedit-plugin.desktop.in.in b/plugins/synctex/synctex.gedit-plugin.desktop.in.in
new file mode 100644
index 0000000..94d1d56
--- /dev/null
+++ b/plugins/synctex/synctex.gedit-plugin.desktop.in.in
@@ -0,0 +1,11 @@
+[Gedit Plugin]
+Loader=python
+Module=synctex
+IAge=1
+_Name=SyncTeX
+_Description=Synchronize between LaTeX and PDF with gedit and evince.
+Icon=gedit-plugin
+Authors=José Aliste <jaliste src gnome org>
+Copyright=Copyright © 2010 José Aliste
+Website=http://www.gedit.org
+Name= VERSION@
diff --git a/plugins/synctex/synctex/Makefile.am b/plugins/synctex/synctex/Makefile.am
new file mode 100644
index 0000000..a61e475
--- /dev/null
+++ b/plugins/synctex/synctex/Makefile.am
@@ -0,0 +1,12 @@
+# Synctex Plugin
+#
+plugindir = $(GEDIT_PLUGINS_LIBS_DIR)/synctex
+plugin_PYTHON = \
+ __init__.py \
+ synctex.py \
+ evince_dbus.py
+
+CLEANFILES =
+DISTCLEANFILES =
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/synctex/synctex/__init__.py b/plugins/synctex/synctex/__init__.py
new file mode 100644
index 0000000..92e6fba
--- /dev/null
+++ b/plugins/synctex/synctex/__init__.py
@@ -0,0 +1 @@
+from synctex import SynctexPlugin
diff --git a/plugins/synctex/synctex/evince_dbus.py b/plugins/synctex/synctex/evince_dbus.py
new file mode 100644
index 0000000..bf0d128
--- /dev/null
+++ b/plugins/synctex/synctex/evince_dbus.py
@@ -0,0 +1,117 @@
+#!/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, time
+from traceback import print_exc
+
+RUNNING, CLOSED = range(2)
+
+class EvinceWindowProxy:
+ """A DBUS proxy for an Evince Window."""
+ daemon = None
+ bus = None
+ def __init__(self, uri, spawn = False):
+ self.uri = uri
+ self.spawn = spawn
+ self.status = CLOSED
+ self.source_handler = None
+ try:
+ if EvinceWindowProxy.bus is None:
+ EvinceWindowProxy.bus = dbus.SessionBus()
+ if EvinceWindowProxy.daemon is None:
+ EvinceWindowProxy.daemon = EvinceWindowProxy.bus.get_object('org.gnome.evince.Daemon',
+ '/org/gnome/evince/Daemon')
+ self._get_dbus_name(False)
+ except dbus.DBusException:
+ print_exc()
+
+ def _get_dbus_name(self, spawn):
+ try:
+ self.dbus_name = self.daemon.FindDocument(self.uri,spawn, dbus_interface = "org.gnome.evince.Daemon")
+ if self.dbus_name != '':
+ self.status = RUNNING
+ self.window = EvinceWindowProxy.bus.get_object(self.dbus_name, '/org/gnome/evince/Window/0')
+ self.window.connect_to_signal("Closed", self.on_window_close, dbus_interface="org.gnome.evince.Window")
+ self.window.connect_to_signal("SyncSource", self.on_sync_source, dbus_interface="org.gnome.evince.Window")
+ except dbus.DBusException:
+ print_exc()
+ def set_source_handler (self, source_handler):
+ self.source_handler = source_handler
+
+ def on_window_close(self):
+ self.status = CLOSED
+ self.window = None
+
+ def on_sync_source(self, input_file, source_link):
+ if self.source_handler is not None:
+ self.source_handler(input_file, source_link)
+
+ def name_owner_changed_cb(self, service_name, old_owner, new_owner):
+ if service_name == self.dbus_name and new_owner == '':
+ self.status = CLOSED
+
+ def SyncView(self, input_file, data):
+ if self.status == CLOSED:
+ print "Evince is closed"
+ if self.spawn:
+ print "Spawning evince"
+ self._get_dbus_name(True)
+ # if self.status is still closed, it means there is a
+ # problem running evince.
+ if self.status == CLOSED:
+ return False
+ self.window.SyncView(input_file, data, dbus_interface="org.gnome.evince.Window")
+ return False
+
+## 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, gobject, glib, sys, os
+
+ 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)
+ bus = dbus.SessionBus()
+
+ a = EvinceWindowProxy(bus, 'file://' + path_output, True )
+ glib.timeout_add(1000, a.SyncView, path_input, (line_number,1))
+ loop = gobject.MainLoop()
+ loop.run()
diff --git a/plugins/synctex/synctex/synctex.py b/plugins/synctex/synctex/synctex.py
new file mode 100644
index 0000000..8779bd7
--- /dev/null
+++ b/plugins/synctex/synctex/synctex.py
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+
+# synctex.py - Synctex support with Gedit and Evince.
+#
+# Copyright (C) 2010 - José 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 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., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307, USA.
+
+import gtk, gedit, gio , gtk.gdk as gdk
+from gettext import gettext as _
+from evince_dbus import EvinceWindowProxy
+import dbus.mainloop.glib
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+
+ui_str = """<ui>
+ <menubar name="MenuBar">
+ <menu name="ToolsMenu" action="Tools">
+ <placeholder name="ToolsOps_2">
+ <separator/>
+ <menuitem name="ExamplePy" action="SynctexForwardSearch"/>
+ </placeholder>
+ </menu>
+ </menubar>
+</ui>
+"""
+
+VIEW_DATA_KEY = "SynctexPluginViewData"
+WINDOW_DATA_KEY = "SynctexPluginWindowData"
+
+
+class SynctexViewHelper:
+
+ mime_types = ['text/x-tex'];
+
+ def __init__(self, view, window, tab):
+ self._view = view
+ self._window = window
+ self._tab = tab
+ 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.active = False
+ self.last_iters = None
+ self.uri = self._doc.get_uri()
+ self.mime_type = self._doc.get_mime_type()
+ self.update_uri_mime_type()
+ self._highlight_tag = self._doc.create_tag()
+ self._highlight_tag.set_property("background", "#dddddd")
+
+ def on_button_release(self, view, event):
+ if event.button == 1 and event.state & gdk.CONTROL_MASK:
+ self.sync_view()
+ def on_saved_or_loaded(self, doc, data):
+ self.update_uri_mime_type()
+
+ def on_cursor_moved(self, cur):
+ self._unhighlight()
+
+ def deactivate(self):
+ for h in self._handlers:
+ self._doc.disconnect(h)
+
+ def update_uri_mime_type(self):
+ uri = self._doc.get_uri()
+ if uri is not None and uri != self.uri:
+ self._window.get_data(WINDOW_DATA_KEY).view_dict[uri] = self
+ self.uri = uri
+ if self.uri is not None:
+ self.file = gio.File(self.uri)
+ self.mime_type = self._doc.get_mime_type()
+ 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):
+ self._doc.goto_line(line)
+ self._view.scroll_to_cursor()
+ self._window.set_active_tab(self._tab)
+ self._highlight()
+
+ def source_view_handler(self, input_file, source_link):
+ if self.file.get_basename() == input_file:
+ self.goto_line(source_link[0] - 1)
+ else:
+ uri_input = self.file.get_parent().get_child(input_file).get_uri()
+ view_dict = self._window.get_data(WINDOW_DATA_KEY).view_dict
+ if uri_input in view_dict:
+ view_dict[uri_input].goto_line(source_link[0] - 1)
+ else:
+ self._window.create_tab_from_uri(uri_input,
+ None, source_link[0]-1, False, True)
+ self._window.present()
+
+ def sync_view(self):
+ 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.file.get_path(), (line, col))
+
+ def update_active(self):
+ # Activate the plugin only if the doc is a LaTeX file.
+ self.active = (self.mime_type in self.mime_types)
+
+ if self.active and self.window_proxy is None:
+ if self.uri.endswith(".tex"):
+ self._active_handlers = [
+ self._doc.connect('cursor-moved', self.on_cursor_moved),
+ self._view.connect('button-release-event',self.on_button_release)]
+
+ self._window.get_data(WINDOW_DATA_KEY)._action_group.set_sensitive(True)
+ uri_output = self.uri[:-3] + "pdf"
+ self.window_proxy = EvinceWindowProxy (uri_output, True)
+ self.window_proxy.set_source_handler (self.source_view_handler)
+ elif not self.active and self.window_proxy is not None:
+ # destroy the evince window proxy.
+ self._doc.disconnect(self._active_handlers[0])
+ self._view.disconnect(self._active_handlers[1])
+ self._window.get_data(WINDOW_DATA_KEY)._action_group.get_sensitive(False)
+ self.window_proxy = None
+
+
+class SynctexWindowHelper:
+ def __init__(self, plugin, window):
+ self._window = window
+ self._plugin = plugin
+ self._insert_menu()
+ self.view_dict = {}
+
+ for view in window.get_views():
+ self.add_helper(view)
+
+ self.handlers = [
+ window.connect("tab-added", lambda w, t: self.add_helper(t.get_view(),w, t)),
+ window.connect("tab-removed", lambda w, t: self.remove_helper(t.get_view())),
+ window.connect("active-tab-changed", self.on_active_tab_changed)
+ ]
+ def on_active_tab_changed(self, window, tab):
+ view_helper = tab.get_view().get_data(VIEW_DATA_KEY)
+ if view_helper is None:
+ active = False
+ else:
+ active = view_helper.active
+ self._action_group.set_sensitive(active)
+
+
+ def add_helper(self, view, window, tab):
+ helper = SynctexViewHelper(view, window, tab)
+ if helper.uri is not None:
+ self.view_dict[helper.uri] = helper
+ view.set_data (VIEW_DATA_KEY, helper)
+
+ def remove_helper(self, view):
+ helper = view.get_data(VIEW_DATA_KEY)
+ if helper.uri is not None:
+ del self.view_dict[helper.uri]
+ helper.deactivate()
+ view.set_data(VIEW_DATA_KEY, None)
+
+ def __del__(self):
+ self._window = None
+ self._plugin = None
+
+ def deactivate(self):
+ for h in self.handlers:
+ self._window.disconnect(h)
+
+ def _insert_menu(self):
+ # Get the GtkUIManager
+ manager = self._window.get_ui_manager()
+
+ # Create a new action group
+ self._action_group = gtk.ActionGroup("ExamplePyPluginActions")
+ self._action_group.add_actions([("SynctexForwardSearch", None, _("Forward Search"),
+ None, _("Forward Search"),
+ self.forward_search_cb)])
+
+ # Insert the action group
+ manager.insert_action_group(self._action_group, -1)
+
+ # Merge the UI
+ self._ui_id = manager.add_ui_from_string(ui_str)
+
+ def forward_search_cb(self, action):
+ self._window.get_active_view().get_data(VIEW_DATA_KEY).sync_view()
+
+class SynctexPlugin(gedit.Plugin):
+
+ def __init__(self):
+ gedit.Plugin.__init__(self)
+
+ def activate(self, window):
+ helper = SynctexWindowHelper(self, window)
+ window.set_data(WINDOW_DATA_KEY, helper)
+
+ def deactivate(self, window):
+ window.get_data(WINDOW_DATA_KEY).deactivate()
+ window.set_data(WINDOW_DATA_KEY, None)
+
+ def update_ui(self, window):
+ pass
+# ex:ts=4:et:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]