Files
gajim-plugins/latex/latex.py
2020-05-25 10:59:39 +02:00

191 lines
6.5 KiB
Python

# Copyright (C) 2010-2011 Yves Fischer <yvesf AT xapek.org>
# Copyright (C) 2011 Yann Leboulanger <asterix AT lagaule.org>
#
# This file is part of Gajim.
#
# Gajim 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; version 3 only.
#
# Gajim 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 Gajim. If not, see <http://www.gnu.org/licenses/>.
import os
import logging
from functools import partial
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Pango
from gajim.plugins import GajimPlugin
from gajim.plugins.plugins_i18n import _
from latex.latex_renderer import LatexRenderer
from latex.config_dialog import LatexPluginConfiguration
from latex.util import try_run
log = logging.getLogger('gajim.p.latex')
class LatexPlugin(GajimPlugin):
def init(self):
self.description = _(
'Render LaTeX markup for $$foobar$$ sourrounded LaTeX equations.')
self.config_dialog = partial(LatexPluginConfiguration, self)
self.config_default_values = {
'png_dpi': ('108', '')
}
self.gui_extension_points = {
'chat_control_base': (
self._connect_chat_control_base,
self._disconnect_chat_control_base)
}
self._test_activatable()
self._timeout_id = None
self._last_eol_offset = -1
def _connect_chat_control_base(self, chat_control):
d = {}
tv = chat_control.conv_textview.tv
tb = tv.get_buffer()
self._latex_tag = Gtk.TextTag.new('latex')
self._latex_tag.set_property('foreground', 'blue')
self._latex_tag.set_property('underline', Pango.Underline.SINGLE)
d['tag_id'] = self._latex_tag.connect('event', self._textview_event_after)
tb.get_tag_table().add(self._latex_tag)
d['h_id'] = tb.connect('changed', self._textbuffer_live_latex_expander)
chat_control.latexs_expander_plugin_data = d
return True
def _disconnect_chat_control_base(self, chat_control):
d = chat_control.latexs_expander_plugin_data
tv = chat_control.conv_textview.tv
tv.get_buffer().disconnect(d['h_id'])
self._latex_tag.disconnect(d['tag_id'])
def _test_activatable(self):
"""
performs very simple checks (check if executable is in PATH)
"""
self.available_text = ''
exitcode = try_run(['latex', '-version'], None)
if exitcode != 0:
latex_available = False
else:
latex_available = True
exitcode = try_run(['dvipng', '--version'], None)
if exitcode != 0:
dvipng_available = False
else:
dvipng_available = True
exitcode = try_run(['convert', '-version'], None)
if exitcode != 0:
imagemagick_available = False
else:
imagemagick_available = True
pkgs = ''
if not latex_available:
if os.name == 'nt':
pkgs = 'MikTex'
else:
pkgs = 'texlive-latex-base'
self.available_text = _('LaTeX is not available')
self.activatable = False
if not dvipng_available and not imagemagick_available:
if os.name == 'nt':
if not pkgs:
pkgs = 'MikTex'
else:
if pkgs:
pkgs += _(' and ')
pkgs += '%s or %s' % ('dvipng', 'ImageMagick')
if self.available_text:
self.available_text += ' and '
self.available_text += _('dvipng and Imagemagick are not available')
if self.available_text:
self.activatable = False
self.available_text += _('. Install %s') % pkgs
def _textview_event_after(self, tag, widget, event, iter_):
"""
start rendering if clicked on a link
"""
if tag.get_property('name') != 'latex' or \
event.type != Gdk.EventType.BUTTON_PRESS:
return
dollar_start, _iter_start = iter_.backward_search(
'$$',
Gtk.TextSearchFlags.TEXT_ONLY, None)
_iter_end, dollar_end = iter_.forward_search(
'$$',
Gtk.TextSearchFlags.TEXT_ONLY, None)
LatexRenderer(dollar_start, dollar_end, widget, self.config['png_dpi'])
def _textbuffer_live_latex_expander(self, tb):
"""
called when conversation text widget changes
"""
def _split_list(list_):
newlist = []
for i in range(0, len(list_)-1, 2):
newlist.append([ list_[i], list_[i+1], ])
return newlist
def _detect_tags(tb, start_it=None, end_it=None):
self._timeout_id = None
if not end_it:
end_it = tb.get_end_iter()
if not start_it:
eol_tag = tb.get_tag_table().lookup('eol')
start_it = end_it.copy()
start_it.backward_to_tag_toggle(eol_tag)
points = []
tuple_found = start_it.forward_search(
'$$',
Gtk.TextSearchFlags.TEXT_ONLY, None)
while tuple_found is not None:
points.append(tuple_found)
tuple_found = tuple_found[1].forward_search(
'$$',
Gtk.TextSearchFlags.TEXT_ONLY, None)
for pair in _split_list(points):
tb.apply_tag_by_name('latex', pair[0][1], pair[1][0])
end_iter = tb.get_end_iter()
eol_tag = tb.get_tag_table().lookup('eol')
it = end_iter.copy()
it.backward_to_tag_toggle(eol_tag)
if it.get_offset() == self._last_eol_offset:
if self._timeout_id:
GLib.source_remove(self._timeout_id)
self._timeout_id = GLib.timeout_add(100, _detect_tags, tb, it, end_iter)
else:
if self._timeout_id:
GLib.source_remove(self._timeout_id)
it1 = it.copy()
it1.backward_char()
it1.backward_to_tag_toggle(eol_tag)
_detect_tags(tb, it1, it)
self._last_eol_offset = it.get_offset()