#!/usr/bin/env python # -*- coding: utf-8 -*- # # autolatex/widgets/latex_console.py # Copyright (C) 2013 Stephane Galland # # 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 ..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" 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, long, 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 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 = long(mo.group(2)) message = mo.group(3) else: filename = '' linenumber = long(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], long(0), latex_warning[2] ]) if log: return ConsoleMode.SHOW if latex_warnings: return ConsoleMode.OPTIONAL return ConsoleMode.HIDE 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 def has_next_error(self): length = len(self._messages) return length>0 and self._current_error < (length-1) 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 def has_previous_error(self): return self._current_error > 0 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(), long(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(), long(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, long(0), None ]) self._messages.remove(list_iter) 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(), long(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, long(0), None ]) self._messages.remove(list_iter) 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(), long(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, long(0), None ]) self._messages.remove(list_iter) 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 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) 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()