#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013-14 Stephane Galland <
[email protected]>
#
# 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; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#---------------------------------
# IMPORTS
#---------------------------------
# Import standard python libs
import os
import tempfile
import re
import gettext
# Include the Glib, Gtk and Gedit libraries
from gi.repository import GObject, Gtk, Gio, GdkPixbuf, Gedit, PeasGtk
# AutoLaTeX shared libs
from autolatex.utils import utils as autolatex_utils
from autolatex.utils import gsettings as autolatex_gsettings
from autolatex.config import window as cli_config
# AutoLaTeX-Gedit internal libs
from .utils import gedit_runner
from .config import main_panel as plugin_config
from .widgets import latex_console
#---------------------------------
# PLUGIN CONFIGURATION
#---------------------------------
autolatex_utils.init_plugin_configuration(__file__, 'autolatex-gedit3')
#---------------------------------
# INTERNATIONALIZATION
#---------------------------------
_T = gettext.gettext
#---------------------------------
# CLASS AutoLaTeXPlugin
#---------------------------------
# Plugin for Gedit
class AutoLaTeXPlugin(GObject.Object, Gedit.WindowActivatable, PeasGtk.Configurable):
__gtype_name__ = "AutoLaTeXPlugin"
window = GObject.property(type=Gedit.Window)
#Constructor
def __init__(self):
GObject.Object.__init__(self)
self._status_bar_context_id = None
self._compilation_under_progress = False # Indicate if the compilation is under progress
self._console_icon = None # Icon of the error console
self._gsettings = autolatex_gsettings.Manager()
self._syntex_regex = re.compile('\%.*mainfile:\s*(.*)$')
# Invoked when the configuration window is open
def do_create_configure_widget(self):
return plugin_config.Panel(self._gsettings, self.window)
# Invoked when the plugin is activated
def do_activate(self):
self._console_icon = self._get_icon('console')
self._latex_console = latex_console.Console(self) # Current instance of the error console
if not self._gsettings:
self._gsettings = autolatex_gsettings.Manager()
self._add_ui()
self._check_autolatex_binaries()
# Invoke when the plugin is desactivated
def do_deactivate(self):
gedit_runner.kill_all_runners()
self._remove_ui()
self._gsettings.unbind()
self._gsettings = None
# Check if the AutoLaTeX binaries were found
def _check_autolatex_binaries(self):
if not autolatex_utils.AUTOLATEX_BINARY and not autolatex_utils.AUTOLATEX_BACKEND_BINARY:
dialog = Gtk.MessageDialog(self.window, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, _T("The programs 'autolatex' and 'autolatex-backend'\nwere not found.\nPlease fix the configuration of the AutoLaTeX plugin."))
answer = dialog.run()
dialog.destroy()
elif not autolatex_utils.AUTOLATEX_BINARY:
dialog = Gtk.MessageDialog(self.window, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, _T("The program 'autolatex' was not found.\nPlease fix the configuration of the AutoLaTeX plugin."))
answer = dialog.run()
dialog.destroy()
elif not autolatex_utils.AUTOLATEX_BACKEND_BINARY:
dialog = Gtk.MessageDialog(self.window, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, _T("The program 'autolatex-backend' was not found.\nPlease fix the configuration of the AutoLaTeX plugin."))
answer = dialog.run()
dialog.destroy()
# Invoke when the UI is updated
def do_update_state(self):
directory = self._find_AutoLaTeX_dir()
hasTeXDocument = self._is_TeX_document()
hasAutoLaTeXDocument = (directory is not None)
isInTeXContext = (hasTeXDocument or hasAutoLaTeXDocument)
# Display or hide the menus
self._menu.set_visible(isInTeXContext)
self._document_actions.set_visible(isInTeXContext)
self._texsensitive_actions.set_visible(isInTeXContext)
self._general_actions.set_visible(isInTeXContext)
if directory:
cfgFile = autolatex_utils.get_autolatex_document_config_file(directory)
hasDocConfFile = os.path.exists(cfgFile)
else:
hasDocConfFile = False
hasUserConfFile = os.path.exists(autolatex_utils.get_autolatex_user_config_file())
# Change the sensitivity
if self._document_actions:
self._document_actions.set_sensitive(hasAutoLaTeXDocument and not self._compilation_under_progress)
if self._texsensitive_actions:
self._texsensitive_actions.set_sensitive(hasTeXDocument and not self._compilation_under_progress)
if self._docconfsensitive_actions:
self._docconfsensitive_actions.set_sensitive(hasDocConfFile and not self._compilation_under_progress)
if self._userconfsensitive_actions:
self._userconfsensitive_actions.set_sensitive(hasUserConfFile and not self._compilation_under_progress)
action = self._document_actions.get_action('AutoLaTeXNextError')
assert action is not None
action.set_sensitive(self._latex_console.has_next_error())
action = self._document_actions.get_action('AutoLaTeXPreviousError')
assert action is not None
action.set_sensitive(self._latex_console.has_previous_error())
def _open_latex_console(self, set_visible=True):
bottom_panel = self.window.get_bottom_panel()
console_parent = self._latex_console.get_parent()
if (console_parent is None):
bottom_panel.add_item(self._latex_console,
"autolatex-console-panel",
_T("AutoLaTeX Console"),
Gtk.Image.new_from_pixbuf(self._console_icon))
bottom_panel.activate_item(self._latex_console)
if set_visible and bottom_panel.get_property("visible") == False:
bottom_panel.set_property("visible", True)
# Update the UI according to the flag "compilation under progress"
# and to compilation outputs
def _update_action_validity(self, valid, console_content, latex_warnings):
bottom_panel = self.window.get_bottom_panel()
statusbar = self.window.get_statusbar()
statusbar.remove_all(self._statusbar_id)
# Display or hide the error console if an error message is given or not
show_console = self._latex_console.set_log(
console_content,
latex_warnings,
self._find_AutoLaTeX_dir())
if show_console != latex_console.ConsoleMode.HIDE:
self._open_latex_console(show_console == latex_console.ConsoleMode.SHOW)
# Update the status bar
if show_console == latex_console.ConsoleMode.OPTIONAL and bottom_panel.get_property("visible") == False:
statusbar.push(self._statusbar_id,
_T("LaTeX warnings were found. Please open the bottom panel to see them."))
# Update the sensitivities of the Widgets
self._compilation_under_progress = not valid
GObject.idle_add(self.do_update_state)
# Load an icon from the AutoLaTeX package
def _get_icon(self, icon):
return GdkPixbuf.Pixbuf.new_from_file(autolatex_utils.make_toolbar_icon_path('autolatex-'+icon+'.png'))
# Add any contribution to the Gtk UI
def _add_ui(self):
# Get status bar id
self._statusbar_id = self.window.get_statusbar().get_context_id('gedit-autolatex-plugin')
# Get the UI manager
manager = self.window.get_ui_manager()
# Create the Top menu for AutoLaTeX
self._menu = Gtk.ActionGroup("AutoLaTeXMenu")
self._menu.add_actions([
('AutoLaTeXMenu', None, _T("AutoLaTeX"),
None, _T("AutoLaTeX"),
None),
])
manager.insert_action_group(self._menu)
# Create the menu for SyncTeX
self._synctex_menu = Gtk.ActionGroup("AutoLaTeXSyncTeXMenu")
self._synctex_menu.add_actions([
('AutoLaTeXSyncTeXMenu', Gtk.STOCK_DISCONNECT, _T("SyncTeX"),
None, _T("SyncTeX"),
None),
])
manager.insert_action_group(self._synctex_menu)
# Create the group of actions that are needing an AutoLaTeX document
self._document_actions = Gtk.ActionGroup("AutoLaTeXDocumentActions")
self._document_actions.add_actions([
('AutoLaTeXGenerateImageAction', None, _T("Generate images"),
None, _T("Generate the images with AutoLaTeX"),
self.on_generateimage_action_activate),
('AutoLaTeXCompileAction', None, _T("Compile"),
'<ctrl>B', _T("Compile with AutoLaTeX"),
self.on_compile_action_activate),
('AutoLaTeXCleanAction', None, _T("Remove temporary files"),
None, _T("Clean with AutoLaTeX"),
self.on_clean_action_activate),
('AutoLaTeXCleanallAction', None, _T("Clean all"),
None, _T("Clean all with AutoLaTeX"),
self.on_cleanall_action_activate),
('AutoLaTeXViewAction', None, _T("View the PDF"),
None, _T("Open the PDF viewer"),
self.on_view_action_activate),
('AutoLaTeXMakeFlatAction', None, _T("Create flat version of the TeX document"),
None, _T("Create a flat version of the document, to be submitted to on-line publication systems (Elsevier...)"),
self.on_makeflat_action_activate),
('AutoLaTeXNextError', None, _T("Show next error/warning"),
'F4', _T("Show the next error or warning from the LaTeX console"),
self.on_autolatex_next_error_action),
('AutoLaTeXPreviousError', None, _T("Show previous error/warning"),
'<shift>F4', _T("Show the previous error or warning from the LaTeX console"),
self.on_autolatex_previous_error_action),
])
manager.insert_action_group(self._document_actions)
# Create the group of actions that are needing an TeX document
self._texsensitive_actions = Gtk.ActionGroup("AutoLaTeXTeXSensitiveActions")
self._texsensitive_actions.add_actions([
('AutoLaTeXDocumentConfAction', None, _T("Document configuration"),
None, _T("Change the configuration for the document"),
self.on_document_configuration_action_activate),
])
manager.insert_action_group(self._texsensitive_actions)
# Create the group of actions that are needing the configuration file of a document
self._docconfsensitive_actions = Gtk.ActionGroup("AutoLaTeXDocConfSensitiveActions")
self._docconfsensitive_actions.add_actions([
('AutoLaTeXRemoveDocumentConfAction', None, _T("Delete document configuration"),
None, _T("Delete the configuration for the document"),
self.on_delete_document_configuration_action_activate),
])
manager.insert_action_group(self._docconfsensitive_actions)
# Create the group of actions that are needing the configuration file of the user
self._userconfsensitive_actions = Gtk.ActionGroup("AutoLaTeXUserConfSensitiveActions")
self._userconfsensitive_actions.add_actions([
('AutoLaTeXRemoveUserConfAction', None, _T("Delete user configuration"),
None, _T("Delete the configuration for the user"),
self.on_delete_user_configuration_action_activate),
])
manager.insert_action_group(self._userconfsensitive_actions)
# Create the group of actions that are not needing any special document
self._general_actions = Gtk.ActionGroup("AutoLaTeXGeneralActions")
self._general_actions.add_toggle_actions([
('AutoLaTeXEnableSyncTeXAction', None, _T("Force the use of SyncTeX"),
None, _T("Use SyncTeX even if the document and user configurations say 'no'"),
self.on_enable_synctex_action_activate),
])
self._general_actions.add_actions([
('AutoLaTeXUpdateForSyncTeXAction', None, _T("Update TeX file with SyncTeX reference"),
None, _T("Update the text of the TeX file to add the reference to the main document"),
self.on_update_for_synctex_action_activate),
('AutoLaTeXUserConfAction', None, _T("User configuration"),
None, _T("Change the configuration for the user"),
self.on_user_configuration_action_activate),
])
manager.insert_action_group(self._general_actions)
# Put the icons into the actions
for definition in [ (self._document_actions, 'AutoLaTeXGenerateImageAction', 'images'),
(self._document_actions, 'AutoLaTeXCompileAction', 'compile'),
(self._document_actions, 'AutoLaTeXCleanAction', 'clean'),
(self._document_actions, 'AutoLaTeXCleanallAction', 'cleanall'),
(self._document_actions, 'AutoLaTeXViewAction', 'view'),
(self._texsensitive_actions, 'AutoLaTeXDocumentConfAction', 'preferences'),
(self._general_actions, 'AutoLaTeXUserConfAction', 'preferences')
]:
action = definition[0].get_action(definition[1])
action.set_gicon(self._get_icon(definition[2]))
# Add the Gtk contributions
ui_path = os.path.join(autolatex_utils.AUTOLATEX_APP_PATH, 'ui')
self._ui_merge_ids = []
for ui_file in [ 'menu.ui', 'toolbar.ui' ]:
self._ui_merge_ids.append(manager.add_ui_from_file(os.path.join(ui_path, ui_file)))
manager.ensure_update()
# Change the state of the check-boxes
checkbox = self._general_actions.get_action('AutoLaTeXEnableSyncTeXAction')
checkbox.set_active(self._gsettings.get_force_synctex())
# Connect from gsettings
self._gsettings.connect('force-synctex', self.on_gsettings_changed)
self._gsettings.connect('save-before-run-autolatex', self.on_gsettings_changed)
# Remove all contributions to the Gtk UI
def _remove_ui(self):
# Disconnect from gsettings
self._gsettings.disconnect('force-synctex')
self._gsettings.disconnect('save-before-run-autolatex')
# Remove the error console
if self._latex_console:
if self._latex_console.get_parent() is not None:
panel = self.window.get_bottom_panel()
panel.remove_item(self._latex_console)
self._latex_console = None
# Remove the Gtk Widgets
manager = self.window.get_ui_manager()
manager.remove_action_group(self._document_actions)
manager.remove_action_group(self._texsensitive_actions)
manager.remove_action_group(self._docconfsensitive_actions)
manager.remove_action_group(self._userconfsensitive_actions)
manager.remove_action_group(self._general_actions)
manager.remove_action_group(self._synctex_menu)
manager.remove_action_group(self._menu)
for merge_id in self._ui_merge_ids:
manager.remove_ui(merge_id)
manager.ensure_update()
# Replies if the active document is a TeX document
def _is_TeX_document(self):
doc = self.window.get_active_document()
if doc:
doc = Gedit.Document.get_location(doc)
if doc:
return autolatex_utils.is_TeX_document(doc.get_path())
return False
# Try to find the directory where an AutoLaTeX configuration file is
# located. The search is traversing the parent directory from the current
# document.
def _find_AutoLaTeX_dir(self):
adir = None
doc = self.window.get_active_document()
if doc:
doc = Gedit.Document.get_location(doc)
if doc:
return autolatex_utils.find_AutoLaTeX_directory(doc.get_path())
return adir
def _save_documents(self):
for document in self.window.get_unsaved_documents():
is_untitled = document.is_untitled()
is_deleted = document.get_deleted()
is_readonly = document.get_readonly()
if not is_untitled and not is_deleted and not is_readonly :
document.save(Gedit.DocumentSaveFlags.IGNORE_MTIME)
def _apply_general_autolatex_cli_options(self, params):
if self._gsettings.get_force_synctex():
params = [ '--synctex' ] + params
params = [ autolatex_utils.DEFAULT_LOG_LEVEL ] + params
return params
def on_clean_action_activate(self, action, data=None):
self._launch_AutoLaTeX(
_T("Removing the generated files (except the figures)"),
'clean', self._apply_general_autolatex_cli_options(
[ '--noview' ]),
False)
def on_cleanall_action_activate(self, action, data=None):
self._launch_AutoLaTeX(
_T("Removing the generated files and figures"),
'cleanall', self._apply_general_autolatex_cli_options(
[ '--noview' ]),
False)
def on_compile_action_activate(self, action, data=None):
self._launch_AutoLaTeX(
_T("Generating the document"),
'all', self._apply_general_autolatex_cli_options(
[ '--noview' ]),
True)
def on_generateimage_action_activate(self, action, data=None):
self._launch_AutoLaTeX(
_T("Generating the figures with the translators"),
'images', self._apply_general_autolatex_cli_options(
[ '--noview' ]),
False)
def on_view_action_activate(self, action, data=None):
self._launch_AutoLaTeX(
_T("Launching the viewer"),
'view', self._apply_general_autolatex_cli_options(
[ '--asyncview' ]),
True)
def on_document_configuration_action_activate(self, action, data=None):
directory = self._find_AutoLaTeX_dir()
if directory:
cli_config.open_configuration_dialog(self.window, True, directory)
def on_delete_document_configuration_action_activate(self, action, data=None):
directory = self._find_AutoLaTeX_dir()
if directory:
cfgFile = autolatex_utils.get_autolatex_document_config_file(directory)
if os.path.exists(cfgFile):
dialog = Gtk.MessageDialog(self.window, Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, _T("Do you want to delete the document configuration?"))
answer = dialog.run()
dialog.destroy()
if answer == Gtk.ResponseType.YES:
os.unlink(cfgFile)
self.do_update_state()
def on_user_configuration_action_activate(self, action, data=None):
directory = self._find_AutoLaTeX_dir()
if directory:
cli_config.open_configuration_dialog(self.window, False, directory)
def on_delete_user_configuration_action_activate(self, action, data=None):
cfgFile = autolatex_utils.get_autolatex_user_config_file()
if os.path.exists(cfgFile):
dialog = Gtk.MessageDialog(self.window, Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, _T("Do you want to delete the user configuration?"))
answer = dialog.run()
dialog.destroy()
if answer == Gtk.ResponseType.YES:
os.unlink(cfgFile)
self.do_update_state()
def on_enable_synctex_action_activate(self, action, data=None):
checkbox = self._general_actions.get_action('AutoLaTeXEnableSyncTeXAction')
self._gsettings.set_force_synctex(checkbox.get_active())
def on_update_for_synctex_action_activate(self, action, data=None):
if self._is_TeX_document():
directory = self._find_AutoLaTeX_dir()
view = self.window.get_active_view()
text_buffer = view.get_buffer()
found = None
# Search in the first tree lines
if text_buffer.get_line_count() > 0:
found = self.__search_for_synctex_flag(text_buffer, 0)
# Search in the last tree lines
if not found and text_buffer.get_line_count() > 3:
found = self.__search_for_synctex_flag(text_buffer,
max(3,text_buffer.get_line_count()-3))
# Add the SyncTeX flag
if not found:
private_config = autolatex_utils.backend_get_configuration(
directory,
'all', '__private__');
main_file = private_config.get('input', 'latex file', '');
current_dir = Gio.File.new_for_path(os.getcwd())
main_file = current_dir.resolve_relative_path(main_file).get_path()
current_document = self.window.get_active_document()
document_file = Gedit.Document.get_location(current_document)
if document_file:
document_filename = current_dir.resolve_relative_path(document_file.get_path())
document_filename = document_filename.get_path()
if main_file != document_filename:
document_dir = document_file.get_parent()
rel_path = os.path.relpath(main_file, document_dir.get_path())
text_buffer.insert_interactive(
text_buffer.get_iter_at_line(0),
unicode("% mainfile: "+rel_path+"\n"),
-1,
view.get_editable())
def __search_for_synctex_flag(self, text_buffer, line_number):
found = None
i = 0
text_iter1 = text_buffer.get_iter_at_line(line_number)
while text_iter1 and i<3 and not found:
text_iter2 = text_iter1.copy();
text_iter2.forward_to_line_end()
line = text_iter1.get_visible_text(text_iter2)
mo = re.match(self._syntex_regex, line)
if mo:
found = mo.group(1)
text_iter1 = text_iter2
if not text_iter1.forward_line():
i = 4
else:
i = i + 1
return found
def on_gsettings_changed(self, settings, key, data=None):
if key == 'force-synctex':
checkbox = self._general_actions.get_action('AutoLaTeXEnableSyncTeXAction')
checkbox.set_active(self._gsettings.get_force_synctex())
elif key == 'save-before-run-autolatex':
pass
def on_enable_synctex_action_activate(self, action, data=None):
checkbox = self._general_actions.get_action('AutoLaTeXEnableSyncTeXAction')
self._gsettings.set_force_synctex(checkbox.get_active())
def on_makeflat_action_activate(self, action, data=None):
self._launch_AutoLaTeX(
_T("Making the \"flat\" version of the document"),
'makeflat', self._apply_general_autolatex_cli_options(
[ '--noview' ],
True))
def on_autolatex_next_error_action(self, action, data=None):
show_console = self._latex_console.show_next_error()
if show_console != latex_console.ConsoleMode.HIDE:
self._open_latex_console(show_console == latex_console.ConsoleMode.SHOW)
self.do_update_state()
def on_autolatex_previous_error_action(self, action, data=None):
show_console = self._latex_console.show_previous_error()
if show_console != latex_console.ConsoleMode.HIDE:
self._open_latex_console(show_console == latex_console.ConsoleMode.SHOW)
self.do_update_state()
def _launch_AutoLaTeX(self, label, directive, params, enable_saving):
directory = self._find_AutoLaTeX_dir()
if directory:
GObject.idle_add(self._update_action_validity, False, None, None)
# Save the documents if necessary
if enable_saving and self._gsettings.get_save_before_run_autolatex():
self._save_documents()
thread = gedit_runner.Runner(
self,
label,
self._gsettings.get_progress_info_visibility(),
directory,
directive,
params)
thread.start()