summaryrefslogtreecommitdiff
path: root/support/autolatex/plugins/gedit3/autolatexeditor/widgets/latex_console.py
diff options
context:
space:
mode:
Diffstat (limited to 'support/autolatex/plugins/gedit3/autolatexeditor/widgets/latex_console.py')
-rw-r--r--support/autolatex/plugins/gedit3/autolatexeditor/widgets/latex_console.py354
1 files changed, 354 insertions, 0 deletions
diff --git a/support/autolatex/plugins/gedit3/autolatexeditor/widgets/latex_console.py b/support/autolatex/plugins/gedit3/autolatexeditor/widgets/latex_console.py
new file mode 100644
index 0000000000..6b65f9800d
--- /dev/null
+++ b/support/autolatex/plugins/gedit3/autolatexeditor/widgets/latex_console.py
@@ -0,0 +1,354 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2013-14 Stephane Galland <galland@arakhne.org>
+#
+# 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
+#---------------------------------
+
+# Standard libraries
+import re
+# Include the Gtk libraries
+from gi.repository import GObject, GdkPixbuf, Gdk, Gio, Gtk
+# AutoLaTeX includes
+from autolatex.utils import latex_log_parser as log_parser
+
+#---------------------------------
+# INTERNATIONALIZATION
+#---------------------------------
+
+import gettext
+_T = gettext.gettext
+
+#---------------------------------
+# CLASS ConsoleMode
+#---------------------------------
+class ConsoleMode:
+ HIDE = 0
+ OPTIONAL = 1
+ SHOW = 2
+
+#---------------------------------
+# CLASS Panel
+#---------------------------------
+
+#
+# Gtk panel that is managing the configuration of the plugin.
+#
+class Console(Gtk.ScrolledWindow):
+ __gtype_name__ = "AutoLaTeXLaTeXConsole"
+
+ # Constructor.
+ # @param plugin - reference to the plugin. The plugin must provide
+ # the attribute "window" that contains the
+ # reference to the Gtk window that launched this,
+ # and the function "do_update_state" that will be
+ # invoked for notifying a state change.
+ def __init__(self, plugin):
+ Gtk.ScrolledWindow.__init__(self)
+ self.window = plugin.window
+ self.plugin = plugin
+ self._re_file_match = re.compile("^(.+):([0-9]+):\\s*(.*?)\\s*$", re.DOTALL)
+ self._document_directory = None
+ self._latex_parser = None
+ self._current_error = -1
+ # Create the store
+ self._messages = Gtk.ListStore(str, str, str, int, str)
+ # Create the list
+ self._message_widget = Gtk.TreeView()
+ self._message_widget.set_size_request(200, 150)
+ self._message_widget.set_model(self._messages)
+ column = Gtk.TreeViewColumn("Level", Gtk.CellRendererPixbuf(), stock_id=0)
+ self._message_widget.append_column(column)
+ column = Gtk.TreeViewColumn("Text", Gtk.CellRendererText(), text=1)
+ self._message_widget.append_column(column)
+ self._message_widget.set_headers_clickable(False)
+ self._message_widget.set_headers_visible(False)
+ # Init the scroll
+ self.add(self._message_widget)
+ self.set_size_request(200, 150)
+ self.set_policy(
+ Gtk.PolicyType.AUTOMATIC,
+ Gtk.PolicyType.AUTOMATIC)
+ self.set_shadow_type(Gtk.ShadowType.IN)
+ self.set_property('hexpand', True)
+ self.set_property('vexpand', True)
+ #
+ self.show_all()
+ #
+ self._message_widget.connect(
+ 'button-press-event', self.on_list_click_action);
+
+ # Set the text of the log.
+ # @param log - message to output, if None the second parameter will be used.
+ # @param latex_warnings - list of LaTeX warnings, if None the first parameter
+ # will be used.
+ # @param document_directory - name of the document's directory.
+ def set_log(self, log, latex_warnings, document_directory):
+ # Reset attributes
+ self._latex_parser = None
+ self._current_error = -1
+ if (document_directory is None):
+ self._document_directory = None;
+ else:
+ self._document_directory = Gio.File.new_for_path(document_directory)
+ # Clear the list
+ self._messages.clear()
+ # Add the errors in the log
+ if log:
+ mo = re.match(self._re_file_match, log)
+ if mo:
+ filename = mo.group(1)
+ linenumber = int(mo.group(2))
+ message = mo.group(3)
+ else:
+ filename = ''
+ linenumber = int(0)
+ message = log
+ ui_icon = Gtk.STOCK_DIALOG_ERROR
+ if filename:
+ m = filename
+ if linenumber>0:
+ m = m + ":" + str(linenumber)
+ self._messages.append(
+ [ ui_icon, m+"\n"+message, filename, linenumber, None ])
+ else:
+ self._messages.append(
+ [ ui_icon, message, filename, linenumber, None ])
+ elif latex_warnings:
+ # Add the warnings
+ for latex_warning in latex_warnings:
+ ui_icon = Gtk.STOCK_JUMP_TO if latex_warning[2] else Gtk.STOCK_DIALOG_WARNING
+ self._messages.append(
+ [ ui_icon, latex_warning[0], latex_warning[1], int(0), latex_warning[2] ])
+ if log:
+ return ConsoleMode.SHOW
+ if latex_warnings:
+ return ConsoleMode.OPTIONAL
+ return ConsoleMode.HIDE
+
+ # Display the next error.
+ def show_next_error(self):
+ length = len(self._messages)
+ if length>0 and self._current_error < (len(self._messages)-1):
+ self._current_error = self._current_error + 1
+ path = Gtk.TreePath(self._current_error)
+ is_expanded = self._do_click_on_list(path)
+ while is_expanded:
+ is_expanded = self._do_click_on_list(path)
+ self._message_widget.get_selection().select_path(path)
+ self._message_widget.scroll_to_cell(
+ path,
+ self._message_widget.get_column(0),
+ True, 0, 0)
+ return ConsoleMode.SHOW
+ return ConsoleMode.HIDE
+
+ # Indicates if there is a "next" error.
+ def has_next_error(self):
+ length = len(self._messages)
+ return length>0 and self._current_error < (length-1)
+
+ # Display the previous error.
+ def show_previous_error(self):
+ if self._current_error > 0:
+ self._current_error = self._current_error - 1
+ path = Gtk.TreePath(self._current_error)
+ is_expanded = self._do_click_on_list(path)
+ while is_expanded:
+ is_expanded = self._do_click_on_list(path)
+ self._message_widget.get_selection().select_path(path)
+ self._message_widget.scroll_to_cell(
+ path,
+ self._message_widget.get_column(0),
+ True, 0, 0)
+ return ConsoleMode.SHOW
+ return ConsoleMode.HIDE
+
+ # Replies if there is a "previous" error.
+ def has_previous_error(self):
+ return self._current_error > 0
+
+ # Replace a "generic" error message for "undefined references" by
+ # the detailled list of the undefined references.
+ # @param list_iter - list of the undefined references.
+ # @param log_file - source of the error messages.
+ def _replace_by_undefined_reference_warnings(self, list_iter, log_file):
+ if not self._latex_parser:
+ self._latex_parser = log_parser.Parser(log_file)
+ warnings = self._latex_parser.get_undefined_reference_warnings()
+ if warnings:
+ ui_icon = Gtk.STOCK_DIALOG_WARNING
+ for warning in reversed(warnings):
+ self._messages.insert_before(list_iter,
+ [ ui_icon,
+ warning.get_message(),
+ warning.get_filename(),
+ int(warning.get_line_number()),
+ None ])
+ else:
+ # Issue 53: sometimes the LaTeX tool is saying
+ # "There were undefined references" for a citation.
+ warnings = self._latex_parser.get_undefined_citation_warnings()
+ if warnings:
+ ui_icon = Gtk.STOCK_DIALOG_WARNING
+ for warning in reversed(warnings):
+ self._messages.insert_before(list_iter,
+ [ ui_icon,
+ warning.get_message(),
+ warning.get_filename(),
+ int(warning.get_line_number()),
+ None ])
+ else:
+ ui_icon = Gtk.STOCK_DIALOG_ERROR
+ self._messages.insert_before(list_iter,
+ [ ui_icon,
+ _T("Internal Error: Unable to retreive the undefined references from the LaTeX log"),
+ None,
+ int(0),
+ None ])
+ self._messages.remove(list_iter)
+
+ # Replace a "generic" error message for "multidefined labels" by
+ # the detailled list of the multidefined labels.
+ # @param list_iter - list of the multidefined labels.
+ # @param log_file - source of the error messages.
+ def _replace_by_multidefined_label_warnings(self, list_iter, log_file):
+ if not self._latex_parser:
+ self._latex_parser = log_parser.Parser(log_file)
+ warnings = self._latex_parser.get_multidefined_label_warnings()
+ if warnings:
+ ui_icon = Gtk.STOCK_DIALOG_WARNING
+ for warning in reversed(warnings):
+ self._messages.insert_before(list_iter,
+ [ ui_icon,
+ warning.get_message(),
+ warning.get_filename(),
+ int(warning.get_line_number()),
+ None ])
+ else:
+ ui_icon = Gtk.STOCK_DIALOG_ERROR
+ self._messages.insert_before(list_iter,
+ [ ui_icon,
+ _T("Internal Error: Unable to retreive the multidefined references from the LaTeX log"),
+ None,
+ int(0),
+ None ])
+ self._messages.remove(list_iter)
+
+ # Replace a "generic" error message for "undefined citations" by
+ # the detailled list of the undefined citations.
+ # @param list_iter - list of the undefined citations.
+ # @param log_file - source of the error messages.
+ def _replace_by_undefined_citation_warnings(self, list_iter, log_file):
+ if not self._latex_parser:
+ self._latex_parser = log_parser.Parser(log_file)
+ warnings = self._latex_parser.get_undefined_citation_warnings()
+ if warnings:
+ ui_icon = Gtk.STOCK_DIALOG_WARNING
+ for warning in reversed(warnings):
+ self._messages.insert_before(list_iter,
+ [ ui_icon,
+ warning.get_message(),
+ warning.get_filename(),
+ int(warning.get_line_number()),
+ None ])
+ else:
+ ui_icon = Gtk.STOCK_DIALOG_ERROR
+ self._messages.insert_before(list_iter,
+ [ ui_icon,
+ _T("Internal Error: Unable to retreive the undefined citations from the LaTeX log"),
+ None,
+ int(0),
+ None ])
+ self._messages.remove(list_iter)
+
+ # Software execution of a "click" on the error message pointed
+ # by the given path.
+ def _do_click_on_list(self, path):
+ if path:
+ list_iter = self._messages.get_iter(path)
+ row = self._messages[list_iter]
+ # Get the filename and the line number
+ filename = row[2]
+ wcode = row[4]
+ if wcode:
+ if wcode == 'W1':
+ self._replace_by_multidefined_label_warnings(list_iter, filename)
+ return True
+ elif wcode == 'W2':
+ self._replace_by_undefined_reference_warnings(list_iter, filename)
+ return True
+ elif wcode == 'W3':
+ self._replace_by_undefined_citation_warnings(list_iter, filename)
+ return True
+ elif filename and self._document_directory:
+ linenumber = row[3]
+ filename = self._document_directory.resolve_relative_path(filename)
+ linenumber = linenumber if linenumber>=1 else 1
+ # Open the file or select the tab
+ tab = self.window.get_tab_from_location(filename)
+ if tab:
+ # Switch to the tab and show the line
+ self.window.set_active_tab(tab)
+ document = tab.get_document()
+ view = tab.get_view()
+ line_iter = document.get_iter_at_line(linenumber - 1)
+ view.scroll_to_iter(
+ line_iter,
+ 0,
+ True,
+ 0, 0.5)
+ view.grab_focus()
+ else:
+ # Open a new tab at the line
+ tab = self.window.create_tab_from_location(
+ filename,
+ None, # encoding
+ linenumber, # row
+ 0, # column
+ False, # Do not create an empty file
+ True) # Switch to the tab
+ view = tab.get_view()
+ view.grab_focus()
+ GObject.idle_add(self._on_differed_selection, tab, linenumber)
+ return False
+
+ # Private handler for the asynchronous highlight of the error's line
+ # in the text editor.
+ def _on_differed_selection(self, tab, linenumber):
+ if tab:
+ document = tab.get_document()
+ line_iter = document.get_iter_at_line(linenumber - 1)
+ end_line_iter = line_iter.copy()
+ end_line_iter.forward_to_line_end()
+ document.select_range(line_iter, end_line_iter)
+
+ # Handle the click on the Gtk list
+ def on_list_click_action(self, widget, event, data=None):
+ if (event.button==1 and (event.type==Gdk.EventType._2BUTTON_PRESS or event.type==Gdk.EventType._3BUTTON_PRESS)) or event.button==2:
+ x, y = event.get_coords()
+ path, column, cell_x, cell_y = widget.get_path_at_pos(x,y)
+ if path:
+ self._current_error = (int)(path.get_indices()[0])
+ else:
+ self._current_error = -1
+ self._do_click_on_list(path)
+ self.plugin.do_update_state()
+