[gedit-latex] Begin Fixing style of py files.



commit a8a29c3d4269c10deb3cc46ff66c15f63696ec39
Author: Josà Aliste <jaliste src gnome org>
Date:   Tue Jun 28 18:10:51 2011 -0400

    Begin Fixing style of py files.

 latex/__init__.py               |    4 +-
 latex/base/__init__.py          | 2565 +++++++++++++++++++--------------------
 latex/base/completion.py        | 1084 +++++++++---------
 latex/base/config.py            |  220 ++--
 latex/base/decorators.py        |  346 +++---
 latex/base/job.py               |  314 +++---
 latex/base/resources.py         |  106 +-
 latex/base/templates.py         | 1130 +++++++++---------
 latex/base/windowactivatable.py | 1196 +++++++++---------
 latex/bibtex/__init__.py        |    2 +-
 latex/bibtex/actions.py         |   48 +-
 latex/bibtex/cache.py           |  161 ++--
 latex/bibtex/completion.py      |  176 ++--
 latex/bibtex/dialogs.py         |  320 +++---
 latex/bibtex/editor.py          |  328 +++---
 latex/bibtex/model.py           |  141 ++--
 latex/bibtex/parser.py          | 1042 ++++++++--------
 latex/bibtex/validator.py       |  117 +-
 latex/bibtex/views.py           |  502 ++++----
 latex/issues.py                 |  233 ++--
 latex/latex/__init__.py         |  148 ++--
 latex/latex/actions.py          |  836 +++++++-------
 latex/latex/archive.py          |  156 ++--
 latex/latex/cache.py            |  243 ++--
 latex/latex/completion.py       |  696 ++++++------
 latex/latex/dialogs.py          | 2214 +++++++++++++++++-----------------
 latex/latex/editor.py           |  744 ++++++------
 latex/latex/environment.py      |  638 +++++-----
 latex/latex/expander.py         |  134 +-
 latex/latex/inversesearch.py    |  105 +-
 latex/latex/lexer.py            |  576 +++++-----
 latex/latex/listing.py          |   85 +-
 latex/latex/model.py            |  488 ++++----
 latex/latex/outline.py          |  439 ++++----
 latex/latex/parser.py           | 1321 ++++++++++----------
 latex/latex/preview.py          |  256 ++--
 latex/latex/validator.py        |  412 ++++----
 latex/latex/views.py            |  664 +++++-----
 latex/outline.py                |  586 +++++-----
 latex/preferences/__init__.py   |   64 +-
 latex/preferences/dialog.py     |  958 ++++++++--------
 latex/preferences/tools.py      |  398 +++---
 latex/relpath.py                |  122 +-
 latex/tools/__init__.py         |  444 ++++----
 latex/tools/postprocess.py      |  381 +++---
 latex/tools/util.py             |  179 ++--
 latex/tools/views.py            |  330 +++---
 latex/util.py                   |  382 +++---
 latex/views.py                  |  348 +++---
 49 files changed, 12184 insertions(+), 12198 deletions(-)
---
diff --git a/latex/__init__.py b/latex/__init__.py
index 8fb0921..dd78ffc 100644
--- a/latex/__init__.py
+++ b/latex/__init__.py
@@ -11,11 +11,11 @@
 #
 # 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 
+# 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
 
-from base.windowactivatable import LaTeXWindowActivatable
+from base.windowactivatable import LaTeXWindowActivatable
\ No newline at end of file
diff --git a/latex/base/__init__.py b/latex/base/__init__.py
index 0c7b603..bc93a85 100644
--- a/latex/base/__init__.py
+++ b/latex/base/__init__.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -29,283 +29,283 @@ from gi.repository import Gtk, Gdk
 
 
 class View(object):
-	"""
-	Base class for a view
-	"""
-	
-	_log = getLogger("View")
-	
-	# TODO: this doesn't belong to the interface of base
-	# TODO: call destroy()
-	
-	SCOPE_WINDOW, SCOPE_EDITOR = 0, 1
-	
-	#
-	# these should be overriden by subclasses
-	#
-	
-	# a label string used for this view
-	label = ""
-	
-	# an icon for this view (Gtk.Image or a stock_id string)
-	icon = None
-	
-	# the scope of this View:
-	# 	SCOPE_WINDOW: the View is created with the window and the same instance is passed to every Editor
-	#	SCOPE_EDITOR: the View is created with the Editor and destroyed with it
-	scope = SCOPE_WINDOW
-	
-	def init(self, context):
-		"""
-		To be overridden
-		"""
-	
-	def destroy(self):
-		"""
-		To be overridden
-		"""
-		
-	def __del__(self):
-		self._log.debug("Properly destroyed %s" % self)
+    """
+    Base class for a view
+    """
+
+    _log = getLogger("View")
+
+    # TODO: this doesn't belong to the interface of base
+    # TODO: call destroy()
+
+    SCOPE_WINDOW, SCOPE_EDITOR = 0, 1
+
+    #
+    # these should be overriden by subclasses
+    #
+
+    # a label string used for this view
+    label = ""
+
+    # an icon for this view (Gtk.Image or a stock_id string)
+    icon = None
+
+    # the scope of this View:
+    #     SCOPE_WINDOW: the View is created with the window and the same instance is passed to every Editor
+    #    SCOPE_EDITOR: the View is created with the Editor and destroyed with it
+    scope = SCOPE_WINDOW
+
+    def init(self, context):
+        """
+        To be overridden
+        """
+
+    def destroy(self):
+        """
+        To be overridden
+        """
+
+    def __del__(self):
+        self._log.debug("Properly destroyed %s" % self)
 
 
 class SideView(View, Gtk.VBox):
-	"""
-	"""
-	def __init__(self, context):
-		GObject.GObject.__init__(self)
-		
-		self._context = context
-		self._initialized = False
-		
-		# connect to expose event and init() on first expose
-		self._expose_handler = self.connect("draw", self._on_expose_event)
-		
-	def _on_expose_event(self, *args):
-		"""
-		The View has been exposed for the first time
-		"""
-		self._do_init()
-	
-	def _do_init(self):
-		self.disconnect(self._expose_handler)
-		self.init(self._context)
-		self.show_all()
-		self._initialized = True
-	
-	def assure_init(self):
-		"""
-		This may be called by the subclassing instance to assure that the View
-		has been initialized. 
-		
-		This is necessary because methods of the instance may be called before 
-		init() as the View is initialized on the first exposure.
-		"""
-		if not self._initialized:
-			self._do_init()
-			
-	def destroy(self):
-		if not self._initialized:
-			self.disconnect(self._expose_handler)
-		Gtk.VBox.destroy(self)
-		self._context = None
+    """
+    """
+    def __init__(self, context):
+        GObject.GObject.__init__(self)
+
+        self._context = context
+        self._initialized = False
+
+        # connect to expose event and init() on first expose
+        self._expose_handler = self.connect("draw", self._on_expose_event)
+
+    def _on_expose_event(self, *args):
+        """
+        The View has been exposed for the first time
+        """
+        self._do_init()
+
+    def _do_init(self):
+        self.disconnect(self._expose_handler)
+        self.init(self._context)
+        self.show_all()
+        self._initialized = True
+
+    def assure_init(self):
+        """
+        This may be called by the subclassing instance to assure that the View
+        has been initialized.
+
+        This is necessary because methods of the instance may be called before
+        init() as the View is initialized on the first exposure.
+        """
+        if not self._initialized:
+            self._do_init()
+
+    def destroy(self):
+        if not self._initialized:
+            self.disconnect(self._expose_handler)
+        Gtk.VBox.destroy(self)
+        self._context = None
 
 
 class BottomView(View, Gtk.HBox):
-	"""
-	"""
-	def __init__(self, context):
-		GObject.GObject.__init__(self)
-		
-		self._context = context
-		self._initialized = False
-		
-		# connect to expose event and init() on first expose
-		self._expose_handler = self.connect("draw", self._on_expose_event)
-		
-	def _on_expose_event(self, *args):
-		"""
-		The View has been exposed for the first time
-		"""
-		self._do_init()
-	
-	def _do_init(self):
-		self.disconnect(self._expose_handler)
-		self.init(self._context)
-		self.show_all()
-		self._initialized = True
-	
-	def assure_init(self):
-		"""
-		This may be called by the subclassing instance to assure that the View
-		has been initialized. 
-		
-		This is necessary because methods of the instance may be called before 
-		init() as the View is initialized on the first exposure.
-		"""
-		if not self._initialized:
-			self._do_init()
-			
-	def destroy(self):
-		if not self._initialized:
-			self.disconnect(self._expose_handler)
-		Gtk.HBox.destroy(self)
-		self._context = None
+    """
+    """
+    def __init__(self, context):
+        GObject.GObject.__init__(self)
+
+        self._context = context
+        self._initialized = False
+
+        # connect to expose event and init() on first expose
+        self._expose_handler = self.connect("draw", self._on_expose_event)
+
+    def _on_expose_event(self, *args):
+        """
+        The View has been exposed for the first time
+        """
+        self._do_init()
+
+    def _do_init(self):
+        self.disconnect(self._expose_handler)
+        self.init(self._context)
+        self.show_all()
+        self._initialized = True
+
+    def assure_init(self):
+        """
+        This may be called by the subclassing instance to assure that the View
+        has been initialized.
+
+        This is necessary because methods of the instance may be called before
+        init() as the View is initialized on the first exposure.
+        """
+        if not self._initialized:
+            self._do_init()
+
+    def destroy(self):
+        if not self._initialized:
+            self.disconnect(self._expose_handler)
+        Gtk.HBox.destroy(self)
+        self._context = None
 
 
 
 class Template(object):
-	"""
-	This one is exposed and should be used by the 'real' plugin code
-	"""
-	def __init__(self, expression):
-		self._expression = expression
-	
-	@property
-	def expression(self):
-		return self._expression
-	
-	def __str__(self):
-		return self._expression
+    """
+    This one is exposed and should be used by the 'real' plugin code
+    """
+    def __init__(self, expression):
+        self._expression = expression
+
+    @property
+    def expression(self):
+        return self._expression
+
+    def __str__(self):
+        return self._expression
 
 
 from gi.repository import GObject
 
 class GeditLaTeXPlugin_MenuToolAction(Gtk.Action):
-	__gtype_name__ = "GeditLaTeXPlugin_MenuToolAction"
+    __gtype_name__ = "GeditLaTeXPlugin_MenuToolAction"
 
-	def do_create_tool_item(self):
-		return Gtk.MenuToolButton() 
+    def do_create_tool_item(self):
+        return Gtk.MenuToolButton()
 
 class Action(object):
-	"""
-	"""
-	
-	menu_tool_action = False	# if True a MenuToolAction is created and hooked for this action
-								# instead of Gtk.Action
-								
-	extensions = [None]			# a list of file extensions for which this action should be enabled
-								# [None] indicates that this action is to be enabled for all extensions
-
-	def __init__(self, *args, **kwargs):
-		pass
-	
-	def hook(self, action_group, window_context):
-		"""
-		Create an internal action object (Gtk.Action or MenuToolAction), listen to it and
-		hook it in an action group
-		
-		@param action_group: a Gtk.ActionGroup object
-		@param window_context: a WindowContext object to pass when this action is activated
-		"""
-		if self.menu_tool_action:
-			action_clazz = GeditLaTeXPlugin_MenuToolAction
-		else:
-			action_clazz = Gtk.Action
-		self._internal_action = action_clazz(self.__class__.__name__, self.label, self.tooltip, self.stock_id)
-		self._handler = self._internal_action.connect("activate", lambda gtk_action, action: action.activate(window_context), self)
-		action_group.add_action_with_accel(self._internal_action, self.accelerator)
-		
-	@property
-	def label(self):
-		raise NotImplementedError
-	
-	@property
-	def stock_id(self):
-		raise NotImplementedError
-	
-	@property
-	def accelerator(self):
-		raise NotImplementedError
-	
-	@property
-	def tooltip(self):
-		raise NotImplementedError
-
-	def activate(self, context):
-		"""
-		@param context: the current WindowContext instance
-		"""
-		raise NotImplementedError
-
-	def unhook(self, action_group):
-		self._internal_action.disconnect(self._handler)
-		action_group.remove_action(self._internal_action)
-		
-	#~ def __del__(self):
-		#~ print "Properly destroyed Action %s" % self
+    """
+    """
+
+    menu_tool_action = False    # if True a MenuToolAction is created and hooked for this action
+                                # instead of Gtk.Action
+
+    extensions = [None]            # a list of file extensions for which this action should be enabled
+                                # [None] indicates that this action is to be enabled for all extensions
+
+    def __init__(self, *args, **kwargs):
+        pass
+
+    def hook(self, action_group, window_context):
+        """
+        Create an internal action object (Gtk.Action or MenuToolAction), listen to it and
+        hook it in an action group
+
+        @param action_group: a Gtk.ActionGroup object
+        @param window_context: a WindowContext object to pass when this action is activated
+        """
+        if self.menu_tool_action:
+            action_clazz = GeditLaTeXPlugin_MenuToolAction
+        else:
+            action_clazz = Gtk.Action
+        self._internal_action = action_clazz(self.__class__.__name__, self.label, self.tooltip, self.stock_id)
+        self._handler = self._internal_action.connect("activate", lambda gtk_action, action: action.activate(window_context), self)
+        action_group.add_action_with_accel(self._internal_action, self.accelerator)
+
+    @property
+    def label(self):
+        raise NotImplementedError
+
+    @property
+    def stock_id(self):
+        raise NotImplementedError
+
+    @property
+    def accelerator(self):
+        raise NotImplementedError
+
+    @property
+    def tooltip(self):
+        raise NotImplementedError
+
+    def activate(self, context):
+        """
+        @param context: the current WindowContext instance
+        """
+        raise NotImplementedError
+
+    def unhook(self, action_group):
+        self._internal_action.disconnect(self._handler)
+        action_group.remove_action(self._internal_action)
+
+    #~ def __del__(self):
+        #~ print "Properly destroyed Action %s" % self
 
 
 class ICompletionHandler(object):
-	"""
-	This should be implemented for each language or 'proposal source'
-	"""
-	@property
-	def trigger_keys(self):
-		"""
-		@return: a list of gdk key codes that trigger completion
-		"""
-		raise NotImplementedError
-	
-	@property
-	def prefix_delimiters(self):
-		"""
-		@return: a list of characters that delimit the prefix on the left
-		"""
-		raise NotImplementedError
-	
-	def complete(self, prefix):
-		"""
-		@return: a list of objects extending Proposal
-		"""
-		raise NotImplementedError
+    """
+    This should be implemented for each language or 'proposal source'
+    """
+    @property
+    def trigger_keys(self):
+        """
+        @return: a list of gdk key codes that trigger completion
+        """
+        raise NotImplementedError
+
+    @property
+    def prefix_delimiters(self):
+        """
+        @return: a list of characters that delimit the prefix on the left
+        """
+        raise NotImplementedError
+
+    def complete(self, prefix):
+        """
+        @return: a list of objects extending Proposal
+        """
+        raise NotImplementedError
 
 
 class Proposal(object):
-	"""
-	A proposal for completion
-	"""
-	@property
-	def source(self):
-		"""
-		@return: a subclass of Source to be inserted on activation
-		"""
-		raise NotImplementedError
-	
-	@property
-	def label(self):
-		"""
-		@return: a string (may be pango markup) to be shown in proposals popup
-		"""
-		raise NotImplementedError
-	
-	@property
-	def details(self):
-		"""
-		@return: a widget to be shown in details popup
-		"""
-		raise NotImplementedError
-	
-	@property
-	def icon(self):
-		"""
-		@return: an instance of GdkPixbuf.Pixbuf
-		"""
-		raise NotImplementedError
-	
-	@property
-	def overlap(self):
-		"""
-		@return: the number of overlapping characters from the beginning of the
-			proposal and the prefix it was generated for
-		"""
-		raise NotImplementedError
-	
-	def __cmp__(self, other):
-		"""
-		Compare this proposal to another one
-		"""
-		return cmp(self.label.lower(), other.label.lower())
+    """
+    A proposal for completion
+    """
+    @property
+    def source(self):
+        """
+        @return: a subclass of Source to be inserted on activation
+        """
+        raise NotImplementedError
+
+    @property
+    def label(self):
+        """
+        @return: a string (may be pango markup) to be shown in proposals popup
+        """
+        raise NotImplementedError
+
+    @property
+    def details(self):
+        """
+        @return: a widget to be shown in details popup
+        """
+        raise NotImplementedError
+
+    @property
+    def icon(self):
+        """
+        @return: an instance of GdkPixbuf.Pixbuf
+        """
+        raise NotImplementedError
+
+    @property
+    def overlap(self):
+        """
+        @return: the number of overlapping characters from the beginning of the
+            proposal and the prefix it was generated for
+        """
+        raise NotImplementedError
+
+    def __cmp__(self, other):
+        """
+        Compare this proposal to another one
+        """
+        return cmp(self.label.lower(), other.label.lower())
 
 
 import re
@@ -317,681 +317,681 @@ from .templates import TemplateDelegate
 
 
 class Editor(object):
-	"""
-	The base class for editors. This manages
-	 - the subclass life-cycle
-	 - the marker framework
-	 - change monitoring
-	 - drag'n'drop support
-	"""
-	
-	__log = getLogger("Editor")
-	
-	
-	class Marker(object):
-		"""
-		Markers refer to and highlight a range of text in the TextBuffer decorated by 
-		an Editor. They are used for highlighting issues.
-		
-		Each Marker instance stores two Gtk.TextMark objects refering to the start and
-		end of the text range.
-		"""
-		def __init__(self, left_mark, right_mark, id, type):
-			"""
-			@param left_mark: a Gtk.TextMark
-			@param right_mark: a Gtk.TextMark
-			@param id: a unique string
-			@param type: a marker type string
-			"""
-			self.left_mark = left_mark
-			self.right_mark = right_mark
-			self.type = type
-			self.id = id
-	
-	
-	class MarkerTypeRecord(object):
-		"""
-		This used for managing Marker types
-		"""
-		def __init__(self, tag, anonymous):
-			"""
-			@param tag: a Gtk.TextTag
-			"""
-			self.tag = tag
-			self.anonymous = anonymous
-			self.markers = []
-	
-	
-	__PATTERN_INDENT = re.compile("[ \t]+")
-	
-	
-	# A list of file extensions
-	#
-	# If one or more files with one of these extensions is dragged and dropped on the editor,
-	# the Editor.drag_drop_received method is called. An empty list disables the dnd support.
-	dnd_extensions = []
-	
-	
-	def __init__(self, tab_decorator, file):
-		self._tab_decorator = tab_decorator
-		self._file = file
-		self._text_buffer = tab_decorator.tab.get_document()
-		self._text_view = tab_decorator.tab.get_view()
-		
-		# create template delegate
-		self._template_delegate = TemplateDelegate(self)
-		
-		# hook completion handlers of the subclassing Editor
-		completion_handlers = self.completion_handlers
-		if len(completion_handlers):
-			self._completion_distributor = CompletionDistributor(self, completion_handlers)
-		else:
-			self._completion_distributor = None
-		
-		#
-		# init marker framework
-		#
-		
-		# needed for cleanup
-		self._tags = []
-		self._marker_types = {}    # {marker type -> MarkerTypeRecord object}
-		self._markers = {}		# { marker id -> marker object }
-		
-		
-		self._window_context = self._tab_decorator._window_decorator._window_context
-		self._window_context.create_editor_views(self, file)
-		
-		
-		self._offset = None		# used by move_cursor
-
-		self.__view_signal_handlers = [
-				self._text_view.connect("button-press-event", self.__on_button_pressed),
-				self._text_view.connect("key-release-event", self.__on_key_released),
-				self._text_view.connect("button-release-event", self.__on_button_released) ]
-		
-		self.__buffer_change_timestamp = time.time()
-		self.__buffer_signal_handlers = [
-				self._text_buffer.connect("changed", self.__on_buffer_changed) ]
-		
-		# dnd support
-		if len(self.dnd_extensions) > 0:
-			self.__view_signal_handlers.append(
-					self._text_view.connect("drag-data-received", self.__on_drag_data_received))
-		
-		# start life-cycle for subclass
-		self.init(file, self._window_context)
-		
-	def __on_buffer_changed(self, text_buffer):
-		"""
-		Store the timestamp of the last buffer change
-		"""
-		self.__buffer_change_timestamp = time.time()
-	
-	def __on_drag_data_received(self, widget, context, x, y, data, info, timestamp):
-		"""
-		The drag destination received the data from the drag operation
-		
-		@param widget: the widget that received the signal
-		@param context: the Gdk.DragContext
-		@param x: the X position of the drop
-		@param y: the Y position of the drop
-		@param data: a Gtk.SelectionData object
-		@param info: an integer ID for the drag
-		@param timestamp: the time of the drag event
-		"""
-		self.__log.debug("drag-data-received")
-		
-		files = []
-		match = False
-		
-		for uri in data.get_uris():
-			file = File(uri)
-			files.append(file)
-			if file.extension.lower() in self.dnd_extensions:
-				match = True
-		
-		if match:
-			self.drag_drop_received(files)
-
-	def __on_key_released(self, *args):
-		"""
-		This helps to call 'move_cursor'
-		"""
-		offset = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert()).get_offset()
-		if offset != self._offset:
-			self._offset = offset
-			self.on_cursor_moved(offset)
-	
-	def __on_button_released(self, *args):
-		"""
-		This helps to call 'move_cursor'
-		"""
-		offset = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert()).get_offset()
-		if offset != self._offset:
-			self._offset = offset
-			self.on_cursor_moved(offset)
-	
-	def __on_button_pressed(self, text_view, event):
-		"""
-		Mouse button has been pressed on the TextView
-		"""
-		if event.button == 3:	# right button
-			x, y = text_view.get_pointer()
-			x, y = text_view.window_to_buffer_coords(Gtk.TextWindowType.WIDGET, x, y)
-			it = text_view.get_iter_at_location(x, y)
-			
-			self.__log.debug("Right button pressed at offset %s" % it.get_offset())
-			
-			#
-			# find Marker at this position
-			#
-			while True:
-				for mark in it.get_marks():
-					name = mark.get_name()
-					
-					self.__log.debug("Found TextMark '%s' at offset %s" % (name, it.get_offset()))
-					
-					if name:
-						if name in self._markers.keys():
-							marker = self._markers[name]
-							return self.on_marker_activated(marker, event)
-						else:
-							self.__log.warning("No marker found for TextMark '%s'" % name)
-					else:
-						# FIXME: this is not safe - use another symbol for right boundaries!
-						self.__log.debug("Unnamed TextMark found, outside of any Markers")
-						return
-				
-				# move left by one char and continue 
-				if not it.backward_char():
-					# start of buffer reached
-					return
-		
-		elif event.button == 1 and event.get_state() & Gdk.ModifierType.CONTROL_MASK:
-			x, y = text_view.get_pointer()
-			x, y = text_view.window_to_buffer_coords(Gtk.TextWindowType.WIDGET, x, y)
-			it = text_view.get_iter_at_location(x, y)
-			# notify subclass
-			self._ctrl_left_clicked(it)
-	
-	def _ctrl_left_clicked(self, it):
-		"""
-		Left-clicked on the editor with Ctrl modifier key pressed
-		@param it: the Gtk.TextIter at the clicked position
-		"""
-	
-	@property
-	def file(self):
-		return self._file
-	
-	@property
-	def tab_decorator(self):
-		return self._tab_decorator
-	
-	def delete_at_cursor(self, offset):
-		"""
-		Delete characters relative to the cursor position:
-		
-		offset < 0		delete characters from offset to cursor
-		offset > 0		delete characters from cursor to offset
-		"""
-		start = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
-		end = self._text_buffer.get_iter_at_offset(start.get_offset() + offset)
-		self._text_buffer.delete(start, end)
-	
-	# methods/properties to be used/overridden by the subclass
-	
-	@property
-	def extensions(self):
-		"""
-		Return a list of extensions for which this Editor is to be activated
-		"""
-		raise NotImplementedError
-	
-	def drag_drop_received(self, files):
-		"""
-		To be overridden
-		
-		@param files: a list of File objects dropped on the Editor
-		"""
-		pass
-	
-	@property
-	def initial_timestamp(self):
-		"""
-		Return an initial reference timestamp (this just has to be smaller than
-		every value returned by current_timestamp)
-		"""
-		return 0
-	
-	@property
-	def current_timestamp(self):
-		"""
-		Return the current timestamp for buffer change recognition
-		"""
-		return time.time()
-	
-	def content_changed(self, reference_timestamp):
-		"""
-		Return True if the content of this Editor has changed since a given
-		reference timestamp (this must be a timestamp as returned by current_timestamp)
-		"""
-		return self.__buffer_change_timestamp > reference_timestamp
-	
-	@property
-	def charset(self):
-		"""
-		Return the character set used by this Editor
-		"""
-		return self._text_buffer.get_encoding().get_charset()
-	
-	@property
-	def content(self):
-		"""
-		Return the string contained in the TextBuffer
-		"""
-		return self._text_buffer.get_text(self._text_buffer.get_start_iter(), 
-									self._text_buffer.get_end_iter(), False).decode(self.charset)
-	
-	@property
-	def content_at_left_of_cursor(self):
-		"""
-		Only return the content at left of the cursor
-		"""
-		end_iter = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
-		return self._text_buffer.get_text(self._text_buffer.get_start_iter(), 
-									end_iter, False).decode(self.charset)
-	
-	def insert(self, source):
-		"""
-		This may be overridden to catch special types like LaTeXSource
-		"""
-		self.__log.debug("insert(%s)" % source)
-		
-		if type(source) is Template:
-			self._template_delegate.insert(source)
-		else:
-			self._text_buffer.insert_at_cursor(str(source))
-		
-		# grab the focus again (necessary e.g. after symbol insert)
-		self._text_view.grab_focus()
-	
-	def insert_at_offset(self, offset, string, scroll=False):
-		"""
-		Insert a string at a certain offset
-		
-		@param offset: a positive int
-		@param string: a str
-		@param scroll: if True the view is scrolled to the insert position
-		"""
-		iter = self._text_buffer.get_iter_at_offset(offset)
-		self._text_buffer.insert(iter, str(string))
-		
-		if scroll:
-			self._text_view.scroll_to_iter(iter, .25, False, 0.5, 0.5)
-		
-		# grab the focus again (necessary e.g. after symbol insert)
-		self._text_view.grab_focus()
-	
-	def append(self, string):
-		"""
-		Append some source (only makes sense with simple string) and scroll to it
-		
-		@param string: a str
-		"""
-		self._text_buffer.insert(self._text_buffer.get_end_iter(), str(string))
-		self._text_view.scroll_to_iter(self._text_buffer.get_end_iter(), .25, False, 0.5, 0.5)
-		
-		# grab the focus again (necessary e.g. after symbol insert)
-		self._text_view.grab_focus()
-	
-	@property
-	def indentation(self):
-		"""
-		Return the indentation string of the line at the cursor
-		"""
-		i_start = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
-		i_start.set_line_offset(0)
-		
-		i_end = i_start.copy()
-		i_end.forward_to_line_end()
-		string = self._text_buffer.get_text(i_start, i_end, False)
-		
-		match = self.__PATTERN_INDENT.match(string)
-		if match:
-			return match.group()
-		else:
-			return ""
-	
-	def select(self, start_offset, end_offset):
-		"""
-		Select a range of text and scroll the view to the right position
-		"""
-		# select
-		it_start = self._text_buffer.get_iter_at_offset(start_offset)
-		it_end = self._text_buffer.get_iter_at_offset(end_offset)
-		self._text_buffer.select_range(it_start, it_end)
-		# scroll
-		self._text_view.scroll_to_iter(it_end, .25, False, 0.5, 0.5)
-	
-	def select_lines(self, start_line, end_line=None):
-		"""
-		Select a range of lines in the text
-		
-		@param start_line: the first line to select (counting from 0)
-		@param end_line: the last line to select (if None only the first line is selected)
-		"""
-		it_start = self._text_buffer.get_iter_at_line(start_line)
-		if end_line:
-			it_end = self._text_buffer.get_iter_at_line(end_line)
-		else:
-			it_end = it_start.copy()
-		it_end.forward_to_line_end()
-		# select
-		self._text_buffer.select_range(it_start, it_end)
-		# scroll
-		self._text_view.scroll_to_iter(it_end, .25, False, 0.5, 0.5)
-	
-	#
-	# markers are used for highlighting (anonymous)
-	#
-	
-	def register_marker_type(self, marker_type, background_color, anonymous=True):
-		"""
-		@param marker_type: a string
-		@param background_color: a hex color
-		@param anonymous: markers of an anonymous type may not be activated and do not get a unique ID
-		"""
-		assert not marker_type in self._marker_types.keys()
-		
-		# create Gtk.TextTag
-		tag = self._text_buffer.create_tag(marker_type, background=background_color)
-		
-		self._tags.append(tag)
-		
-		# create a MarkerTypeRecord for this type
-		self._marker_types[marker_type] = self.MarkerTypeRecord(tag, anonymous)
-	
-	def create_marker(self, marker_type, start_offset, end_offset):
-		"""
-		Mark a section of the text
-		
-		@param marker_type: type string
-		@return: a Marker object if the type is not anonymous or None otherwise
-		"""
-		
-		# check offsets
-		if start_offset < 0:
-			self.__log.error("create_marker(): start offset out of range (%s < 0)" % start_offset)
-			return
-		
-		buffer_end_offset = self._text_buffer.get_end_iter().get_offset()
-		
-		if end_offset > buffer_end_offset:
-			self.__log.error("create_marker(): end offset out of range (%s > %s)" % (end_offset, buffer_end_offset))
-		
-		
-		type_record = self._marker_types[marker_type]
-		
-		# hightlight
-		left = self._text_buffer.get_iter_at_offset(start_offset)
-		right = self._text_buffer.get_iter_at_offset(end_offset)
-		self._text_buffer.apply_tag_by_name(marker_type, left, right)
-		
-		if type_record.anonymous:
-			# create TextMarks
-			left_mark = self._text_buffer.create_mark(None, left, True)
-			right_mark = self._text_buffer.create_mark(None, right, False)
-			
-			# create Marker object
-			marker = self.Marker(left_mark, right_mark, None, marker_type)
-			
-			# store Marker
-			type_record.markers.append(marker)
-			
-			return None
-		else:
-			# create unique marker id
-			id = str(uuid1())
-			
-			# create Marker object and put into map
-			left_mark = self._text_buffer.create_mark(id, left, True)
-			right_mark = self._text_buffer.create_mark(None, right, False)
-			marker = self.Marker(left_mark, right_mark, id, marker_type)
-		
-			# store Marker
-			self._markers[id] = marker
-			type_record.markers.append(marker)
-			
-			return marker
-	
-	def remove_marker(self, marker):
-		"""
-		@param marker: the Marker object to remove
-		"""
-		# create TextIters from TextMarks
-		left_iter = self._text_buffer.get_iter_at_mark(marker.left_mark)
-		right_iter = self._text_buffer.get_iter_at_mark(marker.right_mark)
-		
-		# remove TextTag
-		type_record = self._marker_types[marker.type]
-		self._text_buffer.remove_tag(type_record.tag, left_iter, right_iter)
-		
-		# remove TextMarks
-		self._text_buffer.delete_mark(marker.left_mark)
-		self._text_buffer.delete_mark(marker.right_mark)
-		
-		# remove Marker from MarkerTypeRecord
-		i = type_record.markers.index(marker)
-		del type_record.markers[i]
-		
-		# remove from id map
-		del self._markers[marker.id]
-	
-	def remove_markers(self, marker_type):
-		"""
-		Remove all markers of a certain type
-		"""
-		type_record = self._marker_types[marker_type]
-		
-		for marker in type_record.markers:
-			assert not marker.left_mark.get_deleted()
-			assert not marker.right_mark.get_deleted()
-			
-			# create TextIters from TextMarks
-			left_iter = self._text_buffer.get_iter_at_mark(marker.left_mark)
-			right_iter = self._text_buffer.get_iter_at_mark(marker.right_mark)
-			
-			# remove TextTag
-			self._text_buffer.remove_tag(type_record.tag, left_iter, right_iter)
-			
-			# remove TextMarks
-			self._text_buffer.delete_mark(marker.left_mark)
-			self._text_buffer.delete_mark(marker.right_mark)
-			
-			if not type_record.anonymous:
-				# remove Marker from id map
-				del self._markers[marker.id]
-		
-		# remove markers from MarkerTypeRecord
-		type_record.markers = []
-	
-	def replace_marker_content(self, marker, content):
-		# get TextIters
-		left = self._text_buffer.get_iter_at_mark(marker.left_mark)
-		right = self._text_buffer.get_iter_at_mark(marker.right_mark)
-		
-		# replace
-		self._text_buffer.delete(left, right)
-		left = self._text_buffer.get_iter_at_mark(marker.left_mark)
-		self._text_buffer.insert(left, content)
-		
-		# remove Marker
-		self.remove_marker(marker)
-	
-	def on_marker_activated(self, marker, event):
-		"""
-		A marker has been activated
-		
-		To be overridden
-		
-		@param id: id of the activated marker
-		@param event: the GdkEvent of the mouse click (for raising context menus)
-		"""
-	
-	@property
-	def completion_handlers(self):
-		"""
-		To be overridden
-		
-		@return: a list of objects implementing CompletionHandler
-		"""
-		return []
-	
-	def init(self, file, context):
-		"""
-		@param file: File object
-		@param context: WindowContext object
-		"""
-	
-	def on_save(self):
-		"""
-		The file has been saved to its original location
-		"""
-	
-	def on_cursor_moved(self, offset):
-		"""
-		The cursor has moved
-		"""
-	
-	def destroy(self):
-		"""
-		The edited file has been closed or saved as another file
-		"""
-		self.__log.debug("destroy")
-		
-		# disconnect signal handlers
-		for handler in self.__view_signal_handlers:
-			self._text_view.disconnect(handler)
-		
-		for handler in self.__buffer_signal_handlers:
-			self._text_buffer.disconnect(handler)
-		
-		# delete the tags that were created for markers
-		table = self._text_buffer.get_tag_table()
-		for tag in self._tags:
-			table.remove(tag)
-		
-		# destroy the template delegate
-		self._template_delegate.destroy()
-		del self._template_delegate
-		
-		# destroy the views associated to this editor
-		for i in self._window_context.editor_scope_views[self]:
-			self._window_context.editor_scope_views[self][i].destroy()
-		del self._window_context.editor_scope_views[self]
-		
-		# unreference the tab decorator
-		del self._tab_decorator
-
-		# destroy the completion distributor
-		if self._completion_distributor != None:
-			self._completion_distributor.destroy()
-		del self._completion_distributor
-
-		# unreference the window context
-		del self._window_context
-		
-	def __del__(self):
-		self._log.debug("Properly destroyed %s" % self)
+    """
+    The base class for editors. This manages
+     - the subclass life-cycle
+     - the marker framework
+     - change monitoring
+     - drag'n'drop support
+    """
+
+    __log = getLogger("Editor")
+
+
+    class Marker(object):
+        """
+        Markers refer to and highlight a range of text in the TextBuffer decorated by
+        an Editor. They are used for highlighting issues.
+
+        Each Marker instance stores two Gtk.TextMark objects refering to the start and
+        end of the text range.
+        """
+        def __init__(self, left_mark, right_mark, id, type):
+            """
+            @param left_mark: a Gtk.TextMark
+            @param right_mark: a Gtk.TextMark
+            @param id: a unique string
+            @param type: a marker type string
+            """
+            self.left_mark = left_mark
+            self.right_mark = right_mark
+            self.type = type
+            self.id = id
+
+
+    class MarkerTypeRecord(object):
+        """
+        This used for managing Marker types
+        """
+        def __init__(self, tag, anonymous):
+            """
+            @param tag: a Gtk.TextTag
+            """
+            self.tag = tag
+            self.anonymous = anonymous
+            self.markers = []
+
+
+    __PATTERN_INDENT = re.compile("[ \t]+")
+
+
+    # A list of file extensions
+    #
+    # If one or more files with one of these extensions is dragged and dropped on the editor,
+    # the Editor.drag_drop_received method is called. An empty list disables the dnd support.
+    dnd_extensions = []
+
+
+    def __init__(self, tab_decorator, file):
+        self._tab_decorator = tab_decorator
+        self._file = file
+        self._text_buffer = tab_decorator.tab.get_document()
+        self._text_view = tab_decorator.tab.get_view()
+
+        # create template delegate
+        self._template_delegate = TemplateDelegate(self)
+
+        # hook completion handlers of the subclassing Editor
+        completion_handlers = self.completion_handlers
+        if len(completion_handlers):
+            self._completion_distributor = CompletionDistributor(self, completion_handlers)
+        else:
+            self._completion_distributor = None
+
+        #
+        # init marker framework
+        #
+
+        # needed for cleanup
+        self._tags = []
+        self._marker_types = {}    # {marker type -> MarkerTypeRecord object}
+        self._markers = {}        # { marker id -> marker object }
+
+
+        self._window_context = self._tab_decorator._window_decorator._window_context
+        self._window_context.create_editor_views(self, file)
+
+
+        self._offset = None        # used by move_cursor
+
+        self.__view_signal_handlers = [
+                self._text_view.connect("button-press-event", self.__on_button_pressed),
+                self._text_view.connect("key-release-event", self.__on_key_released),
+                self._text_view.connect("button-release-event", self.__on_button_released) ]
+
+        self.__buffer_change_timestamp = time.time()
+        self.__buffer_signal_handlers = [
+                self._text_buffer.connect("changed", self.__on_buffer_changed) ]
+
+        # dnd support
+        if len(self.dnd_extensions) > 0:
+            self.__view_signal_handlers.append(
+                    self._text_view.connect("drag-data-received", self.__on_drag_data_received))
+
+        # start life-cycle for subclass
+        self.init(file, self._window_context)
+
+    def __on_buffer_changed(self, text_buffer):
+        """
+        Store the timestamp of the last buffer change
+        """
+        self.__buffer_change_timestamp = time.time()
+
+    def __on_drag_data_received(self, widget, context, x, y, data, info, timestamp):
+        """
+        The drag destination received the data from the drag operation
+
+        @param widget: the widget that received the signal
+        @param context: the Gdk.DragContext
+        @param x: the X position of the drop
+        @param y: the Y position of the drop
+        @param data: a Gtk.SelectionData object
+        @param info: an integer ID for the drag
+        @param timestamp: the time of the drag event
+        """
+        self.__log.debug("drag-data-received")
+
+        files = []
+        match = False
+
+        for uri in data.get_uris():
+            file = File(uri)
+            files.append(file)
+            if file.extension.lower() in self.dnd_extensions:
+                match = True
+
+        if match:
+            self.drag_drop_received(files)
+
+    def __on_key_released(self, *args):
+        """
+        This helps to call 'move_cursor'
+        """
+        offset = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert()).get_offset()
+        if offset != self._offset:
+            self._offset = offset
+            self.on_cursor_moved(offset)
+
+    def __on_button_released(self, *args):
+        """
+        This helps to call 'move_cursor'
+        """
+        offset = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert()).get_offset()
+        if offset != self._offset:
+            self._offset = offset
+            self.on_cursor_moved(offset)
+
+    def __on_button_pressed(self, text_view, event):
+        """
+        Mouse button has been pressed on the TextView
+        """
+        if event.button == 3:    # right button
+            x, y = text_view.get_pointer()
+            x, y = text_view.window_to_buffer_coords(Gtk.TextWindowType.WIDGET, x, y)
+            it = text_view.get_iter_at_location(x, y)
+
+            self.__log.debug("Right button pressed at offset %s" % it.get_offset())
+
+            #
+            # find Marker at this position
+            #
+            while True:
+                for mark in it.get_marks():
+                    name = mark.get_name()
+
+                    self.__log.debug("Found TextMark '%s' at offset %s" % (name, it.get_offset()))
+
+                    if name:
+                        if name in self._markers.keys():
+                            marker = self._markers[name]
+                            return self.on_marker_activated(marker, event)
+                        else:
+                            self.__log.warning("No marker found for TextMark '%s'" % name)
+                    else:
+                        # FIXME: this is not safe - use another symbol for right boundaries!
+                        self.__log.debug("Unnamed TextMark found, outside of any Markers")
+                        return
+
+                # move left by one char and continue
+                if not it.backward_char():
+                    # start of buffer reached
+                    return
+
+        elif event.button == 1 and event.get_state() & Gdk.ModifierType.CONTROL_MASK:
+            x, y = text_view.get_pointer()
+            x, y = text_view.window_to_buffer_coords(Gtk.TextWindowType.WIDGET, x, y)
+            it = text_view.get_iter_at_location(x, y)
+            # notify subclass
+            self._ctrl_left_clicked(it)
+
+    def _ctrl_left_clicked(self, it):
+        """
+        Left-clicked on the editor with Ctrl modifier key pressed
+        @param it: the Gtk.TextIter at the clicked position
+        """
+
+    @property
+    def file(self):
+        return self._file
+
+    @property
+    def tab_decorator(self):
+        return self._tab_decorator
+
+    def delete_at_cursor(self, offset):
+        """
+        Delete characters relative to the cursor position:
+
+        offset < 0        delete characters from offset to cursor
+        offset > 0        delete characters from cursor to offset
+        """
+        start = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
+        end = self._text_buffer.get_iter_at_offset(start.get_offset() + offset)
+        self._text_buffer.delete(start, end)
+
+    # methods/properties to be used/overridden by the subclass
+
+    @property
+    def extensions(self):
+        """
+        Return a list of extensions for which this Editor is to be activated
+        """
+        raise NotImplementedError
+
+    def drag_drop_received(self, files):
+        """
+        To be overridden
+
+        @param files: a list of File objects dropped on the Editor
+        """
+        pass
+
+    @property
+    def initial_timestamp(self):
+        """
+        Return an initial reference timestamp (this just has to be smaller than
+        every value returned by current_timestamp)
+        """
+        return 0
+
+    @property
+    def current_timestamp(self):
+        """
+        Return the current timestamp for buffer change recognition
+        """
+        return time.time()
+
+    def content_changed(self, reference_timestamp):
+        """
+        Return True if the content of this Editor has changed since a given
+        reference timestamp (this must be a timestamp as returned by current_timestamp)
+        """
+        return self.__buffer_change_timestamp > reference_timestamp
+
+    @property
+    def charset(self):
+        """
+        Return the character set used by this Editor
+        """
+        return self._text_buffer.get_encoding().get_charset()
+
+    @property
+    def content(self):
+        """
+        Return the string contained in the TextBuffer
+        """
+        return self._text_buffer.get_text(self._text_buffer.get_start_iter(),
+                                    self._text_buffer.get_end_iter(), False).decode(self.charset)
+
+    @property
+    def content_at_left_of_cursor(self):
+        """
+        Only return the content at left of the cursor
+        """
+        end_iter = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
+        return self._text_buffer.get_text(self._text_buffer.get_start_iter(),
+                                    end_iter, False).decode(self.charset)
+
+    def insert(self, source):
+        """
+        This may be overridden to catch special types like LaTeXSource
+        """
+        self.__log.debug("insert(%s)" % source)
+
+        if type(source) is Template:
+            self._template_delegate.insert(source)
+        else:
+            self._text_buffer.insert_at_cursor(str(source))
+
+        # grab the focus again (necessary e.g. after symbol insert)
+        self._text_view.grab_focus()
+
+    def insert_at_offset(self, offset, string, scroll=False):
+        """
+        Insert a string at a certain offset
+
+        @param offset: a positive int
+        @param string: a str
+        @param scroll: if True the view is scrolled to the insert position
+        """
+        iter = self._text_buffer.get_iter_at_offset(offset)
+        self._text_buffer.insert(iter, str(string))
+
+        if scroll:
+            self._text_view.scroll_to_iter(iter, .25, False, 0.5, 0.5)
+
+        # grab the focus again (necessary e.g. after symbol insert)
+        self._text_view.grab_focus()
+
+    def append(self, string):
+        """
+        Append some source (only makes sense with simple string) and scroll to it
+
+        @param string: a str
+        """
+        self._text_buffer.insert(self._text_buffer.get_end_iter(), str(string))
+        self._text_view.scroll_to_iter(self._text_buffer.get_end_iter(), .25, False, 0.5, 0.5)
+
+        # grab the focus again (necessary e.g. after symbol insert)
+        self._text_view.grab_focus()
+
+    @property
+    def indentation(self):
+        """
+        Return the indentation string of the line at the cursor
+        """
+        i_start = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
+        i_start.set_line_offset(0)
+
+        i_end = i_start.copy()
+        i_end.forward_to_line_end()
+        string = self._text_buffer.get_text(i_start, i_end, False)
+
+        match = self.__PATTERN_INDENT.match(string)
+        if match:
+            return match.group()
+        else:
+            return ""
+
+    def select(self, start_offset, end_offset):
+        """
+        Select a range of text and scroll the view to the right position
+        """
+        # select
+        it_start = self._text_buffer.get_iter_at_offset(start_offset)
+        it_end = self._text_buffer.get_iter_at_offset(end_offset)
+        self._text_buffer.select_range(it_start, it_end)
+        # scroll
+        self._text_view.scroll_to_iter(it_end, .25, False, 0.5, 0.5)
+
+    def select_lines(self, start_line, end_line=None):
+        """
+        Select a range of lines in the text
+
+        @param start_line: the first line to select (counting from 0)
+        @param end_line: the last line to select (if None only the first line is selected)
+        """
+        it_start = self._text_buffer.get_iter_at_line(start_line)
+        if end_line:
+            it_end = self._text_buffer.get_iter_at_line(end_line)
+        else:
+            it_end = it_start.copy()
+        it_end.forward_to_line_end()
+        # select
+        self._text_buffer.select_range(it_start, it_end)
+        # scroll
+        self._text_view.scroll_to_iter(it_end, .25, False, 0.5, 0.5)
+
+    #
+    # markers are used for highlighting (anonymous)
+    #
+
+    def register_marker_type(self, marker_type, background_color, anonymous=True):
+        """
+        @param marker_type: a string
+        @param background_color: a hex color
+        @param anonymous: markers of an anonymous type may not be activated and do not get a unique ID
+        """
+        assert not marker_type in self._marker_types.keys()
+
+        # create Gtk.TextTag
+        tag = self._text_buffer.create_tag(marker_type, background=background_color)
+
+        self._tags.append(tag)
+
+        # create a MarkerTypeRecord for this type
+        self._marker_types[marker_type] = self.MarkerTypeRecord(tag, anonymous)
+
+    def create_marker(self, marker_type, start_offset, end_offset):
+        """
+        Mark a section of the text
+
+        @param marker_type: type string
+        @return: a Marker object if the type is not anonymous or None otherwise
+        """
+
+        # check offsets
+        if start_offset < 0:
+            self.__log.error("create_marker(): start offset out of range (%s < 0)" % start_offset)
+            return
+
+        buffer_end_offset = self._text_buffer.get_end_iter().get_offset()
+
+        if end_offset > buffer_end_offset:
+            self.__log.error("create_marker(): end offset out of range (%s > %s)" % (end_offset, buffer_end_offset))
+
+
+        type_record = self._marker_types[marker_type]
+
+        # hightlight
+        left = self._text_buffer.get_iter_at_offset(start_offset)
+        right = self._text_buffer.get_iter_at_offset(end_offset)
+        self._text_buffer.apply_tag_by_name(marker_type, left, right)
+
+        if type_record.anonymous:
+            # create TextMarks
+            left_mark = self._text_buffer.create_mark(None, left, True)
+            right_mark = self._text_buffer.create_mark(None, right, False)
+
+            # create Marker object
+            marker = self.Marker(left_mark, right_mark, None, marker_type)
+
+            # store Marker
+            type_record.markers.append(marker)
+
+            return None
+        else:
+            # create unique marker id
+            id = str(uuid1())
+
+            # create Marker object and put into map
+            left_mark = self._text_buffer.create_mark(id, left, True)
+            right_mark = self._text_buffer.create_mark(None, right, False)
+            marker = self.Marker(left_mark, right_mark, id, marker_type)
+
+            # store Marker
+            self._markers[id] = marker
+            type_record.markers.append(marker)
+
+            return marker
+
+    def remove_marker(self, marker):
+        """
+        @param marker: the Marker object to remove
+        """
+        # create TextIters from TextMarks
+        left_iter = self._text_buffer.get_iter_at_mark(marker.left_mark)
+        right_iter = self._text_buffer.get_iter_at_mark(marker.right_mark)
+
+        # remove TextTag
+        type_record = self._marker_types[marker.type]
+        self._text_buffer.remove_tag(type_record.tag, left_iter, right_iter)
+
+        # remove TextMarks
+        self._text_buffer.delete_mark(marker.left_mark)
+        self._text_buffer.delete_mark(marker.right_mark)
+
+        # remove Marker from MarkerTypeRecord
+        i = type_record.markers.index(marker)
+        del type_record.markers[i]
+
+        # remove from id map
+        del self._markers[marker.id]
+
+    def remove_markers(self, marker_type):
+        """
+        Remove all markers of a certain type
+        """
+        type_record = self._marker_types[marker_type]
+
+        for marker in type_record.markers:
+            assert not marker.left_mark.get_deleted()
+            assert not marker.right_mark.get_deleted()
+
+            # create TextIters from TextMarks
+            left_iter = self._text_buffer.get_iter_at_mark(marker.left_mark)
+            right_iter = self._text_buffer.get_iter_at_mark(marker.right_mark)
+
+            # remove TextTag
+            self._text_buffer.remove_tag(type_record.tag, left_iter, right_iter)
+
+            # remove TextMarks
+            self._text_buffer.delete_mark(marker.left_mark)
+            self._text_buffer.delete_mark(marker.right_mark)
+
+            if not type_record.anonymous:
+                # remove Marker from id map
+                del self._markers[marker.id]
+
+        # remove markers from MarkerTypeRecord
+        type_record.markers = []
+
+    def replace_marker_content(self, marker, content):
+        # get TextIters
+        left = self._text_buffer.get_iter_at_mark(marker.left_mark)
+        right = self._text_buffer.get_iter_at_mark(marker.right_mark)
+
+        # replace
+        self._text_buffer.delete(left, right)
+        left = self._text_buffer.get_iter_at_mark(marker.left_mark)
+        self._text_buffer.insert(left, content)
+
+        # remove Marker
+        self.remove_marker(marker)
+
+    def on_marker_activated(self, marker, event):
+        """
+        A marker has been activated
+
+        To be overridden
+
+        @param id: id of the activated marker
+        @param event: the GdkEvent of the mouse click (for raising context menus)
+        """
+
+    @property
+    def completion_handlers(self):
+        """
+        To be overridden
+
+        @return: a list of objects implementing CompletionHandler
+        """
+        return []
+
+    def init(self, file, context):
+        """
+        @param file: File object
+        @param context: WindowContext object
+        """
+
+    def on_save(self):
+        """
+        The file has been saved to its original location
+        """
+
+    def on_cursor_moved(self, offset):
+        """
+        The cursor has moved
+        """
+
+    def destroy(self):
+        """
+        The edited file has been closed or saved as another file
+        """
+        self.__log.debug("destroy")
+
+        # disconnect signal handlers
+        for handler in self.__view_signal_handlers:
+            self._text_view.disconnect(handler)
+
+        for handler in self.__buffer_signal_handlers:
+            self._text_buffer.disconnect(handler)
+
+        # delete the tags that were created for markers
+        table = self._text_buffer.get_tag_table()
+        for tag in self._tags:
+            table.remove(tag)
+
+        # destroy the template delegate
+        self._template_delegate.destroy()
+        del self._template_delegate
+
+        # destroy the views associated to this editor
+        for i in self._window_context.editor_scope_views[self]:
+            self._window_context.editor_scope_views[self][i].destroy()
+        del self._window_context.editor_scope_views[self]
+
+        # unreference the tab decorator
+        del self._tab_decorator
+
+        # destroy the completion distributor
+        if self._completion_distributor != None:
+            self._completion_distributor.destroy()
+        del self._completion_distributor
+
+        # unreference the window context
+        del self._window_context
+
+    def __del__(self):
+        self._log.debug("Properly destroyed %s" % self)
 
 
 class WindowContext(object):
-	"""
-	The WindowContext is passed to Editors and is used to 
-	 * retrieve View instances
-	 * activate a specific Editor instance
-	 * retrieve the currently active Editor
-	
-	This also creates and destroys the View instances.
-	"""
-	
-	_log = getLogger("WindowContext")
-	
-	def __init__(self, window_decorator, editor_scope_view_classes):
-		"""
-		@param window_decorator: the GeditWindowDecorator this context corresponds to
-		@param editor_scope_view_classes: a map from extension to list of View classes
-		"""
-		self._window_decorator = window_decorator
-		self._editor_scope_view_classes = editor_scope_view_classes
-		
-		self.window_scope_views = {}	# maps view ids to View objects
-		self.editor_scope_views = {}	# maps Editor object to a map from ID to View object
-
-		self._log.debug("init")
-	
-	def create_editor_views(self, editor, file):
-		"""
-		Create instances of the editor specific Views for a given Editor instance
-		and File
-		
-		Called by Editor base class
-		"""
-		self.editor_scope_views[editor] = {}
-		try:
-			for id, clazz in self._editor_scope_view_classes[file.extension].iteritems():
-				# create View instance and add it to the map
-				self.editor_scope_views[editor][id] = clazz(self, editor)
-				
-				self._log.debug("Created view " + id)
-		except KeyError:
-			self._log.debug("No views for %s" % file.extension)
-	
-	###
-	# public interface
-	
-	@property
-	def active_editor(self):
-		"""
-		Return the active Editor instance
-		"""
-		return self._window_decorator._active_tab_decorator.editor
-	
-	def activate_editor(self, file):
-		"""
-		Activate the Editor containing a given File or open a new tab for it
-		
-		@param file: a File object
-		
-		@raise AssertError: if the file is no File object
-		"""
-		assert type(file) is File
-		
-		self._window_decorator.activate_tab(file)
-	
-	def find_view(self, editor, view_id):
-		"""
-		Return a View object
-		"""
-		try:
-			return self.editor_scope_views[editor][view_id]
-		except KeyError:
-			return self.window_scope_views[view_id]
-	
-	def set_action_enabled(self, action_id, enabled):
-		"""
-		Enable/disable an IAction object
-		"""
-		self._window_decorator._action_group.get_action(action_id).set_sensitive(enabled)
-		
-	def destroy(self):
-		# unreference the window decorator
-		del self._window_decorator
-
-	def __del__(self):
-		self._log.debug("Properly destroyed %s" % self)
-	
+    """
+    The WindowContext is passed to Editors and is used to
+     * retrieve View instances
+     * activate a specific Editor instance
+     * retrieve the currently active Editor
+
+    This also creates and destroys the View instances.
+    """
+
+    _log = getLogger("WindowContext")
+
+    def __init__(self, window_decorator, editor_scope_view_classes):
+        """
+        @param window_decorator: the GeditWindowDecorator this context corresponds to
+        @param editor_scope_view_classes: a map from extension to list of View classes
+        """
+        self._window_decorator = window_decorator
+        self._editor_scope_view_classes = editor_scope_view_classes
+
+        self.window_scope_views = {}    # maps view ids to View objects
+        self.editor_scope_views = {}    # maps Editor object to a map from ID to View object
+
+        self._log.debug("init")
+
+    def create_editor_views(self, editor, file):
+        """
+        Create instances of the editor specific Views for a given Editor instance
+        and File
+
+        Called by Editor base class
+        """
+        self.editor_scope_views[editor] = {}
+        try:
+            for id, clazz in self._editor_scope_view_classes[file.extension].iteritems():
+                # create View instance and add it to the map
+                self.editor_scope_views[editor][id] = clazz(self, editor)
+
+                self._log.debug("Created view " + id)
+        except KeyError:
+            self._log.debug("No views for %s" % file.extension)
+
+    ###
+    # public interface
+
+    @property
+    def active_editor(self):
+        """
+        Return the active Editor instance
+        """
+        return self._window_decorator._active_tab_decorator.editor
+
+    def activate_editor(self, file):
+        """
+        Activate the Editor containing a given File or open a new tab for it
+
+        @param file: a File object
+
+        @raise AssertError: if the file is no File object
+        """
+        assert type(file) is File
+
+        self._window_decorator.activate_tab(file)
+
+    def find_view(self, editor, view_id):
+        """
+        Return a View object
+        """
+        try:
+            return self.editor_scope_views[editor][view_id]
+        except KeyError:
+            return self.window_scope_views[view_id]
+
+    def set_action_enabled(self, action_id, enabled):
+        """
+        Enable/disable an IAction object
+        """
+        self._window_decorator._action_group.get_action(action_id).set_sensitive(enabled)
+
+    def destroy(self):
+        # unreference the window decorator
+        del self._window_decorator
+
+    def __del__(self):
+        self._log.debug("Properly destroyed %s" % self)
+
 
 from os import remove
 import os.path
@@ -1005,366 +1005,363 @@ import urllib
 import urlparse
 
 def fixurl(url):
-	r"""From http://stackoverflow.com/questions/804336/best-way-to-convert-a-unicode-url-to-ascii-utf-8-percent-escaped-in-python/805166#805166 .
-	Was named canonurl(). Comments added to the original are prefixed with ##.
-	
-	Return the canonical, ASCII-encoded form of a UTF-8 encoded URL, or ''
-	if the URL looks invalid.
-
-	>>> canonurl('	')
-	''
-	>>> canonurl('www.google.com')
-	'http://www.google.com/'
-	>>> canonurl('bad-utf8.com/path\xff/file')
-	''
-	>>> canonurl('svn://blah.com/path/file')
-	'svn://blah.com/path/file'
-	>>> canonurl('1234://badscheme.com')
-	''
-	>>> canonurl('bad$scheme://google.com')
-	''
-	>>> canonurl('site.badtopleveldomain')
-	''
-	>>> canonurl('site.com:badport')
-	''
-	>>> canonurl('http://123.24.8.240/blah')
-	'http://123.24.8.240/blah'
-	>>> canonurl('http://123.24.8.240:1234/blah?q#f')
-	'http://123.24.8.240:1234/blah?q#f'
-	>>> canonurl('\xe2\x9e\xa1.ws')  # tinyarro.ws
-	'http://xn--hgi.ws/'
-	>>> canonurl('  http://www.google.com:80/path/file;params?query#fragment  ')
-	'http://www.google.com:80/path/file;params?query#fragment'
-	>>> canonurl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5')
-	'http://xn--hgi.ws/%E2%99%A5'
-	>>> canonurl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5/pa%2Fth')
-	'http://xn--hgi.ws/%E2%99%A5/pa/th'
-	>>> canonurl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5/pa%2Fth;par%2Fams?que%2Fry=a&b=c')
-	'http://xn--hgi.ws/%E2%99%A5/pa/th;par/ams?que/ry=a&b=c'
-	>>> canonurl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5?\xe2\x99\xa5#\xe2\x99\xa5')
-	'http://xn--hgi.ws/%E2%99%A5?%E2%99%A5#%E2%99%A5'
-	>>> canonurl('http://\xe2\x9e\xa1.ws/%e2%99%a5?%E2%99%A5#%E2%99%A5')
-	'http://xn--hgi.ws/%E2%99%A5?%E2%99%A5#%E2%99%A5'
-	>>> canonurl('http://badutf8pcokay.com/%FF?%FE#%FF')
-	'http://badutf8pcokay.com/%FF?%FE#%FF'
-	>>> len(canonurl('google.com/' + 'a' * 16384))
-	4096
-	"""
-	# strip spaces at the ends and ensure it's prefixed with 'scheme://'	
-	url = url.strip()
-	if not url:
-		return ''
-	if not urlparse.urlsplit(url).scheme:
-		## We usually deal with local files here
-		url = 'file://' + url
-		## url = 'http://' + url
-
-	# turn it into Unicode
-	try:
-		url = unicode(url, 'utf-8')
-	except Exception, exc: #UnicodeDecodeError, exc:
-		## It often happens that the url is already "python unicode" encoded
-		if not str(exc) == "decoding Unicode is not supported":
-			return ''  # bad UTF-8 chars in URL
-		## If the exception is indeed "decoding Unicode is not supported"
-		## this generally means that url is already unicode encoded,
-		## so we can just continue (see http://www.red-mercury.com/blog/eclectic-tech/python-mystery-of-the-day/ )
-
-	# parse the URL into its components
-	parsed = urlparse.urlsplit(url)
-	scheme, netloc, path, query, fragment = parsed
-
-	# ensure scheme is a letter followed by letters, digits, and '+-.' chars
-	if not re.match(r'[a-z][-+.a-z0-9]*$', scheme, flags=re.I):
-		return ''
-	scheme = str(scheme)
-
-	## We mostly deal with local files here, and the following check 
-	## would exclude all local files, so we drop it.	
-	# ensure domain and port are valid, eg: sub.domain.<1-to-6-TLD-chars>[:port]
-	#~ match = re.match(r'(.+\.[a-z0-9]{1,6})(:\d{1,5})?$', netloc, flags=re.I)
-	#~ if not match:
-		#~ print "return 4"
-		#~ return ''
-	#~ domain, port = match.groups()
-	#~ netloc = domain + (port if port else '')
-	netloc = netloc.encode('idna')
-
-	# ensure path is valid and convert Unicode chars to %-encoded
-	if not path:
-		path = '/'  # eg: 'http://google.com' -> 'http://google.com/'
-	path = urllib.quote(urllib.unquote(path.encode('utf-8')), safe='/;')
-
-	# ensure query is valid
-	query = urllib.quote(urllib.unquote(query.encode('utf-8')), safe='=&?/')
-
-	# ensure fragment is valid
-	fragment = urllib.quote(urllib.unquote(fragment.encode('utf-8')))
-
-	# piece it all back together, truncating it to a maximum of 4KB
-	url = urlparse.urlunsplit((scheme, netloc, path, query, fragment))
-	return url[:4096]
+    r"""From http://stackoverflow.com/questions/804336/best-way-to-convert-a-unicode-url-to-ascii-utf-8-percent-escaped-in-python/805166#805166 .
+    Was named canonurl(). Comments added to the original are prefixed with ##.
+
+    Return the canonical, ASCII-encoded form of a UTF-8 encoded URL, or ''
+    if the URL looks invalid.
+
+    >>> canonurl('    ')
+    ''
+    >>> canonurl('www.google.com')
+    'http://www.google.com/'
+    >>> canonurl('bad-utf8.com/path\xff/file')
+    ''
+    >>> canonurl('svn://blah.com/path/file')
+    'svn://blah.com/path/file'
+    >>> canonurl('1234://badscheme.com')
+    ''
+    >>> canonurl('bad$scheme://google.com')
+    ''
+    >>> canonurl('site.badtopleveldomain')
+    ''
+    >>> canonurl('site.com:badport')
+    ''
+    >>> canonurl('http://123.24.8.240/blah')
+    'http://123.24.8.240/blah'
+    >>> canonurl('http://123.24.8.240:1234/blah?q#f')
+    'http://123.24.8.240:1234/blah?q#f'
+    >>> canonurl('\xe2\x9e\xa1.ws')  # tinyarro.ws
+    'http://xn--hgi.ws/'
+    >>> canonurl('  http://www.google.com:80/path/file;params?query#fragment  ')
+    'http://www.google.com:80/path/file;params?query#fragment'
+    >>> canonurl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5')
+    'http://xn--hgi.ws/%E2%99%A5'
+    >>> canonurl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5/pa%2Fth')
+    'http://xn--hgi.ws/%E2%99%A5/pa/th'
+    >>> canonurl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5/pa%2Fth;par%2Fams?que%2Fry=a&b=c')
+    'http://xn--hgi.ws/%E2%99%A5/pa/th;par/ams?que/ry=a&b=c'
+    >>> canonurl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5?\xe2\x99\xa5#\xe2\x99\xa5')
+    'http://xn--hgi.ws/%E2%99%A5?%E2%99%A5#%E2%99%A5'
+    >>> canonurl('http://\xe2\x9e\xa1.ws/%e2%99%a5?%E2%99%A5#%E2%99%A5')
+    'http://xn--hgi.ws/%E2%99%A5?%E2%99%A5#%E2%99%A5'
+    >>> canonurl('http://badutf8pcokay.com/%FF?%FE#%FF')
+    'http://badutf8pcokay.com/%FF?%FE#%FF'
+    >>> len(canonurl('google.com/' + 'a' * 16384))
+    4096
+    """
+    # strip spaces at the ends and ensure it's prefixed with 'scheme://'
+    url = url.strip()
+    if not url:
+        return ''
+    if not urlparse.urlsplit(url).scheme:
+        ## We usually deal with local files here
+        url = 'file://' + url
+        ## url = 'http://' + url
+
+    # turn it into Unicode
+    try:
+        url = unicode(url, 'utf-8')
+    except Exception, exc: #UnicodeDecodeError, exc:
+        ## It often happens that the url is already "python unicode" encoded
+        if not str(exc) == "decoding Unicode is not supported":
+            return ''  # bad UTF-8 chars in URL
+        ## If the exception is indeed "decoding Unicode is not supported"
+        ## this generally means that url is already unicode encoded,
+        ## so we can just continue (see http://www.red-mercury.com/blog/eclectic-tech/python-mystery-of-the-day/ )
+
+    # parse the URL into its components
+    parsed = urlparse.urlsplit(url)
+    scheme, netloc, path, query, fragment = parsed
+
+    # ensure scheme is a letter followed by letters, digits, and '+-.' chars
+    if not re.match(r'[a-z][-+.a-z0-9]*$', scheme, flags=re.I):
+        return ''
+    scheme = str(scheme)
+
+    ## We mostly deal with local files here, and the following check
+    ## would exclude all local files, so we drop it.
+    # ensure domain and port are valid, eg: sub.domain.<1-to-6-TLD-chars>[:port]
+    #~ match = re.match(r'(.+\.[a-z0-9]{1,6})(:\d{1,5})?$', netloc, flags=re.I)
+    #~ if not match:
+        #~ print "return 4"
+        #~ return ''
+    #~ domain, port = match.groups()
+    #~ netloc = domain + (port if port else '')
+    netloc = netloc.encode('idna')
+
+    # ensure path is valid and convert Unicode chars to %-encoded
+    if not path:
+        path = '/'  # eg: 'http://google.com' -> 'http://google.com/'
+    path = urllib.quote(urllib.unquote(path.encode('utf-8')), safe='/;')
+
+    # ensure query is valid
+    query = urllib.quote(urllib.unquote(query.encode('utf-8')), safe='=&?/')
+
+    # ensure fragment is valid
+    fragment = urllib.quote(urllib.unquote(fragment.encode('utf-8')))
+
+    # piece it all back together, truncating it to a maximum of 4KB
+    url = urlparse.urlunsplit((scheme, netloc, path, query, fragment))
+    return url[:4096]
 
 
 class File(object):
-	"""
-	This is an object-oriented wrapper for all the os.* stuff. A File object
-	represents the reference to a file.
-	"""
-	
-	# TODO: use Gio.File as underlying implementation
-	
-	@staticmethod
-	def create_from_relative_path(relative_path, working_directory):
-		"""
-		Create a File from a path relative to some working directory. 
-		
-		File.create_from_relative_path('../sub/file.txt', '/home/michael/base') == File('/home/michael/sub/file.txt')
-		
-		@param relative_path: a relative path, e.g. '../../dir/myfile.txt'
-		@param working_directory: an absolute directory to be used as the starting point for the relative path
-		"""
-		absolute_path = os.path.abspath(os.path.join(working_directory, relative_path))
-		return File(absolute_path)
-	
-	@staticmethod
-	def is_absolute(path):
-		return os.path.isabs(path)
-	
-	__log = getLogger("File")
-	
-	_DEFAULT_SCHEME = "file://"
-	
-	def __init__(self, uri):
-		"""
-		@param uri: any URI, URL or local filename
-		"""
-		if uri is None:
-			raise ValueError("URI must not be None")
-		
-		self._uri = urlparse.urlparse(uri)
-		if len(self._uri.scheme) == 0:
-			# prepend default scheme if missing
-			self._uri = urlparse.urlparse("%s%s" % (self._DEFAULT_SCHEME, uri))
-	
-	def create(self, content=None):
-		"""
-		Create a the File in the file system
-		"""
-		f = open(self.path, "w")
-		if content is not None:
-			f.write(content)
-		f.close()
-	
-	@property
-	def path(self):
-		"""
-		Returns '/home/user/image.jpg' for 'file:///home/user/image.jpg'
-		"""
-		return urllib.url2pathname(self._uri.path)
-	
-	@property
-	def extension(self):
-		"""
-		Returns '.jpg' for 'file:///home/user/image.jpg'
-		"""
-		return os.path.splitext(self.path)[1]
-	
-	@property
-	def shortname(self):
-		"""
-		Returns '/home/user/image' for 'file:///home/user/image.jpg'
-		"""
-		return os.path.splitext(self.path)[0]
-	
-	@property
-	def basename(self):
-		"""
-		Returns 'image.jpg' for 'file:///home/user/image.jpg'
-		"""
-		return os.path.basename(self.path)
-	
-	@property
-	def shortbasename(self):
-		"""
-		Returns 'image' for 'file:///home/user/image.jpg'
-		"""
-		return os.path.splitext(os.path.basename(self.path))[0]
-	
-	@property
-	def dirname(self):
-		"""
-		Returns '/home/user' for 'file:///home/user/image.jpg'
-		"""
-		return os.path.dirname(self.path)
-	
-	@property
-	def uri(self):
-		# TODO: urllib.quote doesn't support utf-8
-		return fixurl(self._uri.geturl())
-	
-	@property
-	def exists(self):
-		return os.path.exists(self.path)
-	
-	@property
-	def mtime(self):
-		if self.exists:
-			return os.path.getmtime(self.path)
-		else:
-			raise IOError("File not found")
-	
-	def find_neighbors(self, extension):
-		"""
-		Find other files in the directory of this one having
-		a certain extension
-		
-		@param extension: a file extension pattern like '.tex' or '.*'
-		"""
-		
-		# TODO: glob is quite expensive, find a simpler way for this
-		
-		try:
-			filenames = glob("%s/*%s" % (self.dirname, extension))
-			neighbors = [File(filename) for filename in filenames]
-			return neighbors
-		
-		except Exception, e:
-			# as seen in Bug #2002630 the glob() call compiles a regex and so we must be prepared
-			# for an exception from that because the shortname may contain regex characters
-			
-			# TODO: a more robust solution would be an escape() method for re
-			
-			self.__log.debug("find_neighbors: %s" % e)
-			
-			return []
-	
-	@property
-	def siblings(self):
-		"""
-		Find other files in the directory of this one having the same 
-		basename. This means for a file '/dir/a.doc' this method returns 
-		[ '/dir/a.tmp', '/dir/a.sh' ]
-		"""
-		siblings = []
-		try:
-			filenames = glob("%s.*" % self.shortname)
-			siblings = [File(filename) for filename in filenames]
-		except Exception, e:
-			# as seen in Bug #2002630 the glob() call compiles a regex and so we must be prepared
-			# for an exception from that because the shortname may contain regex characters
-			
-			# TODO: a more robust solution would be an escape() method for re
-			
-			self.__log.debug("find_siblings: %s" % e)
-		return siblings
-	
-	def relativize(self, base, allow_up_level=False):
-		"""
-		Relativize the path of this File against a base directory. That means that e.g.
-		File("/home/user/doc.tex").relativize("/home") == "user/doc.tex"
-		
-		If up-level references are NOT allowed but necessary (e.g. base='/a/b/c', path='/a/b/d') 
-		then the absolute path is returned.
-		
-		@param base: the base directory to relativize against
-		@param allow_up_level: allow up-level references (../../) or not
-		"""
-		if allow_up_level:
-			# TODO: os.path.relpath from Python 2.6 does the job
-			
-			return relpath(base, self.path)
-		else:
-			# TODO: why do we need this?
-			
-			# relative path must be 'below' base path
-			if len(base) >= len(self.path):
-				return self.path
-			if self.path[:len(base)] == base:
-				# bases match, return relative part
-				return self.path[len(base)+1:]
-			return self.path
-	
-	def relativize_shortname(self, base):
-		"""
-		Relativize the path of this File and return only the shortname of the resulting
-		relative path. That means that e.g.
-		File("/home/user/doc.tex").relativize_shortname("/home") == "user/doc"
-		
-		This is just a convenience method.
-		
-		@param base: the base directory to relativize against
-		"""
-		relative_path = self.relativize(base)
-		return os.path.splitext(relative_path)[0]
-	
-	def delete(self):
-		"""
-		Delete the File from the file system
-		
-		@raise OSError: 
-		"""
-		if self.exists:
-			remove(self.path)
-		else:
-			raise IOError("File not found")
-	
-	def __eq__(self, other):
-		"""
-		Override == operator
-		"""
-		try:
-			return self.uri == other.uri
-		except AttributeError:		# no File object passed or None
-			# returning NotImplemented is bad because we have to
-			# compare None with File
-			return False
-	
-	def __ne__(self, other):
-		"""
-		Override != operator
-		"""
-		return not self.__eq__(other)
-	
-	def __str__(self):
-		return self.uri
-	
-	def __cmp__(self, other):
-		try:
-			return self.basename.__cmp__(other.basename)
-		except AttributeError:		# no File object passed or None
-			# returning NotImplemented is bad because we have to
-			# compare None with File
-			return False
+    """
+    This is an object-oriented wrapper for all the os.* stuff. A File object
+    represents the reference to a file.
+    """
+
+    # TODO: use Gio.File as underlying implementation
+
+    @staticmethod
+    def create_from_relative_path(relative_path, working_directory):
+        """
+        Create a File from a path relative to some working directory.
+
+        File.create_from_relative_path('../sub/file.txt', '/home/michael/base') == File('/home/michael/sub/file.txt')
+
+        @param relative_path: a relative path, e.g. '../../dir/myfile.txt'
+        @param working_directory: an absolute directory to be used as the starting point for the relative path
+        """
+        absolute_path = os.path.abspath(os.path.join(working_directory, relative_path))
+        return File(absolute_path)
+
+    @staticmethod
+    def is_absolute(path):
+        return os.path.isabs(path)
+
+    __log = getLogger("File")
+
+    _DEFAULT_SCHEME = "file://"
+
+    def __init__(self, uri):
+        """
+        @param uri: any URI, URL or local filename
+        """
+        if uri is None:
+            raise ValueError("URI must not be None")
+
+        self._uri = urlparse.urlparse(uri)
+        if len(self._uri.scheme) == 0:
+            # prepend default scheme if missing
+            self._uri = urlparse.urlparse("%s%s" % (self._DEFAULT_SCHEME, uri))
+
+    def create(self, content=None):
+        """
+        Create a the File in the file system
+        """
+        f = open(self.path, "w")
+        if content is not None:
+            f.write(content)
+        f.close()
+
+    @property
+    def path(self):
+        """
+        Returns '/home/user/image.jpg' for 'file:///home/user/image.jpg'
+        """
+        return urllib.url2pathname(self._uri.path)
+
+    @property
+    def extension(self):
+        """
+        Returns '.jpg' for 'file:///home/user/image.jpg'
+        """
+        return os.path.splitext(self.path)[1]
+
+    @property
+    def shortname(self):
+        """
+        Returns '/home/user/image' for 'file:///home/user/image.jpg'
+        """
+        return os.path.splitext(self.path)[0]
+
+    @property
+    def basename(self):
+        """
+        Returns 'image.jpg' for 'file:///home/user/image.jpg'
+        """
+        return os.path.basename(self.path)
+
+    @property
+    def shortbasename(self):
+        """
+        Returns 'image' for 'file:///home/user/image.jpg'
+        """
+        return os.path.splitext(os.path.basename(self.path))[0]
+
+    @property
+    def dirname(self):
+        """
+        Returns '/home/user' for 'file:///home/user/image.jpg'
+        """
+        return os.path.dirname(self.path)
+
+    @property
+    def uri(self):
+        # TODO: urllib.quote doesn't support utf-8
+        return fixurl(self._uri.geturl())
+
+    @property
+    def exists(self):
+        return os.path.exists(self.path)
+
+    @property
+    def mtime(self):
+        if self.exists:
+            return os.path.getmtime(self.path)
+        else:
+            raise IOError("File not found")
+
+    def find_neighbors(self, extension):
+        """
+        Find other files in the directory of this one having
+        a certain extension
+
+        @param extension: a file extension pattern like '.tex' or '.*'
+        """
+
+        # TODO: glob is quite expensive, find a simpler way for this
+
+        try:
+            filenames = glob("%s/*%s" % (self.dirname, extension))
+            neighbors = [File(filename) for filename in filenames]
+            return neighbors
+
+        except Exception, e:
+            # as seen in Bug #2002630 the glob() call compiles a regex and so we must be prepared
+            # for an exception from that because the shortname may contain regex characters
+
+            # TODO: a more robust solution would be an escape() method for re
+
+            self.__log.debug("find_neighbors: %s" % e)
+
+            return []
+
+    @property
+    def siblings(self):
+        """
+        Find other files in the directory of this one having the same
+        basename. This means for a file '/dir/a.doc' this method returns
+        [ '/dir/a.tmp', '/dir/a.sh' ]
+        """
+        siblings = []
+        try:
+            filenames = glob("%s.*" % self.shortname)
+            siblings = [File(filename) for filename in filenames]
+        except Exception, e:
+            # as seen in Bug #2002630 the glob() call compiles a regex and so we must be prepared
+            # for an exception from that because the shortname may contain regex characters
+
+            # TODO: a more robust solution would be an escape() method for re
+
+            self.__log.debug("find_siblings: %s" % e)
+        return siblings
+
+    def relativize(self, base, allow_up_level=False):
+        """
+        Relativize the path of this File against a base directory. That means that e.g.
+        File("/home/user/doc.tex").relativize("/home") == "user/doc.tex"
+
+        If up-level references are NOT allowed but necessary (e.g. base='/a/b/c', path='/a/b/d')
+        then the absolute path is returned.
+
+        @param base: the base directory to relativize against
+        @param allow_up_level: allow up-level references (../../) or not
+        """
+        if allow_up_level:
+            # TODO: os.path.relpath from Python 2.6 does the job
+
+            return relpath(base, self.path)
+        else:
+            # TODO: why do we need this?
+
+            # relative path must be 'below' base path
+            if len(base) >= len(self.path):
+                return self.path
+            if self.path[:len(base)] == base:
+                # bases match, return relative part
+                return self.path[len(base)+1:]
+            return self.path
+
+    def relativize_shortname(self, base):
+        """
+        Relativize the path of this File and return only the shortname of the resulting
+        relative path. That means that e.g.
+        File("/home/user/doc.tex").relativize_shortname("/home") == "user/doc"
+
+        This is just a convenience method.
+
+        @param base: the base directory to relativize against
+        """
+        relative_path = self.relativize(base)
+        return os.path.splitext(relative_path)[0]
+
+    def delete(self):
+        """
+        Delete the File from the file system
+
+        @raise OSError:
+        """
+        if self.exists:
+            remove(self.path)
+        else:
+            raise IOError("File not found")
+
+    def __eq__(self, other):
+        """
+        Override == operator
+        """
+        try:
+            return self.uri == other.uri
+        except AttributeError:        # no File object passed or None
+            # returning NotImplemented is bad because we have to
+            # compare None with File
+            return False
+
+    def __ne__(self, other):
+        """
+        Override != operator
+        """
+        return not self.__eq__(other)
+
+    def __str__(self):
+        return self.uri
+
+    def __cmp__(self, other):
+        try:
+            return self.basename.__cmp__(other.basename)
+        except AttributeError:        # no File object passed or None
+            # returning NotImplemented is bad because we have to
+            # compare None with File
+            return False
 
 class Folder(File):
-	
-	# FIXME: a Folder is NOT a subclass of a File, both are a subclass of some AbstractFileSystemObject,
-	# this is just a quick hack
-	#
-	# FIXME: but basically a Folder is a File so this class should not be needed 
-	
-	__log = getLogger("Folder")
-	
-	@property
-	def files(self):
-		"""
-		Return File objects for all files in this Folder
-		"""
-		try:
-			filenames = glob("%s/*" % (self.path))
-			files = [File(filename) for filename in filenames]
-			return files
-		
-		except Exception, e:
-			# as seen in Bug #2002630 the glob() call compiles a regex and so we must be prepared
-			# for an exception from that because the shortname may contain regex characters
-			
-			# TODO: a more robust solution would be an escape() method for re
-			
-			self.__log.debug("files: %s" % e)
-			
-			return []
-		
-		
-		
+
+    # FIXME: a Folder is NOT a subclass of a File, both are a subclass of some AbstractFileSystemObject,
+    # this is just a quick hack
+    #
+    # FIXME: but basically a Folder is a File so this class should not be needed
+
+    __log = getLogger("Folder")
+
+    @property
+    def files(self):
+        """
+        Return File objects for all files in this Folder
+        """
+        try:
+            filenames = glob("%s/*" % (self.path))
+            files = [File(filename) for filename in filenames]
+            return files
+
+        except Exception, e:
+            # as seen in Bug #2002630 the glob() call compiles a regex and so we must be prepared
+            # for an exception from that because the shortname may contain regex characters
+
+            # TODO: a more robust solution would be an escape() method for re
+
+            self.__log.debug("files: %s" % e)
+
+            return []
diff --git a/latex/base/completion.py b/latex/base/completion.py
index 7398820..474964c 100644
--- a/latex/base/completion.py
+++ b/latex/base/completion.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -29,550 +29,550 @@ from ..preferences import Preferences
 
 
 class ProposalPopup(Gtk.Window):
-	"""
-	Popup showing a list of proposals. This is implemented as a singleton
-	as it doesn't make sense to have multiple popups around.
-	"""
-	
-	_log = getLogger("ProposalPopup")
-	
-	_POPUP_WIDTH = 300
-	_POPUP_HEIGHT = 200
-	_SPACE = 0
-	
-	def __new__(cls):
-		if not '_instance' in cls.__dict__:
-			cls._instance = Gtk.Window.__new__(cls)
-		return cls._instance
-	
-	def __init__(self):
-		if not '_ready' in dir(self):
-			Gtk.Window.__init__(self,type=Gtk.WindowType.POPUP)
-			#self, Gtk.WindowType.POPUP)
-			
-			self._store = Gtk.ListStore(str, object, GdkPixbuf.Pixbuf)		# markup, Proposal instance
-			
-			self._view = Gtk.TreeView(model=self._store)
-			
-			# pack the icon and text cells in one column to avoid the column separator
-			column = Gtk.TreeViewColumn()
-			pixbuf_renderer = Gtk.CellRendererPixbuf()
-			column.pack_start(pixbuf_renderer, False)
-			column.add_attribute(pixbuf_renderer, "pixbuf", 2)
-		
-			text_renderer = Gtk.CellRendererText()
-			column.pack_start(text_renderer, True)
-			column.add_attribute(text_renderer, "markup", 0)
-		
-			self._view.append_column(column)
-			
-#			self._view.insert_column_with_attributes(-1, "", Gtk.CellRendererPixbuf(), pixbuf=2)
-#			self._view.insert_column_with_attributes(-1, "", Gtk.CellRendererText(), markup=0)
-
-			self._view.set_enable_search(False)
-			self._view.set_headers_visible(False)
-			
-			scr = Gtk.ScrolledWindow()
-			scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
-			scr.add(self._view)
-			scr.set_size_request(self._POPUP_WIDTH, self._POPUP_HEIGHT)
-			
-			frame = Gtk.Frame()
-			frame.set_shadow_type(Gtk.ShadowType.OUT)
-			frame.add(scr)
-			
-			self.add(frame)
-			
-			self._details_popup = DetailsPopup()
-			
-			self._ready = True
-	
-	@property
-	def selected_proposal(self):	
-		"""
-		Returns the currently selected proposal
-		"""
-		store, it = self._view.get_selection().get_selected()
-		return store.get_value(it, 1)
-	
-	def activate(self, proposals, text_view):
-		"""
-		Load proposals, move to the cursor position and show
-		"""
-		self._set_proposals(proposals)
-		self._move_to_cursor(text_view)
-		
-		self.show_all()
-		
-		self._update_details_popup()
-	
-	def deactivate(self):
-		"""
-		Hide this popup and the DetailsPopup
-		"""
-		self._details_popup.deactivate()
-		self.hide()
-	
-	def _set_proposals(self, proposals):
-		"""
-		Loads proposals into the popup
-		"""
-		# sort
-		proposals.sort()
-		
-		# load
-		self._store.clear()
-		for proposal in proposals:
-			self._store.append([proposal.label, proposal, proposal.icon])
-			
-		self._view.set_cursor(Gtk.TreePath.new_from_string("0"),None, False)
-		
-	def navigate(self, key):
-		"""
-		Moves the selection in the view according to key
-		"""
-		if key == "Up":
-			d = -1
-		elif key == "Down":
-			d = 1
-		elif key == "Page_Up":
-			d = -5
-		elif key == "Page_Down":
-			d = 5
-		else:
-			return
-		
-		path = self._view.get_cursor()[0]
-		index = path[0]
-		max = self._store.iter_n_children(None)
-		
-		index += d
-		
-		if index < 0:
-			index = 0
-		elif index >= max:
-			index = max - 1
-			
-		self._view.set_cursor(index)
-		
-		self._update_details_popup()
-	
-	def _get_cursor_pos(self, text_view):
-		"""
-		Retrieve the current absolute position of the cursor in a TextView
-		"""
-		buffer = text_view.get_buffer()
-		location = text_view.get_iter_location(buffer.get_iter_at_mark(buffer.get_insert()))
-		
-		winX, winY = text_view.buffer_to_window_coords(Gtk.TextWindowType.WIDGET, location.x, location.y)
-		
-		win = text_view.get_window(Gtk.TextWindowType.WIDGET)
-		ignore, xx, yy = win.get_origin()
-		
-		x = winX + xx
-		y = winY + yy + location.height + self._SPACE
-		
-		return (x, y)
-	
-	def _update_details_popup(self):
-		"""
-		Move and show the DetailsPopup if the currently selected proposal
-		contains details.
-		"""
-		try:
-			index = self._view.get_cursor()[0][0]
-			proposal = self._store[index][1]
-			
-#			self._log.debug("proposal.details: " + str(proposal.details))
-			
-			if proposal.details is None:
-				self._details_popup.hide()
-				return
-			
-			# move
-			x, y = self.get_position()
-			width = self.get_size()[0]
-			path, column = self._view.get_cursor()
-			rect = self._view.get_cell_area(path, column)
-			
-			self._details_popup.move(x + width + 2, y + rect.y)
-			
-			# activate
-			self._details_popup.activate(proposal.details)
-			
-		except Exception, e:
-			self._log.error(e)
-		
-	def _move_to_cursor(self, text_view):
-		"""
-		Move the popup to the current location of the cursor
-		"""
-		sw = Gdk.Screen.width()
-		sh = Gdk.Screen.height()
-		
-		x, y = self._get_cursor_pos(text_view)
-		
-		w, h = self.get_size()
-		
-		if x + w > sw:
-			x = sw - w - 4
-		
-		if y + h > sh:
-			# get the height of a character
-			layout = text_view.create_pango_layout("a")
-			ytext = layout.get_pixel_size()[1]
-			y = y - ytext - h
-		
-		self.move(x, y)
+    """
+    Popup showing a list of proposals. This is implemented as a singleton
+    as it doesn't make sense to have multiple popups around.
+    """
+
+    _log = getLogger("ProposalPopup")
+
+    _POPUP_WIDTH = 300
+    _POPUP_HEIGHT = 200
+    _SPACE = 0
+
+    def __new__(cls):
+        if not '_instance' in cls.__dict__:
+            cls._instance = Gtk.Window.__new__(cls)
+        return cls._instance
+
+    def __init__(self):
+        if not '_ready' in dir(self):
+            Gtk.Window.__init__(self,type=Gtk.WindowType.POPUP)
+            #self, Gtk.WindowType.POPUP)
+
+            self._store = Gtk.ListStore(str, object, GdkPixbuf.Pixbuf)        # markup, Proposal instance
+
+            self._view = Gtk.TreeView(model=self._store)
+
+            # pack the icon and text cells in one column to avoid the column separator
+            column = Gtk.TreeViewColumn()
+            pixbuf_renderer = Gtk.CellRendererPixbuf()
+            column.pack_start(pixbuf_renderer, False)
+            column.add_attribute(pixbuf_renderer, "pixbuf", 2)
+
+            text_renderer = Gtk.CellRendererText()
+            column.pack_start(text_renderer, True)
+            column.add_attribute(text_renderer, "markup", 0)
+
+            self._view.append_column(column)
+
+#            self._view.insert_column_with_attributes(-1, "", Gtk.CellRendererPixbuf(), pixbuf=2)
+#            self._view.insert_column_with_attributes(-1, "", Gtk.CellRendererText(), markup=0)
+
+            self._view.set_enable_search(False)
+            self._view.set_headers_visible(False)
+
+            scr = Gtk.ScrolledWindow()
+            scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+            scr.add(self._view)
+            scr.set_size_request(self._POPUP_WIDTH, self._POPUP_HEIGHT)
+
+            frame = Gtk.Frame()
+            frame.set_shadow_type(Gtk.ShadowType.OUT)
+            frame.add(scr)
+
+            self.add(frame)
+
+            self._details_popup = DetailsPopup()
+
+            self._ready = True
+
+    @property
+    def selected_proposal(self):
+        """
+        Returns the currently selected proposal
+        """
+        store, it = self._view.get_selection().get_selected()
+        return store.get_value(it, 1)
+
+    def activate(self, proposals, text_view):
+        """
+        Load proposals, move to the cursor position and show
+        """
+        self._set_proposals(proposals)
+        self._move_to_cursor(text_view)
+
+        self.show_all()
+
+        self._update_details_popup()
+
+    def deactivate(self):
+        """
+        Hide this popup and the DetailsPopup
+        """
+        self._details_popup.deactivate()
+        self.hide()
+
+    def _set_proposals(self, proposals):
+        """
+        Loads proposals into the popup
+        """
+        # sort
+        proposals.sort()
+
+        # load
+        self._store.clear()
+        for proposal in proposals:
+            self._store.append([proposal.label, proposal, proposal.icon])
+
+        self._view.set_cursor(Gtk.TreePath.new_from_string("0"),None, False)
+
+    def navigate(self, key):
+        """
+        Moves the selection in the view according to key
+        """
+        if key == "Up":
+            d = -1
+        elif key == "Down":
+            d = 1
+        elif key == "Page_Up":
+            d = -5
+        elif key == "Page_Down":
+            d = 5
+        else:
+            return
+
+        path = self._view.get_cursor()[0]
+        index = path[0]
+        max = self._store.iter_n_children(None)
+
+        index += d
+
+        if index < 0:
+            index = 0
+        elif index >= max:
+            index = max - 1
+
+        self._view.set_cursor(index)
+
+        self._update_details_popup()
+
+    def _get_cursor_pos(self, text_view):
+        """
+        Retrieve the current absolute position of the cursor in a TextView
+        """
+        buffer = text_view.get_buffer()
+        location = text_view.get_iter_location(buffer.get_iter_at_mark(buffer.get_insert()))
+
+        winX, winY = text_view.buffer_to_window_coords(Gtk.TextWindowType.WIDGET, location.x, location.y)
+
+        win = text_view.get_window(Gtk.TextWindowType.WIDGET)
+        ignore, xx, yy = win.get_origin()
+
+        x = winX + xx
+        y = winY + yy + location.height + self._SPACE
+
+        return (x, y)
+
+    def _update_details_popup(self):
+        """
+        Move and show the DetailsPopup if the currently selected proposal
+        contains details.
+        """
+        try:
+            index = self._view.get_cursor()[0][0]
+            proposal = self._store[index][1]
+
+#            self._log.debug("proposal.details: " + str(proposal.details))
+
+            if proposal.details is None:
+                self._details_popup.hide()
+                return
+
+            # move
+            x, y = self.get_position()
+            width = self.get_size()[0]
+            path, column = self._view.get_cursor()
+            rect = self._view.get_cell_area(path, column)
+
+            self._details_popup.move(x + width + 2, y + rect.y)
+
+            # activate
+            self._details_popup.activate(proposal.details)
+
+        except Exception, e:
+            self._log.error(e)
+
+    def _move_to_cursor(self, text_view):
+        """
+        Move the popup to the current location of the cursor
+        """
+        sw = Gdk.Screen.width()
+        sh = Gdk.Screen.height()
+
+        x, y = self._get_cursor_pos(text_view)
+
+        w, h = self.get_size()
+
+        if x + w > sw:
+            x = sw - w - 4
+
+        if y + h > sh:
+            # get the height of a character
+            layout = text_view.create_pango_layout("a")
+            ytext = layout.get_pixel_size()[1]
+            y = y - ytext - h
+
+        self.move(x, y)
 
 
 class DetailsPopup(Gtk.Window):
-	"""
-	A popup showing additional information at the right of the currently 
-	selected proposal in the ProposalPopup.
-	
-	This is used to display details of a BibTeX entry or the result of
-	a template. 
-	"""
-	
-	def __init__(self):
-		Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
-		
-		self._color = Preferences().get("light-foreground-color")
-		
-		self._label = Gtk.Label()
-		self._label.set_use_markup(True)
-		self._label.set_alignment(0, .5)
-		
-		self._frame = Gtk.Frame()
-		self._frame.set_shadow_type(Gtk.ShadowType.OUT)
-		#self._frame.set_border_width(3)
-		self._frame.add(self._label)
-		
-		self.add(self._frame)
-		
-	def activate(self, details):
-		"""
-		Create widget(s) for the given details and show the popup
-		"""
-		# remove the old child widget if present
-		child = self._frame.get_child()
-		if child:
-			self._frame.remove(child)
-			child.destroy()
-		
-		# create a child widget depending on the type of details
-		if type(details) is list:
-			# table data
-			table = Gtk.Table()
-			table.set_border_width(5)
-			table.set_col_spacings(5)
-			rc = 0
-			for row in details:
-				cc = 0
-				for column in row:
-					if cc == 0:
-						# first column
-						label = Gtk.Label("<span color='%s'>%s</span>" % (self._color, column))
-					else:
-						label = Gtk.Label(label=column)
-					label.set_use_markup(True)
-					if cc == 0:
-						# 1st column is right aligned
-						label.set_alignment(1.0, 0.5)
-					else:
-						# others are left aligned
-						label.set_alignment(0.0, 0.5)
-					table.attach(label, cc, cc + 1, rc, rc + 1)
-					cc += 1
-				rc += 1
-			self._frame.add(table)
-		
-		else:
-			# markup text
-			label = Gtk.Label(label=details)
-			label.set_use_markup(True)
-			self._frame.add(label)
-		
-		self.show_all()
-		
-		# force a recompute of the window size
-		self.resize(1, 1)
-		
-	def deactivate(self):
-		"""
-		Hide the popup
-		"""
-		self.hide()
+    """
+    A popup showing additional information at the right of the currently
+    selected proposal in the ProposalPopup.
+
+    This is used to display details of a BibTeX entry or the result of
+    a template.
+    """
+
+    def __init__(self):
+        Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
+
+        self._color = Preferences().get("light-foreground-color")
+
+        self._label = Gtk.Label()
+        self._label.set_use_markup(True)
+        self._label.set_alignment(0, .5)
+
+        self._frame = Gtk.Frame()
+        self._frame.set_shadow_type(Gtk.ShadowType.OUT)
+        #self._frame.set_border_width(3)
+        self._frame.add(self._label)
+
+        self.add(self._frame)
+
+    def activate(self, details):
+        """
+        Create widget(s) for the given details and show the popup
+        """
+        # remove the old child widget if present
+        child = self._frame.get_child()
+        if child:
+            self._frame.remove(child)
+            child.destroy()
+
+        # create a child widget depending on the type of details
+        if type(details) is list:
+            # table data
+            table = Gtk.Table()
+            table.set_border_width(5)
+            table.set_col_spacings(5)
+            rc = 0
+            for row in details:
+                cc = 0
+                for column in row:
+                    if cc == 0:
+                        # first column
+                        label = Gtk.Label("<span color='%s'>%s</span>" % (self._color, column))
+                    else:
+                        label = Gtk.Label(label=column)
+                    label.set_use_markup(True)
+                    if cc == 0:
+                        # 1st column is right aligned
+                        label.set_alignment(1.0, 0.5)
+                    else:
+                        # others are left aligned
+                        label.set_alignment(0.0, 0.5)
+                    table.attach(label, cc, cc + 1, rc, rc + 1)
+                    cc += 1
+                rc += 1
+            self._frame.add(table)
+
+        else:
+            # markup text
+            label = Gtk.Label(label=details)
+            label.set_use_markup(True)
+            self._frame.add(label)
+
+        self.show_all()
+
+        # force a recompute of the window size
+        self.resize(1, 1)
+
+    def deactivate(self):
+        """
+        Hide the popup
+        """
+        self.hide()
 
 
 class CompletionDistributor(object):
-	"""
-	This forms the lower end of the completion mechanism and hosts one 
-	or more CompletionHandlers
-	"""
-	
-	
-	# TODO: clearify and simplify states here!
-	# TODO: auto-close (maybe...)
-	
-	
-	_log = getLogger("CompletionDistributor")
-	
-	_MAX_PREFIX_LENGTH = 100
-	
-	# completion delay in ms
-	_DELAY = 500
-	
-	_STATE_IDLE, _STATE_CTRL_PRESSED, _STATE_ACTIVE = 0, 1, 2
-	
-	# keys that abort completion
-	_ABORT_KEYS = [ "Escape", "Left", "Right", "Home", "End", "space", "Tab" ]
-	
-	# keys that are used to navigate in the popup
-	_NAVIGATION_KEYS = [ "Up", "Down", "Page_Up", "Page_Down" ]
-	
-	# some characters have key constants that differ from their value
-	_SPECIAL_KEYS = {"@" : "at"}
-	
-	def __init__(self, editor, handlers):
-		"""
-		@param editor: the instance of Editor this CompletionDistributor should observe
-		@param handlers: a list of ICompletionHandler instances
-		"""
-		
-		self._log.debug("init")
-		
-		self._handlers = handlers		# we already get objects
-		
-		self._editor = editor
-		self._text_buffer = editor.tab_decorator.tab.get_document()
-		self._text_view = editor.tab_decorator.tab.get_view()
-		
-		self._state = self._STATE_IDLE
-		self._timer = None
-		
-		# collect trigger keys from all handlers
-		self._trigger_keys = []
-		for handler in self._handlers:
-			for key in handler.trigger_keys:
-				if key in self._SPECIAL_KEYS.keys():
-					self._trigger_keys.append(self._SPECIAL_KEYS[key])
-				else:
-					self._trigger_keys.append(key)
-		
-		# TODO: is it right to instatiate this here?
-		self._popup = ProposalPopup()
-		
-		# connect to signals
-		self._signal_handlers = [
-				self._text_view.connect("key-press-event", self._on_key_pressed),
-				self._text_view.connect_after("key-release-event", self._on_key_released),
-				self._text_view.connect("button-press-event", self._on_button_pressed),
-				self._text_view.connect("focus-out-event", self._on_focus_out)
-		]
-	
-	def _on_key_pressed(self, view, event):
-		"""
-		"""
-		key = Gdk.keyval_name(event.keyval)
-		
-		if self._state == self._STATE_IDLE:
-			if key == "Control_L" or key == "Control_R":
-				self._state = self._STATE_CTRL_PRESSED
-				
-			elif key in self._trigger_keys:
-				self._start_timer()
-				return False
-
-		elif self._state == self._STATE_ACTIVE:
-			if key == "Return":
-				# select proposal
-				
-				self._abort()
-				
-				proposal = self._popup.selected_proposal
-				self._select_proposal(proposal)
-				
-				# TODO: self._autoClose(proposalSelected=True)
-				
-				# returning True stops the signal
-				return True
-			
-			elif key in self._ABORT_KEYS:
-				self._abort()
-				
-			elif key in self._NAVIGATION_KEYS:
-				self._popup.navigate(key)
-				
-				# returning True stops the signal
-				return True
-		
-		elif self._state == self._STATE_CTRL_PRESSED:
-			if key == "space":
-				self._state = self._STATE_IDLE
-				self._complete()
-				return True
-			else:
-				self._state = self._STATE_IDLE
-		
-		self._stop_timer()
-	
-	def _on_key_released(self, view, event):
-		"""
-		"""
-		key = Gdk.keyval_name(event.keyval)
-		
-#		# trigger auto close on "}"
-#		if key == "braceright":
-#			# TODO: self._autoClose(braceTyped=True)
-#			pass
-		
-		
-		if self._state == self._STATE_ACTIVE:
-			if key in self._NAVIGATION_KEYS or key in ["Control_L", "Control_R", "space"]:
-				# returning True stops the signal
-				return True
-			else:
-				# user is typing on with active popup...
-				
-				# TODO: we should check here if the cursor has moved
-				# or better if the buffer has changed
-				
-				self._complete()
-	
-	def _on_button_pressed(self, view, event):
-		self._abort()
-	
-	def _on_focus_out(self, view, event):
-		self._abort()
-	
-	def _start_timer(self):
-		"""
-		Start a timer for completion
-		"""
-		# stop eventually running timer
-		self._stop_timer()
-		# start timer
-		self._timer = GObject.timeout_add(self._DELAY, self._timer_callback)
-	
-	def _stop_timer(self):
-		"""
-		Stop the timer if it has been started
-		"""
-		if self._timer is not None:
-			GObject.source_remove(self._timer)
-			self._timer = None
-		
-	def _timer_callback(self):
-		"""
-		Timeout
-		"""
-		self._complete()
-		
-		return False	# do not call again
-	
-	def _complete(self):
-		all_proposals = []
-		
-		for handler in self._handlers:
-			delimiters = handler.prefix_delimiters
-			
-			prefix = self._find_prefix(delimiters)
-			if prefix:
-#				if handler.strip_delimiter:
-#					prefix = prefix[1:]
-				
-				proposals = handler.complete(prefix)
-				assert type(proposals) is list
-				
-				all_proposals.extend(proposals)
-			
-		if len(all_proposals):
-			self._popup.activate(all_proposals, self._text_view)
-			self._state = self._STATE_ACTIVE
-		else:
-			self._abort()
-	
-	def _find_prefix(self, delimiters):
-		"""
-		Find the start of the surrounding command and return the text
-		from there to the cursor position.
-		
-		This is the prefix forming the basis for LaTeX completion.
-		"""
-		it_right = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
-		it_left = it_right.copy()
-		
-		# go back by one character (insert iter points to the char at the right of
-		# the cursor!)
-		if not it_left.backward_char():
-			self._log.debug("_find_prefix: start of buffer reached")
-			return None
-		
-		# move left until 'the left-most of a sequence of delimiters'
-		#
-		# e.g. if 'x' is a delimiter and 'abcxxx' is at left of the cursor, we 
-		# recognize 'xxx' as the prefix instead of only 'x'
-		delim_found = False
-		delim_char = None
-		
-		i = 0
-		while i < self._MAX_PREFIX_LENGTH:
-			c = it_left.get_char()
-			
-			if delim_found:
-				if c != delim_char:
-					# a delimiter has been found and the preceding character 
-					# is different from it
-					break
-			else:
-				if c in delimiters:
-					# a delimiter has been found
-					delim_found = True
-					delim_char = c
-			
-			if not it_left.backward_char():
-				self._log.debug("_find_prefix: start of buffer reached")
-				return None
-			
-			i += 1
-		
-		# to recognize the left-most delimiter, we have moved one char
-		# too much
-		it_left.forward_char()
-		
-		if i == self._MAX_PREFIX_LENGTH:
-			self._log.debug("_find_prefix: prefix too long")
-			return None
-		
-		prefix = self._text_buffer.get_text(it_left, it_right, False)
-		
-		return prefix
-	
-	def _select_proposal(self, proposal):
-		"""
-		Insert the source contained in the activated proposal
-		"""
-		self._editor.delete_at_cursor(- proposal.overlap)
-		self._editor.insert(proposal.source)
-	
-	def _abort(self):
-		"""
-		Abort completion
-		"""
-		if self._state == self._STATE_ACTIVE:
-			self._popup.deactivate()
-			self._state = self._STATE_IDLE
-	
-	def destroy(self):
-		# unreference the editor (very important! cyclic reference)
-		del self._editor
-		
-		# disconnect text view events
-		for handler in self._signal_handlers:
-			self._text_view.disconnect(handler)
-	
-	def __del__(self):
-		self._log.debug("Properly destroyed %s" % self)
-	
+    """
+    This forms the lower end of the completion mechanism and hosts one
+    or more CompletionHandlers
+    """
+
+
+    # TODO: clearify and simplify states here!
+    # TODO: auto-close (maybe...)
+
+
+    _log = getLogger("CompletionDistributor")
+
+    _MAX_PREFIX_LENGTH = 100
+
+    # completion delay in ms
+    _DELAY = 500
+
+    _STATE_IDLE, _STATE_CTRL_PRESSED, _STATE_ACTIVE = 0, 1, 2
+
+    # keys that abort completion
+    _ABORT_KEYS = [ "Escape", "Left", "Right", "Home", "End", "space", "Tab" ]
+
+    # keys that are used to navigate in the popup
+    _NAVIGATION_KEYS = [ "Up", "Down", "Page_Up", "Page_Down" ]
+
+    # some characters have key constants that differ from their value
+    _SPECIAL_KEYS = {"@" : "at"}
+
+    def __init__(self, editor, handlers):
+        """
+        @param editor: the instance of Editor this CompletionDistributor should observe
+        @param handlers: a list of ICompletionHandler instances
+        """
+
+        self._log.debug("init")
+
+        self._handlers = handlers        # we already get objects
+
+        self._editor = editor
+        self._text_buffer = editor.tab_decorator.tab.get_document()
+        self._text_view = editor.tab_decorator.tab.get_view()
+
+        self._state = self._STATE_IDLE
+        self._timer = None
+
+        # collect trigger keys from all handlers
+        self._trigger_keys = []
+        for handler in self._handlers:
+            for key in handler.trigger_keys:
+                if key in self._SPECIAL_KEYS.keys():
+                    self._trigger_keys.append(self._SPECIAL_KEYS[key])
+                else:
+                    self._trigger_keys.append(key)
+
+        # TODO: is it right to instatiate this here?
+        self._popup = ProposalPopup()
+
+        # connect to signals
+        self._signal_handlers = [
+                self._text_view.connect("key-press-event", self._on_key_pressed),
+                self._text_view.connect_after("key-release-event", self._on_key_released),
+                self._text_view.connect("button-press-event", self._on_button_pressed),
+                self._text_view.connect("focus-out-event", self._on_focus_out)
+        ]
+
+    def _on_key_pressed(self, view, event):
+        """
+        """
+        key = Gdk.keyval_name(event.keyval)
+
+        if self._state == self._STATE_IDLE:
+            if key == "Control_L" or key == "Control_R":
+                self._state = self._STATE_CTRL_PRESSED
+
+            elif key in self._trigger_keys:
+                self._start_timer()
+                return False
+
+        elif self._state == self._STATE_ACTIVE:
+            if key == "Return":
+                # select proposal
+
+                self._abort()
+
+                proposal = self._popup.selected_proposal
+                self._select_proposal(proposal)
+
+                # TODO: self._autoClose(proposalSelected=True)
+
+                # returning True stops the signal
+                return True
+
+            elif key in self._ABORT_KEYS:
+                self._abort()
+
+            elif key in self._NAVIGATION_KEYS:
+                self._popup.navigate(key)
+
+                # returning True stops the signal
+                return True
+
+        elif self._state == self._STATE_CTRL_PRESSED:
+            if key == "space":
+                self._state = self._STATE_IDLE
+                self._complete()
+                return True
+            else:
+                self._state = self._STATE_IDLE
+
+        self._stop_timer()
+
+    def _on_key_released(self, view, event):
+        """
+        """
+        key = Gdk.keyval_name(event.keyval)
+
+#        # trigger auto close on "}"
+#        if key == "braceright":
+#            # TODO: self._autoClose(braceTyped=True)
+#            pass
+
+
+        if self._state == self._STATE_ACTIVE:
+            if key in self._NAVIGATION_KEYS or key in ["Control_L", "Control_R", "space"]:
+                # returning True stops the signal
+                return True
+            else:
+                # user is typing on with active popup...
+
+                # TODO: we should check here if the cursor has moved
+                # or better if the buffer has changed
+
+                self._complete()
+
+    def _on_button_pressed(self, view, event):
+        self._abort()
+
+    def _on_focus_out(self, view, event):
+        self._abort()
+
+    def _start_timer(self):
+        """
+        Start a timer for completion
+        """
+        # stop eventually running timer
+        self._stop_timer()
+        # start timer
+        self._timer = GObject.timeout_add(self._DELAY, self._timer_callback)
+
+    def _stop_timer(self):
+        """
+        Stop the timer if it has been started
+        """
+        if self._timer is not None:
+            GObject.source_remove(self._timer)
+            self._timer = None
+
+    def _timer_callback(self):
+        """
+        Timeout
+        """
+        self._complete()
+
+        return False    # do not call again
+
+    def _complete(self):
+        all_proposals = []
+
+        for handler in self._handlers:
+            delimiters = handler.prefix_delimiters
+
+            prefix = self._find_prefix(delimiters)
+            if prefix:
+#                if handler.strip_delimiter:
+#                    prefix = prefix[1:]
+
+                proposals = handler.complete(prefix)
+                assert type(proposals) is list
+
+                all_proposals.extend(proposals)
+
+        if len(all_proposals):
+            self._popup.activate(all_proposals, self._text_view)
+            self._state = self._STATE_ACTIVE
+        else:
+            self._abort()
+
+    def _find_prefix(self, delimiters):
+        """
+        Find the start of the surrounding command and return the text
+        from there to the cursor position.
+
+        This is the prefix forming the basis for LaTeX completion.
+        """
+        it_right = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
+        it_left = it_right.copy()
+
+        # go back by one character (insert iter points to the char at the right of
+        # the cursor!)
+        if not it_left.backward_char():
+            self._log.debug("_find_prefix: start of buffer reached")
+            return None
+
+        # move left until 'the left-most of a sequence of delimiters'
+        #
+        # e.g. if 'x' is a delimiter and 'abcxxx' is at left of the cursor, we
+        # recognize 'xxx' as the prefix instead of only 'x'
+        delim_found = False
+        delim_char = None
+
+        i = 0
+        while i < self._MAX_PREFIX_LENGTH:
+            c = it_left.get_char()
+
+            if delim_found:
+                if c != delim_char:
+                    # a delimiter has been found and the preceding character
+                    # is different from it
+                    break
+            else:
+                if c in delimiters:
+                    # a delimiter has been found
+                    delim_found = True
+                    delim_char = c
+
+            if not it_left.backward_char():
+                self._log.debug("_find_prefix: start of buffer reached")
+                return None
+
+            i += 1
+
+        # to recognize the left-most delimiter, we have moved one char
+        # too much
+        it_left.forward_char()
+
+        if i == self._MAX_PREFIX_LENGTH:
+            self._log.debug("_find_prefix: prefix too long")
+            return None
+
+        prefix = self._text_buffer.get_text(it_left, it_right, False)
+
+        return prefix
+
+    def _select_proposal(self, proposal):
+        """
+        Insert the source contained in the activated proposal
+        """
+        self._editor.delete_at_cursor(- proposal.overlap)
+        self._editor.insert(proposal.source)
+
+    def _abort(self):
+        """
+        Abort completion
+        """
+        if self._state == self._STATE_ACTIVE:
+            self._popup.deactivate()
+            self._state = self._STATE_IDLE
+
+    def destroy(self):
+        # unreference the editor (very important! cyclic reference)
+        del self._editor
+
+        # disconnect text view events
+        for handler in self._signal_handlers:
+            self._text_view.disconnect(handler)
+
+    def __del__(self):
+        self._log.debug("Properly destroyed %s" % self)
+
diff --git a/latex/base/config.py b/latex/base/config.py
index 5db59c9..692a148 100644
--- a/latex/base/config.py
+++ b/latex/base/config.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -25,114 +25,114 @@ base.config
 # ui definition
 
 UI = """
-	<ui>
-		<menubar name="MenuBar">
-			<menu name="FileMenu" action="File">
-				<placeholder name="FileOps_1">
-					<menuitem action="LaTeXNewAction" />
-				</placeholder>
-				<placeholder name="FileOps_3">
-					<menuitem action="LaTeXSaveAsTemplateAction" />
-				</placeholder>
-			</menu>
-			<placeholder name="ExtraMenu_1">
-				<menu action="LaTeXMenuAction">
-					<menuitem action="LaTeXChooseMasterAction" />
-					<separator />
-					<menuitem action="LaTeXGraphicsAction" />
-					<menuitem action="LaTeXTableAction" />
-					<menuitem action="LaTeXListingAction" />
-					<menuitem action="LaTeXUseBibliographyAction" />
-					<separator />
-					<menuitem action="LaTeXCloseEnvironmentAction" />
-					<separator />
-					<menuitem action="LaTeXBuildImageAction" />
-				</menu>
-				<menu action="BibTeXMenuAction">
-					<menuitem action="BibTeXNewEntryAction" />
-				</menu>
-			</placeholder>
-		</menubar>
-		<toolbar name="LaTeXToolbar">
-			<toolitem action="LaTeXFontFamilyAction">
-				<menu action="LaTeXFontFamilyMenuAction">
-					<menuitem action="LaTeXBoldAction" />
-					<menuitem action="LaTeXItalicAction" />
-					<menuitem action="LaTeXEmphasizeAction" />
-					<menuitem action="LaTeXUnderlineAction" />
-					<menuitem action="LaTeXSmallCapitalsAction" />
-					<menuitem action="LaTeXRomanAction" />
-					<menuitem action="LaTeXSansSerifAction" />
-					<menuitem action="LaTeXTypewriterAction" />
-					<separator />
-					<menuitem action="LaTeXBlackboardBoldAction" />
-					<menuitem action="LaTeXCaligraphyAction" />
-					<menuitem action="LaTeXFrakturAction" />
-				</menu>
-			</toolitem>
-			<toolitem action="LaTeXJustifyLeftAction" />
-			<toolitem action="LaTeXJustifyCenterAction" />
-			<toolitem action="LaTeXJustifyRightAction" />
-			<separator />
-			<toolitem action="LaTeXItemizeAction" />
-			<toolitem action="LaTeXEnumerateAction" />
-			<toolitem action="LaTeXDescriptionAction" />
-			<separator />
-			<toolitem action="LaTeXStructureAction">
-				<menu action="LaTeXStructureMenuAction">
-					<menuitem action="LaTeXPartAction" />
-					<menuitem action="LaTeXChapterAction" />
-					<separator />
-					<menuitem action="LaTeXSectionAction" />
-					<menuitem action="LaTeXSubsectionAction" />
-					<menuitem action="LaTeXParagraphAction" />
-					<menuitem action="LaTeXSubparagraphAction" />
-				</menu>
-			</toolitem>
-			<separator />
-			<toolitem action="LaTeXMathAction">
-				<menu action="LaTeXMathMenuAction">
-					<menuitem action="LaTeXMathAction" />
-					<menuitem action="LaTeXDisplayMathAction" />
-					<menuitem action="LaTeXEquationAction" />
-					<menuitem action="LaTeXUnEqnArrayAction" />
-					<menuitem action="LaTeXEqnArrayAction" />
-				</menu>
-			</toolitem>
-			<separator />
-			<toolitem action="LaTeXGraphicsAction" />
-			<toolitem action="LaTeXTableAction" />
-			<toolitem action="LaTeXListingAction" />
-			<toolitem action="LaTeXUseBibliographyAction" />
-			<separator />
-			<toolitem action="LaTeXBuildImageAction" />
-		</toolbar>
-	</ui>"""
+    <ui>
+        <menubar name="MenuBar">
+            <menu name="FileMenu" action="File">
+                <placeholder name="FileOps_1">
+                    <menuitem action="LaTeXNewAction" />
+                </placeholder>
+                <placeholder name="FileOps_3">
+                    <menuitem action="LaTeXSaveAsTemplateAction" />
+                </placeholder>
+            </menu>
+            <placeholder name="ExtraMenu_1">
+                <menu action="LaTeXMenuAction">
+                    <menuitem action="LaTeXChooseMasterAction" />
+                    <separator />
+                    <menuitem action="LaTeXGraphicsAction" />
+                    <menuitem action="LaTeXTableAction" />
+                    <menuitem action="LaTeXListingAction" />
+                    <menuitem action="LaTeXUseBibliographyAction" />
+                    <separator />
+                    <menuitem action="LaTeXCloseEnvironmentAction" />
+                    <separator />
+                    <menuitem action="LaTeXBuildImageAction" />
+                </menu>
+                <menu action="BibTeXMenuAction">
+                    <menuitem action="BibTeXNewEntryAction" />
+                </menu>
+            </placeholder>
+        </menubar>
+        <toolbar name="LaTeXToolbar">
+            <toolitem action="LaTeXFontFamilyAction">
+                <menu action="LaTeXFontFamilyMenuAction">
+                    <menuitem action="LaTeXBoldAction" />
+                    <menuitem action="LaTeXItalicAction" />
+                    <menuitem action="LaTeXEmphasizeAction" />
+                    <menuitem action="LaTeXUnderlineAction" />
+                    <menuitem action="LaTeXSmallCapitalsAction" />
+                    <menuitem action="LaTeXRomanAction" />
+                    <menuitem action="LaTeXSansSerifAction" />
+                    <menuitem action="LaTeXTypewriterAction" />
+                    <separator />
+                    <menuitem action="LaTeXBlackboardBoldAction" />
+                    <menuitem action="LaTeXCaligraphyAction" />
+                    <menuitem action="LaTeXFrakturAction" />
+                </menu>
+            </toolitem>
+            <toolitem action="LaTeXJustifyLeftAction" />
+            <toolitem action="LaTeXJustifyCenterAction" />
+            <toolitem action="LaTeXJustifyRightAction" />
+            <separator />
+            <toolitem action="LaTeXItemizeAction" />
+            <toolitem action="LaTeXEnumerateAction" />
+            <toolitem action="LaTeXDescriptionAction" />
+            <separator />
+            <toolitem action="LaTeXStructureAction">
+                <menu action="LaTeXStructureMenuAction">
+                    <menuitem action="LaTeXPartAction" />
+                    <menuitem action="LaTeXChapterAction" />
+                    <separator />
+                    <menuitem action="LaTeXSectionAction" />
+                    <menuitem action="LaTeXSubsectionAction" />
+                    <menuitem action="LaTeXParagraphAction" />
+                    <menuitem action="LaTeXSubparagraphAction" />
+                </menu>
+            </toolitem>
+            <separator />
+            <toolitem action="LaTeXMathAction">
+                <menu action="LaTeXMathMenuAction">
+                    <menuitem action="LaTeXMathAction" />
+                    <menuitem action="LaTeXDisplayMathAction" />
+                    <menuitem action="LaTeXEquationAction" />
+                    <menuitem action="LaTeXUnEqnArrayAction" />
+                    <menuitem action="LaTeXEqnArrayAction" />
+                </menu>
+            </toolitem>
+            <separator />
+            <toolitem action="LaTeXGraphicsAction" />
+            <toolitem action="LaTeXTableAction" />
+            <toolitem action="LaTeXListingAction" />
+            <toolitem action="LaTeXUseBibliographyAction" />
+            <separator />
+            <toolitem action="LaTeXBuildImageAction" />
+        </toolbar>
+    </ui>"""
 
 # actions
 
 from ..latex.actions import LaTeXMenuAction, LaTeXNewAction, LaTeXChooseMasterAction, \
-		LaTeXItemizeAction, LaTeXEnumerateAction, LaTeXFontFamilyAction, LaTeXFontFamilyMenuAction, LaTeXBoldAction, \
-		LaTeXItalicAction, LaTeXEmphasizeAction, LaTeXDescriptionAction, LaTeXStructureMenuAction, LaTeXPartAction, LaTeXChapterAction, \
-		LaTeXSectionAction, LaTeXSubsectionAction, LaTeXParagraphAction,LaTeXSubparagraphAction, LaTeXStructureAction, \
-		LaTeXGraphicsAction, LaTeXUseBibliographyAction, LaTeXTableAction, LaTeXListingAction, LaTeXJustifyLeftAction, \
-		LaTeXJustifyCenterAction, LaTeXJustifyRightAction, LaTeXMathMenuAction, LaTeXMathAction, LaTeXDisplayMathAction, \
-		LaTeXEquationAction, LaTeXUnEqnArrayAction, LaTeXEqnArrayAction, LaTeXUnderlineAction, LaTeXSmallCapitalsAction, \
-		LaTeXRomanAction, LaTeXSansSerifAction, LaTeXTypewriterAction, LaTeXCloseEnvironmentAction, LaTeXBlackboardBoldAction, \
-		LaTeXCaligraphyAction, LaTeXFrakturAction, LaTeXBuildImageAction, LaTeXSaveAsTemplateAction
+        LaTeXItemizeAction, LaTeXEnumerateAction, LaTeXFontFamilyAction, LaTeXFontFamilyMenuAction, LaTeXBoldAction, \
+        LaTeXItalicAction, LaTeXEmphasizeAction, LaTeXDescriptionAction, LaTeXStructureMenuAction, LaTeXPartAction, LaTeXChapterAction, \
+        LaTeXSectionAction, LaTeXSubsectionAction, LaTeXParagraphAction,LaTeXSubparagraphAction, LaTeXStructureAction, \
+        LaTeXGraphicsAction, LaTeXUseBibliographyAction, LaTeXTableAction, LaTeXListingAction, LaTeXJustifyLeftAction, \
+        LaTeXJustifyCenterAction, LaTeXJustifyRightAction, LaTeXMathMenuAction, LaTeXMathAction, LaTeXDisplayMathAction, \
+        LaTeXEquationAction, LaTeXUnEqnArrayAction, LaTeXEqnArrayAction, LaTeXUnderlineAction, LaTeXSmallCapitalsAction, \
+        LaTeXRomanAction, LaTeXSansSerifAction, LaTeXTypewriterAction, LaTeXCloseEnvironmentAction, LaTeXBlackboardBoldAction, \
+        LaTeXCaligraphyAction, LaTeXFrakturAction, LaTeXBuildImageAction, LaTeXSaveAsTemplateAction
 
 from ..bibtex.actions import BibTeXMenuAction, BibTeXNewEntryAction
 
-ACTIONS = [ LaTeXMenuAction, LaTeXNewAction, LaTeXChooseMasterAction, 
-		LaTeXItemizeAction, LaTeXEnumerateAction, LaTeXFontFamilyAction, LaTeXFontFamilyMenuAction, LaTeXBoldAction, 
-		LaTeXItalicAction, LaTeXEmphasizeAction, LaTeXDescriptionAction, LaTeXStructureMenuAction, LaTeXPartAction, LaTeXChapterAction, 
-		LaTeXSectionAction, LaTeXSubsectionAction, LaTeXParagraphAction,LaTeXSubparagraphAction, LaTeXStructureAction, 
-		LaTeXGraphicsAction, LaTeXUseBibliographyAction, LaTeXTableAction, LaTeXListingAction, LaTeXJustifyLeftAction, 
-		LaTeXJustifyCenterAction, LaTeXJustifyRightAction, LaTeXMathMenuAction, LaTeXMathAction, LaTeXDisplayMathAction, 
-		LaTeXEquationAction, LaTeXUnEqnArrayAction, LaTeXEqnArrayAction, LaTeXUnderlineAction, LaTeXSmallCapitalsAction, 
-		LaTeXRomanAction, LaTeXSansSerifAction, LaTeXTypewriterAction, LaTeXCloseEnvironmentAction, LaTeXBlackboardBoldAction, 
-		LaTeXCaligraphyAction, LaTeXFrakturAction, LaTeXBuildImageAction, LaTeXSaveAsTemplateAction, 
-		BibTeXMenuAction, BibTeXNewEntryAction ]
+ACTIONS = [ LaTeXMenuAction, LaTeXNewAction, LaTeXChooseMasterAction,
+        LaTeXItemizeAction, LaTeXEnumerateAction, LaTeXFontFamilyAction, LaTeXFontFamilyMenuAction, LaTeXBoldAction,
+        LaTeXItalicAction, LaTeXEmphasizeAction, LaTeXDescriptionAction, LaTeXStructureMenuAction, LaTeXPartAction, LaTeXChapterAction,
+        LaTeXSectionAction, LaTeXSubsectionAction, LaTeXParagraphAction,LaTeXSubparagraphAction, LaTeXStructureAction,
+        LaTeXGraphicsAction, LaTeXUseBibliographyAction, LaTeXTableAction, LaTeXListingAction, LaTeXJustifyLeftAction,
+        LaTeXJustifyCenterAction, LaTeXJustifyRightAction, LaTeXMathMenuAction, LaTeXMathAction, LaTeXDisplayMathAction,
+        LaTeXEquationAction, LaTeXUnEqnArrayAction, LaTeXEqnArrayAction, LaTeXUnderlineAction, LaTeXSmallCapitalsAction,
+        LaTeXRomanAction, LaTeXSansSerifAction, LaTeXTypewriterAction, LaTeXCloseEnvironmentAction, LaTeXBlackboardBoldAction,
+        LaTeXCaligraphyAction, LaTeXFrakturAction, LaTeXBuildImageAction, LaTeXSaveAsTemplateAction,
+        BibTeXMenuAction, BibTeXNewEntryAction ]
 
 # views
 
@@ -143,11 +143,11 @@ from ..bibtex.views import BibTeXOutlineView
 
 #WINDOW_SCOPE_VIEWS = { ".tex" : {"LaTeXSymbolMapView" : LaTeXSymbolMapView } }
 #
-#EDITOR_SCOPE_VIEWS = { ".tex" : {"IssueView" : IssueView, 
-#								 "LaTeXOutlineView" : LaTeXOutlineView},
-#								 
-#					   ".bib" : {"IssueView" : IssueView, 
-#								 "BibTeXOutlineView" : BibTeXOutlineView} }
+#EDITOR_SCOPE_VIEWS = { ".tex" : {"IssueView" : IssueView,
+#                                 "LaTeXOutlineView" : LaTeXOutlineView},
+#
+#                       ".bib" : {"IssueView" : IssueView,
+#                                 "BibTeXOutlineView" : BibTeXOutlineView} }
 
 from ..preferences import Preferences
 LATEX_EXTENSIONS = Preferences().get("latex-extensions").split(",")
@@ -157,11 +157,11 @@ WINDOW_SCOPE_VIEWS = {}
 EDITOR_SCOPE_VIEWS = {}
 
 for e in LATEX_EXTENSIONS:
-	WINDOW_SCOPE_VIEWS[e] = {"LaTeXSymbolMapView" : LaTeXSymbolMapView }
-	EDITOR_SCOPE_VIEWS[e] = {"IssueView" : IssueView, "LaTeXOutlineView" : LaTeXOutlineView}
+    WINDOW_SCOPE_VIEWS[e] = {"LaTeXSymbolMapView" : LaTeXSymbolMapView }
+    EDITOR_SCOPE_VIEWS[e] = {"IssueView" : IssueView, "LaTeXOutlineView" : LaTeXOutlineView}
 
 for e in BIBTEX_EXTENSIONS:
-	EDITOR_SCOPE_VIEWS[e] = {"IssueView" : IssueView, "BibTeXOutlineView" : BibTeXOutlineView}
+    EDITOR_SCOPE_VIEWS[e] = {"IssueView" : IssueView, "BibTeXOutlineView" : BibTeXOutlineView}
 
 
 # editors
diff --git a/latex/base/decorators.py b/latex/base/decorators.py
index 204fc54..844b131 100644
--- a/latex/base/decorators.py
+++ b/latex/base/decorators.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -34,175 +34,175 @@ from . import File
 # TODO: maybe create ActionDelegate for GeditWindowDecorator
 
 class GeditTabDecorator(object):
-	"""
-	This monitors the opened file and manages the Editor objects
-	according to the current file extension
-	"""
-	
-	_log = getLogger("GeditTabDecorator")
-	
-	def __init__(self, window_decorator, tab, init=False):
-		"""
-		Construct a GeditTabDecorator
-		
-		@param window_decorator: the parent GeditWindowDecorator
-		@param tab: the GeditTab to create this for
-		@param init: has to be True if this is created on plugin init to decorate 
-						already opened files
-		"""
-		self._window_decorator = window_decorator
-		self._tab = tab
-		self._text_buffer = tab.get_document()
-		self._text_view = tab.get_view()
-		
-		self._editor = None
-		self._file = None
-		
-		# initially check the editor instance
-		#
-		# this needs to be done, because when we init for already opened files
-		# (when plugin is activated in config) we get no "loaded" signal
-		if init:
-			self._adjust_editor()
-		
-		# listen to GeditDocument signals
-		self._signals_handlers = [
-				self._text_buffer.connect("loaded", self._on_load),
-				self._text_buffer.connect("saved", self._on_save)
-		]
-		
-		self._log.debug("Created %s" % self)
-		
-	@property
-	def tab(self):
-		return self._tab
-	
-	def _on_load(self, document, param):
-		"""
-		A file has been loaded
-		"""
-		self._log.debug("loaded")
-		
-		self._adjust_editor()
-	
-	def _on_save(self, document, param):
-		"""
-		The file has been saved
-		"""
-		self._log.debug("saved")
-		
-		if not self._adjust_editor():
-			# if the editor has not changed
-			if self._editor:
-				self._editor.on_save()
-	
-	def _adjust_editor(self):
-		"""
-		Check if the URI has changed and manage Editor object according to
-		file extension
-		
-		@return: True if the editor has changed
-		"""
-		location = self._text_buffer.get_location()
-		if location is None:
-			# this happends when the plugin is activated in a running Gedit
-			# and this decorator is created for the empty file
-			
-			self._log.debug("No file loaded")
-			
-			if self._window_decorator.window.get_active_view() == self._text_view:
-				self._window_decorator.adjust(self)
-			
-		else:
-			file = File(location.get_uri())
-			
-			if file == self._file:		# FIXME: != doesn't work for File...
-				return False
-			else:
-				self._log.debug("_adjust_editor: URI has changed")
-				
-				self._file = file
-				
-				# URI has changed - manage the editor instance
-				if self._editor:
-					# editor is present - destroy editor
-					self._editor.destroy()
-					self._editor = None
-	
-				# FIXME: comparing file extensions should be case-INsensitive... 
-				extension = file.extension
-				
-				# find Editor class for extension
-				editor_class = None
-				for clazz in EDITORS:
-					if extension in clazz.extensions:
-						editor_class = clazz
-						break
-				
-				if not editor_class is None:
-					# create instance
-					self._editor = editor_class.__new__(editor_class)
-					editor_class.__init__(self._editor, self, file)
-					
-					# The following doesn't work because the right expression is evaluated
-					# and then assigned to the left. This means that Editor.__init__ is
-					# running and reading _editor while _editor is None. That leads to
-					# 
-					# Traceback (most recent call last):
-					#   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/base/decorators.py", line 662, in _on_load
-					#     self._adjust_editor()
-					#   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/base/decorators.py", line 716, in _adjust_editor
-					#     self._editor = editor_class(self, file)
-					#   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/base/__init__.py", line 353, in __init__
-					#     self.init(file, self._window_context)
-					#   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/latex/editor.py", line 106, in init
-					#     self.__parse()
-					#   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/latex/editor.py", line 279, in __parse
-					#     self._outline_view.set_outline(self._outline)
-					#   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/latex/views.py", line 228, in set_outline
-					#     OutlineConverter().convert(self._store, outline, self._offset_map, self._context.active_editor.edited_file)
-					
-					#self._editor = editor_class(self, file)
-				else:
-					self._log.warning("No editor class found for extension %s" % extension)
-				
-				# tell WindowDecorator to adjust actions
-				# but only if this tab is the active tab
-				if self._window_decorator.window.get_active_view() == self._text_view:
-					self._window_decorator.adjust(self)
-	
-				# notify that URI has changed
-				return True
-	
-	@property
-	def file(self):
-		return self._file
-	
-	@property
-	def editor(self):
-		return self._editor
-	
-	@property
-	def extension(self):
-		"""
-		@return: the extension of the currently opened file
-		"""
-		if self._file is None:
-			return None
-		else:
-			return self._file.extension
-	
-	def destroy(self):
-		# disconnect from signals
-		for handler in self._signals_handlers:
-			self._text_buffer.disconnect(handler)
-		
-		# unreference the window decorator
-		del self._window_decorator
-
-		# destroy Editor instance
-		if not self._editor is None:
-			self._editor.destroy()
-
-	def __del__(self):
-		self._log.debug("Properly destroyed %s" % self)
+    """
+    This monitors the opened file and manages the Editor objects
+    according to the current file extension
+    """
+
+    _log = getLogger("GeditTabDecorator")
+
+    def __init__(self, window_decorator, tab, init=False):
+        """
+        Construct a GeditTabDecorator
+
+        @param window_decorator: the parent GeditWindowDecorator
+        @param tab: the GeditTab to create this for
+        @param init: has to be True if this is created on plugin init to decorate
+                        already opened files
+        """
+        self._window_decorator = window_decorator
+        self._tab = tab
+        self._text_buffer = tab.get_document()
+        self._text_view = tab.get_view()
+
+        self._editor = None
+        self._file = None
+
+        # initially check the editor instance
+        #
+        # this needs to be done, because when we init for already opened files
+        # (when plugin is activated in config) we get no "loaded" signal
+        if init:
+            self._adjust_editor()
+
+        # listen to GeditDocument signals
+        self._signals_handlers = [
+                self._text_buffer.connect("loaded", self._on_load),
+                self._text_buffer.connect("saved", self._on_save)
+        ]
+
+        self._log.debug("Created %s" % self)
+
+    @property
+    def tab(self):
+        return self._tab
+
+    def _on_load(self, document, param):
+        """
+        A file has been loaded
+        """
+        self._log.debug("loaded")
+
+        self._adjust_editor()
+
+    def _on_save(self, document, param):
+        """
+        The file has been saved
+        """
+        self._log.debug("saved")
+
+        if not self._adjust_editor():
+            # if the editor has not changed
+            if self._editor:
+                self._editor.on_save()
+
+    def _adjust_editor(self):
+        """
+        Check if the URI has changed and manage Editor object according to
+        file extension
+
+        @return: True if the editor has changed
+        """
+        location = self._text_buffer.get_location()
+        if location is None:
+            # this happends when the plugin is activated in a running Gedit
+            # and this decorator is created for the empty file
+
+            self._log.debug("No file loaded")
+
+            if self._window_decorator.window.get_active_view() == self._text_view:
+                self._window_decorator.adjust(self)
+
+        else:
+            file = File(location.get_uri())
+
+            if file == self._file:        # FIXME: != doesn't work for File...
+                return False
+            else:
+                self._log.debug("_adjust_editor: URI has changed")
+
+                self._file = file
+
+                # URI has changed - manage the editor instance
+                if self._editor:
+                    # editor is present - destroy editor
+                    self._editor.destroy()
+                    self._editor = None
+
+                # FIXME: comparing file extensions should be case-INsensitive...
+                extension = file.extension
+
+                # find Editor class for extension
+                editor_class = None
+                for clazz in EDITORS:
+                    if extension in clazz.extensions:
+                        editor_class = clazz
+                        break
+
+                if not editor_class is None:
+                    # create instance
+                    self._editor = editor_class.__new__(editor_class)
+                    editor_class.__init__(self._editor, self, file)
+
+                    # The following doesn't work because the right expression is evaluated
+                    # and then assigned to the left. This means that Editor.__init__ is
+                    # running and reading _editor while _editor is None. That leads to
+                    #
+                    # Traceback (most recent call last):
+                    #   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/base/decorators.py", line 662, in _on_load
+                    #     self._adjust_editor()
+                    #   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/base/decorators.py", line 716, in _adjust_editor
+                    #     self._editor = editor_class(self, file)
+                    #   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/base/__init__.py", line 353, in __init__
+                    #     self.init(file, self._window_context)
+                    #   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/latex/editor.py", line 106, in init
+                    #     self.__parse()
+                    #   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/latex/editor.py", line 279, in __parse
+                    #     self._outline_view.set_outline(self._outline)
+                    #   File "/home/michael/.gnome2/Gedit/plugins/GeditLaTeXPlugin/src/latex/views.py", line 228, in set_outline
+                    #     OutlineConverter().convert(self._store, outline, self._offset_map, self._context.active_editor.edited_file)
+
+                    #self._editor = editor_class(self, file)
+                else:
+                    self._log.warning("No editor class found for extension %s" % extension)
+
+                # tell WindowDecorator to adjust actions
+                # but only if this tab is the active tab
+                if self._window_decorator.window.get_active_view() == self._text_view:
+                    self._window_decorator.adjust(self)
+
+                # notify that URI has changed
+                return True
+
+    @property
+    def file(self):
+        return self._file
+
+    @property
+    def editor(self):
+        return self._editor
+
+    @property
+    def extension(self):
+        """
+        @return: the extension of the currently opened file
+        """
+        if self._file is None:
+            return None
+        else:
+            return self._file.extension
+
+    def destroy(self):
+        # disconnect from signals
+        for handler in self._signals_handlers:
+            self._text_buffer.disconnect(handler)
+
+        # unreference the window decorator
+        del self._window_decorator
+
+        # destroy Editor instance
+        if not self._editor is None:
+            self._editor.destroy()
+
+    def __del__(self):
+        self._log.debug("Properly destroyed %s" % self)
diff --git a/latex/base/job.py b/latex/base/job.py
index 4ac1fc7..59f9242 100644
--- a/latex/base/job.py
+++ b/latex/base/job.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -35,169 +35,169 @@ OBJECT_PATH = '/org/gedit/LaTeXPlugin/JobManager'
 
 class Job(object):
 
-	__log = logging.getLogger("Job")
-
-	class NoneReturned(object):
-		pass
-
-	def __init__(self, argument=None):
-		"""
-		@param arguments: a list of objects to be passed to the Job
-		"""
-		self.__argument = argument
-		self.__returned = self.NoneReturned()
-		self.__change_listener = None
-
-	def set_argument(self, argument):
-		self.__argument = argument
-
-	def schedule(self):
-		"""
-		Run the Job as a subprocess
-		"""
-		# create queue for communication
-		self.__queue = multiprocessing.Queue()
-		
-		# run process
-		self.__process = multiprocessing.Process(target=self.__start, args=(self.__queue,))
-		
-		# enqueue argument object
-		self.__queue.put(self.__argument)
-		
-		# start process
-		self.__process.start()
-	
-	def abort(self):
-		"""
-		Abort the Job process
-		"""
-		self.__process.terminate()
-		
-		# TODO: cleanup?
-	
-	def get_returned(self):
-		"""
-		Get the objects returned by the Job
-		"""
-		if type(self.__returned) is self.NoneReturned:
-			# dequeue returned object
-			self.__returned = self.__queue.get()
-		return self.__returned
-	
-	def get_exception(self):
-		return self.__exception
-	
-	@property
-	def id(self):
-		return id(self)
-	
-	def set_change_listener(self, job_change_listener):
-		self.__change_listener = job_change_listener
-	
-	def __start(self, queue):
-		"""
-		This is started as a subprocess in a separate address space
-		"""
-		# register state change listener
-		if not self.__change_listener is None:
-			job_manager.add_listener(self.id, self.__change_listener)
-		
-		# notify state change
-		job_manager.change_state(self.id, JobManager.STATE_STARTED)
-		
-		# dequeue argument object
-		argument = queue.get()
-		
-		# run the job
-		self.__exception = None
-		try:
-			returned = self._run(argument)
-		except Exception, e:
-			self.__log.error(e)
-			self.__exception = e
-		
-		# enqueue returned object
-		queue.put(returned)
-		
-		# notify state change
-		job_manager.change_state(self.id, JobManager.STATE_COMPLETED)
-		
-		# deregister state change listener
-		if not self.__change_listener is None:
-			job_manager.remove_listener(self.id)
-	
-	def _run(self, arguments):
-		"""
-		@return: a list of objects that should be made available after completion
-		"""
-		pass
+    __log = logging.getLogger("Job")
+
+    class NoneReturned(object):
+        pass
+
+    def __init__(self, argument=None):
+        """
+        @param arguments: a list of objects to be passed to the Job
+        """
+        self.__argument = argument
+        self.__returned = self.NoneReturned()
+        self.__change_listener = None
+
+    def set_argument(self, argument):
+        self.__argument = argument
+
+    def schedule(self):
+        """
+        Run the Job as a subprocess
+        """
+        # create queue for communication
+        self.__queue = multiprocessing.Queue()
+
+        # run process
+        self.__process = multiprocessing.Process(target=self.__start, args=(self.__queue,))
+
+        # enqueue argument object
+        self.__queue.put(self.__argument)
+
+        # start process
+        self.__process.start()
+
+    def abort(self):
+        """
+        Abort the Job process
+        """
+        self.__process.terminate()
+
+        # TODO: cleanup?
+
+    def get_returned(self):
+        """
+        Get the objects returned by the Job
+        """
+        if type(self.__returned) is self.NoneReturned:
+            # dequeue returned object
+            self.__returned = self.__queue.get()
+        return self.__returned
+
+    def get_exception(self):
+        return self.__exception
+
+    @property
+    def id(self):
+        return id(self)
+
+    def set_change_listener(self, job_change_listener):
+        self.__change_listener = job_change_listener
+
+    def __start(self, queue):
+        """
+        This is started as a subprocess in a separate address space
+        """
+        # register state change listener
+        if not self.__change_listener is None:
+            job_manager.add_listener(self.id, self.__change_listener)
+
+        # notify state change
+        job_manager.change_state(self.id, JobManager.STATE_STARTED)
+
+        # dequeue argument object
+        argument = queue.get()
+
+        # run the job
+        self.__exception = None
+        try:
+            returned = self._run(argument)
+        except Exception, e:
+            self.__log.error(e)
+            self.__exception = e
+
+        # enqueue returned object
+        queue.put(returned)
+
+        # notify state change
+        job_manager.change_state(self.id, JobManager.STATE_COMPLETED)
+
+        # deregister state change listener
+        if not self.__change_listener is None:
+            job_manager.remove_listener(self.id)
+
+    def _run(self, arguments):
+        """
+        @return: a list of objects that should be made available after completion
+        """
+        pass
 
 
 class JobChangeListener(object):
-	"""
-	Callback oject for listening to the state changes of a Job
-	"""
-	def _on_state_changed(self, state):
-		pass
-	
-	
+    """
+    Callback oject for listening to the state changes of a Job
+    """
+    def _on_state_changed(self, state):
+        pass
+
+
 class GlobalJobChangeListener(object):
-	"""
-	Callback oject for listening to the state changes of ALL Jobs
-	"""
-	def _on_state_changed(self, job_id, state):
-		pass
+    """
+    Callback oject for listening to the state changes of ALL Jobs
+    """
+    def _on_state_changed(self, job_id, state):
+        pass
 
 
 class JobManager(dbus.service.Object):
-	
-	STATE_STARTED, STATE_COMPLETED = 1, 2
-	
-	__log = logging.getLogger("JobManager")
-	
-	def __init__(self):
-		bus_name = dbus.service.BusName(BUS_NAME, bus=dbus.SessionBus())
-		dbus.service.Object.__init__(self, bus_name, OBJECT_PATH)
-		
-		self.__global_listener = None
-		self.__listeners = {}
-		
-		self.__log.debug("Created JobManager instance %s" % id(self))
-
-	@dbus.service.method(dbus_interface="org.gedit.JobManagerInterface")
-	def change_state(self, job_id, state):
-		"""
-		The job with id <job_id> has changed its state to <state>
-		"""
-		self.__log.debug("change_state(%s, %s)" % (job_id, state))
-		
-		# notify global listener
-		if self.__global_listener is None:
-			self.__log.warn("No global listener")
-		else:
-			self.__global_listener._on_state_changed(job_id, state)
-		
-		# notify listener if present
-		try:
-			self.__listeners[job_id]._on_state_changed(state)
-		except KeyError:
-			self.__log.warn("No listener for job %s" % job_id)
-	
-	def set_global_listener(self, global_job_change_listener):
-		self.__global_listener = global_job_change_listener
-	
-	def add_listener(self, job_id, job_change_listener):
-		self.__listeners[job_id] = job_change_listener
-	
-	def remove_listener(self, job_id):
-		del self.__listeners[job_id]
-	
-	def dispose(self):
-		"""
-		End life-cycle
-		"""
-		self.__log.debug("dispose")
-		
+
+    STATE_STARTED, STATE_COMPLETED = 1, 2
+
+    __log = logging.getLogger("JobManager")
+
+    def __init__(self):
+        bus_name = dbus.service.BusName(BUS_NAME, bus=dbus.SessionBus())
+        dbus.service.Object.__init__(self, bus_name, OBJECT_PATH)
+
+        self.__global_listener = None
+        self.__listeners = {}
+
+        self.__log.debug("Created JobManager instance %s" % id(self))
+
+    @dbus.service.method(dbus_interface="org.gedit.JobManagerInterface")
+    def change_state(self, job_id, state):
+        """
+        The job with id <job_id> has changed its state to <state>
+        """
+        self.__log.debug("change_state(%s, %s)" % (job_id, state))
+
+        # notify global listener
+        if self.__global_listener is None:
+            self.__log.warn("No global listener")
+        else:
+            self.__global_listener._on_state_changed(job_id, state)
+
+        # notify listener if present
+        try:
+            self.__listeners[job_id]._on_state_changed(state)
+        except KeyError:
+            self.__log.warn("No listener for job %s" % job_id)
+
+    def set_global_listener(self, global_job_change_listener):
+        self.__global_listener = global_job_change_listener
+
+    def add_listener(self, job_id, job_change_listener):
+        self.__listeners[job_id] = job_change_listener
+
+    def remove_listener(self, job_id):
+        del self.__listeners[job_id]
+
+    def dispose(self):
+        """
+        End life-cycle
+        """
+        self.__log.debug("dispose")
+
 
 job_manager = JobManager()
 
diff --git a/latex/base/resources.py b/latex/base/resources.py
index 4369ba8..06f9199 100644
--- a/latex/base/resources.py
+++ b/latex/base/resources.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -43,67 +43,67 @@ _PATH_SRCDIR = os.path.abspath(os.path.join(_PATH_ME,"..","..","data"))
 # beyond that case, by preferring the local copy to the system one, it
 # allows the user to customize things cleanly
 _PATH_RO_RESOURCES = [p for p in (
-	_PATH_SRCDIR, _PATH_USER, _PATH_SYSTEM) if os.path.exists(p)]
+    _PATH_SRCDIR, _PATH_USER, _PATH_SYSTEM) if os.path.exists(p)]
 
 _log.debug("RO locations: %s" % ",".join(_PATH_RO_RESOURCES ))
 _log.debug("RW location: %s" % _PATH_SYSTEM)
 
 _installed_system_wide = os.path.exists(_PATH_SYSTEM)
 if _installed_system_wide:
-	# ensure that we have a user plugin dir
-	if not os.path.exists(_PATH_USER):
-		_log.debug("Creating %s" % _PATH_USER)
-		os.makedirs(_PATH_USER)
-	PLUGIN_PATH = _PATH_SYSTEM      # FIXME: only used by build to expand $plugin
+    # ensure that we have a user plugin dir
+    if not os.path.exists(_PATH_USER):
+        _log.debug("Creating %s" % _PATH_USER)
+        os.makedirs(_PATH_USER)
+    PLUGIN_PATH = _PATH_SYSTEM      # FIXME: only used by build to expand $plugin
 else:
-	PLUGIN_PATH = _PATH_USER
+    PLUGIN_PATH = _PATH_USER
 
 MODE_READONLY, MODE_READWRITE = 1, 2
 
 def find_resource(relative_path, access_mode=MODE_READONLY):
-	"""
-	This locates a resource used by the plugin. The access mode determines where to 
-	search for the relative path.
-	
-	@param relative_path: a relative path like 'icons/smiley.png'
-	@param access_mode: MODE_READONLY|MODE_READWRITE
-	
-	@return: the full filename of the resource
-	"""
-	_log.debug("Finding: %s (%d)" % (relative_path, access_mode))
-	if access_mode == MODE_READONLY:
-		# locate a resource for read-only access. Prefer user files
-		# to system ones. See comment above
-		for p in _PATH_RO_RESOURCES:
-			path = "%s/%s" % (p, relative_path)
-			if os.path.exists(path):
-				return path
-
-		_log.critical("File not found: %s" % path)
-		return None
-	
-	elif access_mode == MODE_READWRITE:
-		# locate a user-specific resource for read/write access
-		path = "%s/%s" % (_PATH_USER, relative_path)
-		if os.path.exists(path):
-			return path
-
-		if _installed_system_wide:
-			# resource doesn't exist yet in the user's directory
-			# copy the system-wide version
-			rw_source = "%s/%s" % (_PATH_SYSTEM, relative_path)
-		else:
-			# we are in the sourcedir
-			rw_source = "%s/%s" % (_PATH_SRCDIR, relative_path)
-
-		try:
-			_log.info("Copying file to user path %s -> %s" % (rw_source, path))
-			assert(rw_source != path)
-			shutil.copyfile(rw_source, path)
-		except IOError:
-			_log.critical("Failed to copy resource to user directory: %s -> %s" % (rw_source, path))
-		except AssertionError:
-			_log.critical("Source and dest are the same. Bad programmer")
-
-		return path
+    """
+    This locates a resource used by the plugin. The access mode determines where to
+    search for the relative path.
+
+    @param relative_path: a relative path like 'icons/smiley.png'
+    @param access_mode: MODE_READONLY|MODE_READWRITE
+
+    @return: the full filename of the resource
+    """
+    _log.debug("Finding: %s (%d)" % (relative_path, access_mode))
+    if access_mode == MODE_READONLY:
+        # locate a resource for read-only access. Prefer user files
+        # to system ones. See comment above
+        for p in _PATH_RO_RESOURCES:
+            path = "%s/%s" % (p, relative_path)
+            if os.path.exists(path):
+                return path
+
+        _log.critical("File not found: %s" % path)
+        return None
+
+    elif access_mode == MODE_READWRITE:
+        # locate a user-specific resource for read/write access
+        path = "%s/%s" % (_PATH_USER, relative_path)
+        if os.path.exists(path):
+            return path
+
+        if _installed_system_wide:
+            # resource doesn't exist yet in the user's directory
+            # copy the system-wide version
+            rw_source = "%s/%s" % (_PATH_SYSTEM, relative_path)
+        else:
+            # we are in the sourcedir
+            rw_source = "%s/%s" % (_PATH_SRCDIR, relative_path)
+
+        try:
+            _log.info("Copying file to user path %s -> %s" % (rw_source, path))
+            assert(rw_source != path)
+            shutil.copyfile(rw_source, path)
+        except IOError:
+            _log.critical("Failed to copy resource to user directory: %s -> %s" % (rw_source, path))
+        except AssertionError:
+            _log.critical("Source and dest are the same. Bad programmer")
+
+        return path
 
diff --git a/latex/base/templates.py b/latex/base/templates.py
index 66f58e2..02eeade 100644
--- a/latex/base/templates.py
+++ b/latex/base/templates.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -26,588 +26,588 @@ from logging import getLogger
 
 
 class TemplateToken:
-	"""
-	A token of a template expression
-	"""
-	LITERAL, PLACEHOLDER, CURSOR = 1,2,3
-	
-	def __init__(self, type, value=None):
-		self.type = type
-		self.value = value
+    """
+    A token of a template expression
+    """
+    LITERAL, PLACEHOLDER, CURSOR = 1,2,3
+
+    def __init__(self, type, value=None):
+        self.type = type
+        self.value = value
 
 
 class TemplateTokenizer:
-	"""
-	A simple state machine for tokenizing a template expression
-	"""
-	_INIT, _LITERAL, _LITERAL_DOLLAR, _DOLLAR, _PLACEHOLDER = 1,2,3,4,5
-	
-	def __init__(self, expression):
-		self._character_iter = iter(expression)
-		self._buffer = []
-		self._end_of_expression = False
-	
-	def __iter__(self):
-		return self
-	
-	@property
-	def _next(self):
-		"""
-		Return the next character from the push back stack or the
-		source and return (not raise) StopIteration on end of string
-		"""
-		try:
-			return self._buffer.pop()
-		except IndexError:
-			try:
-				return self._character_iter.next()
-			except StopIteration:
-				return StopIteration
-	
-	def _push(self, character):
-		"""
-		Push one character of the push back stack
-		"""
-		self._buffer.append(character)
-	
-	def next(self):
-		"""
-		Return the next token
-		"""
-		state = self._INIT
-		while True:
-			if self._end_of_expression:
-				# end of expression reached in last iteration
-				raise StopIteration
-			
-			char = self._next
-			
-			if state == self._INIT:
-				if char == "$":
-					state = self._DOLLAR
-				elif char == StopIteration:
-					raise StopIteration
-				else:
-					literal_value = char
-					state = self._LITERAL
-			elif state == self._LITERAL:
-				if char == "$":
-					state = self._LITERAL_DOLLAR
-				elif char == StopIteration:
-					self._end_of_expression = True
-					return TemplateToken(TemplateToken.LITERAL, literal_value)
-				else:
-					literal_value += char
-			elif state == self._LITERAL_DOLLAR:
-				if char == "{":
-					self._push("{")
-					self._push("$")
-					return TemplateToken(TemplateToken.LITERAL, literal_value)
-				elif char == "_":
-					self._push("_")
-					self._push("$")
-					return TemplateToken(TemplateToken.LITERAL, literal_value)
-				elif char == StopIteration:
-					self._end_of_expression = True
-					literal_value += "$"
-					return TemplateToken(TemplateToken.LITERAL, literal_value)
-				else:
-					literal_value += "$"
-					state = self._LITERAL
-			elif state == self._DOLLAR:
-				if char == "_":
-					return TemplateToken(TemplateToken.CURSOR)
-				elif char == "{":
-					placeholder_value = ""
-					state = self._PLACEHOLDER
-				elif char == StopIteration:
-					self._end_of_expression = True
-					return TemplateToken(TemplateToken.LITERAL, "$")
-				else:
-					literal_value = "$"
-					state = self._LITERAL
-			elif state == self._PLACEHOLDER:
-				if char == "}":
-					return TemplateToken(TemplateToken.PLACEHOLDER, placeholder_value)
-				elif char == StopIteration:
-					self._end_of_expression = True
-					return TemplateToken(TemplateToken.LITERAL, "${" + placeholder_value)
-				else:
-					placeholder_value += char
+    """
+    A simple state machine for tokenizing a template expression
+    """
+    _INIT, _LITERAL, _LITERAL_DOLLAR, _DOLLAR, _PLACEHOLDER = 1,2,3,4,5
+
+    def __init__(self, expression):
+        self._character_iter = iter(expression)
+        self._buffer = []
+        self._end_of_expression = False
+
+    def __iter__(self):
+        return self
+
+    @property
+    def _next(self):
+        """
+        Return the next character from the push back stack or the
+        source and return (not raise) StopIteration on end of string
+        """
+        try:
+            return self._buffer.pop()
+        except IndexError:
+            try:
+                return self._character_iter.next()
+            except StopIteration:
+                return StopIteration
+
+    def _push(self, character):
+        """
+        Push one character of the push back stack
+        """
+        self._buffer.append(character)
+
+    def next(self):
+        """
+        Return the next token
+        """
+        state = self._INIT
+        while True:
+            if self._end_of_expression:
+                # end of expression reached in last iteration
+                raise StopIteration
+
+            char = self._next
+
+            if state == self._INIT:
+                if char == "$":
+                    state = self._DOLLAR
+                elif char == StopIteration:
+                    raise StopIteration
+                else:
+                    literal_value = char
+                    state = self._LITERAL
+            elif state == self._LITERAL:
+                if char == "$":
+                    state = self._LITERAL_DOLLAR
+                elif char == StopIteration:
+                    self._end_of_expression = True
+                    return TemplateToken(TemplateToken.LITERAL, literal_value)
+                else:
+                    literal_value += char
+            elif state == self._LITERAL_DOLLAR:
+                if char == "{":
+                    self._push("{")
+                    self._push("$")
+                    return TemplateToken(TemplateToken.LITERAL, literal_value)
+                elif char == "_":
+                    self._push("_")
+                    self._push("$")
+                    return TemplateToken(TemplateToken.LITERAL, literal_value)
+                elif char == StopIteration:
+                    self._end_of_expression = True
+                    literal_value += "$"
+                    return TemplateToken(TemplateToken.LITERAL, literal_value)
+                else:
+                    literal_value += "$"
+                    state = self._LITERAL
+            elif state == self._DOLLAR:
+                if char == "_":
+                    return TemplateToken(TemplateToken.CURSOR)
+                elif char == "{":
+                    placeholder_value = ""
+                    state = self._PLACEHOLDER
+                elif char == StopIteration:
+                    self._end_of_expression = True
+                    return TemplateToken(TemplateToken.LITERAL, "$")
+                else:
+                    literal_value = "$"
+                    state = self._LITERAL
+            elif state == self._PLACEHOLDER:
+                if char == "}":
+                    return TemplateToken(TemplateToken.PLACEHOLDER, placeholder_value)
+                elif char == StopIteration:
+                    self._end_of_expression = True
+                    return TemplateToken(TemplateToken.LITERAL, "${" + placeholder_value)
+                else:
+                    placeholder_value += char
 
 
 class Placeholder(object):
-	"""
-	@deprecated: use TemplateToken
-	"""
-	def __init__(self, label, offset):
-		"""
-		@param label: label of this placeholder
-		@param offset: offset in plain text
-		"""
-		self._label = label
-		self._offset = offset
-	
-	@property
-	def label(self):
-		return self._label
-	
-	@property
-	def offset(self):
-		return self._offset
+    """
+    @deprecated: use TemplateToken
+    """
+    def __init__(self, label, offset):
+        """
+        @param label: label of this placeholder
+        @param offset: offset in plain text
+        """
+        self._label = label
+        self._offset = offset
+
+    @property
+    def label(self):
+        return self._label
+
+    @property
+    def offset(self):
+        return self._offset
 
 
 class MalformedTemplateException(Exception):
-	"""
-	Raised if a template expression could not be tokenized
-	"""
+    """
+    Raised if a template expression could not be tokenized
+    """
 
 
 from ..util import verbose
 
 
 class TemplateCompiler(object):
-	"""
-	@deprecated: use TemplateTokenizer
-	"""
-	
-	_S_DEFAULT, _S_IDENT, _S_PLACEHOLDER = 0, 1, 2
-	
-	def _reset(self):
-		self._placeholders = []
-		self._plain = ""
-		self._final_cursor_offset = None
-	
-	@property
-	def placeholders(self):
-		return self._placeholders
-	
-	@property
-	def plain(self):
-		return self._plain
-	
-	@property
-	def final_cursor_offset(self):
-		return self._final_cursor_offset
-	
-	def compile(self, expression):
-		"""
-		@param expression: template expression
-		@raise MalformedTemplateException: if the expression could not be tokenized
-		@return: a list of tokens
-		"""
-		
-		# TODO: redo this from DFA
-		
-		self._reset()
-		
-		state = self._S_DEFAULT
-		offset = 0
-		
-		try:
-			for c in expression:
-				if state == self._S_DEFAULT:
-					# we're in plain text
-					if c == "$":
-						# the magic char appeared, so change state
-						state = self._S_IDENT
-					else:
-						# nothing special, so append to plain text
-						self._plain += c
-						offset += 1
-						
-				elif state == self._S_IDENT:
-					# the magic char has appeared
-					if c == "{":
-						# a placeholder is starting, so create a builder for its name
-						name = []
-						# save its position
-						position = offset
-						# and change state
-						state = self._S_PLACEHOLDER
-					elif c == "_":
-						# "$_" marks the final cursor position
-						self._final_cursor_offset = offset
-						# and change state back to default
-						state = self._S_DEFAULT
-					else:
-						# false alarm, the magic sign was just a dollar sign, so append
-						# the "$" and the current char to plain text
-						self._plain += "$" + c
-						offset += 2
-						# and change to default state
-						state = self._S_DEFAULT
-						
-				elif state == self._S_PLACEHOLDER:
-					# we're in a placeholder definition
-					if c == "}":
-						# it is ending, so append object
-						self._placeholders.append(Placeholder("".join(name), position))
-						self._plain += "".join(name)
-						# change state
-						state = self._S_DEFAULT
-					else:
-						# it is not ending
-						name.append(c)
-						offset += 1
-		except Exception, e:
-			raise MalformedTemplateException(e)
-		
-		if state == self._S_IDENT:
-			# we ended in INDENT state so '$' was the last character - it can't be 
-			# the magic sign
-			self._plain += "$"
-		elif state == self._S_PLACEHOLDER:
-			# if everything went fine we should end up in DEFAULT state
-			# or in INDENT  - but never in PLACEHOLDER
-			raise MalformedTemplateException("Illegal state: %s" % state)
-	
+    """
+    @deprecated: use TemplateTokenizer
+    """
+
+    _S_DEFAULT, _S_IDENT, _S_PLACEHOLDER = 0, 1, 2
+
+    def _reset(self):
+        self._placeholders = []
+        self._plain = ""
+        self._final_cursor_offset = None
+
+    @property
+    def placeholders(self):
+        return self._placeholders
+
+    @property
+    def plain(self):
+        return self._plain
+
+    @property
+    def final_cursor_offset(self):
+        return self._final_cursor_offset
+
+    def compile(self, expression):
+        """
+        @param expression: template expression
+        @raise MalformedTemplateException: if the expression could not be tokenized
+        @return: a list of tokens
+        """
+
+        # TODO: redo this from DFA
+
+        self._reset()
+
+        state = self._S_DEFAULT
+        offset = 0
+
+        try:
+            for c in expression:
+                if state == self._S_DEFAULT:
+                    # we're in plain text
+                    if c == "$":
+                        # the magic char appeared, so change state
+                        state = self._S_IDENT
+                    else:
+                        # nothing special, so append to plain text
+                        self._plain += c
+                        offset += 1
+
+                elif state == self._S_IDENT:
+                    # the magic char has appeared
+                    if c == "{":
+                        # a placeholder is starting, so create a builder for its name
+                        name = []
+                        # save its position
+                        position = offset
+                        # and change state
+                        state = self._S_PLACEHOLDER
+                    elif c == "_":
+                        # "$_" marks the final cursor position
+                        self._final_cursor_offset = offset
+                        # and change state back to default
+                        state = self._S_DEFAULT
+                    else:
+                        # false alarm, the magic sign was just a dollar sign, so append
+                        # the "$" and the current char to plain text
+                        self._plain += "$" + c
+                        offset += 2
+                        # and change to default state
+                        state = self._S_DEFAULT
+
+                elif state == self._S_PLACEHOLDER:
+                    # we're in a placeholder definition
+                    if c == "}":
+                        # it is ending, so append object
+                        self._placeholders.append(Placeholder("".join(name), position))
+                        self._plain += "".join(name)
+                        # change state
+                        state = self._S_DEFAULT
+                    else:
+                        # it is not ending
+                        name.append(c)
+                        offset += 1
+        except Exception, e:
+            raise MalformedTemplateException(e)
+
+        if state == self._S_IDENT:
+            # we ended in INDENT state so '$' was the last character - it can't be
+            # the magic sign
+            self._plain += "$"
+        elif state == self._S_PLACEHOLDER:
+            # if everything went fine we should end up in DEFAULT state
+            # or in INDENT  - but never in PLACEHOLDER
+            raise MalformedTemplateException("Illegal state: %s" % state)
+
 
 from gi.repository import Gdk
 from . import Template
 from ..preferences import Preferences
 
-	
+
 class TemplateDelegate(object):
-	"""
-	This handles templates for an Editor instance
-	"""
-	
-	_log = getLogger("TemplateDelegate")
-	
-	_KEY_TAB = "Tab"
-	_KEY_LEFT_SHIFT_TAB = "ISO_Left_Tab"
-	_KEY_ESCAPE = "Escape"
-	_KEY_RETURN = "Return"
-	
-	def __init__(self, editor):
-		self._editor = editor
-		self._text_buffer = editor.tab_decorator.tab.get_document()
-		self._text_view = editor.tab_decorator.tab.get_view()
-		
-		self._compiler = TemplateCompiler()
-		
-		# create tags
-		self._tag_template = self._text_buffer.create_tag("template", 
-						background=Preferences().get("template-background-color"))
-		self._tag_placeholder = self._text_buffer.create_tag("placeholder", 
-						background=Preferences().get("placeholder-background-color"), 
-						foreground=Preferences().get("placeholder-foreground-color"))
-		
-		self._active = False
-	
-	@verbose
-	def insert(self, template):
-		"""
-		@param template: a Template instance
-		@raise MalformedTemplateException: from TemplateCompiler.compile 
-		"""
-		assert type(template) is Template
-		
-		# apply indentation
-		expression = template.expression.replace("\n", "\n%s" % self._editor.indentation)
-		
-		self._compiler.compile(expression)
-		self._do_insert()
-	
-	def _do_insert(self):
-		if len(self._compiler.placeholders) == 0:
-			# template contains no placeholders > just insert plain text
-			#
-			# if it contains a cursor position, we check if there's a selection
-			# if there is one, we surround that selection by the two pieces of the template
-			
-			if self._compiler.final_cursor_offset:
-				bounds = self._text_buffer.get_selection_bounds()
-				if len(bounds):
-					# cursor position and selection > surround selection
-					
-					text = self._compiler.plain
-					position = self._compiler.final_cursor_offset
-					
-					leftText = text[:position]
-					rightText = text[position:]
-					
-					# store the selection marks
-					lMark = self._text_buffer.create_mark(None, bounds[0], False)
-					
-					rMark = self._text_buffer.create_mark(None, bounds[1], True)
-					
-					# insert first piece at the beginning...
-					self._text_buffer.insert(bounds[0], leftText)				
-					
-					# ...and the other one at the end
-					rIter = self._text_buffer.get_iter_at_mark(rMark)
-					self._text_buffer.insert(rIter, rightText)					
-					
-					# restore selection
-					lIter = self._text_buffer.get_iter_at_mark(lMark)
-					rIter = self._text_buffer.get_iter_at_mark(rMark)
-					self._text_buffer.select_range(lIter, rIter)
-					
-					# delete marks
-					self._text_buffer.delete_mark(lMark)
-					self._text_buffer.delete_mark(rMark)
-				
-				else:
-					# cursor position, no selection > insert text and place cursor
-					
-					startOffset = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert()).get_offset()
-					self._text_buffer.insert_at_cursor(self._compiler.plain)
-					self._markEnd = self._text_buffer.create_mark(None, 
-							self._text_buffer.get_iter_at_offset(startOffset + self._compiler.final_cursor_offset),  True)
-					self._text_buffer.place_cursor(self._text_buffer.get_iter_at_mark(self._markEnd))
-			
-			else:
-				# no final cursor position > just insert plain text
-				
-				self._text_buffer.insert_at_cursor(self._compiler.plain)
-			
-		else:
-			# avoid inserting templates into templates
-			if self._active:
-				self._leave_template(place_cursor=False)
-			
-			# save template start offset
-			startIter = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
-			start = startIter.get_offset()
-			self._markStart = self._text_buffer.create_mark(None, startIter, True)
-			
-			# insert template text
-			self._text_buffer.insert_at_cursor(self._compiler.plain)
-			
-			# highlight placeholders and save their positions
-
-			self._placeholder_marks = []		# holds the left and right marks of all placeholders in the active template
-			
-			for placeholder in self._compiler.placeholders:
-				
-				itLeft = self._text_buffer.get_iter_at_offset(start + placeholder.offset)
-				itRight = self._text_buffer.get_iter_at_offset(start + placeholder.offset + len(placeholder.label))
-				
-				self._text_buffer.apply_tag_by_name("placeholder", itLeft, itRight)
-				
-				markLeft = self._text_buffer.create_mark(None, itLeft, True)
-				markRight = self._text_buffer.create_mark(None, itRight, False)
-				
-				self._placeholder_marks.append([markLeft, markRight])
-			
-			
-			# highlight complete template area
-			itStart = self._text_buffer.get_iter_at_offset(start)
-			itEnd = self._text_buffer.get_iter_at_offset(start + len(self._compiler.plain))
-			
-			self._text_buffer.apply_tag_by_name("template", itStart, itEnd)
-			
-			self._markEnd = self._text_buffer.create_mark(None, itEnd, True)
-			
-			# mark end cursor position or template end
-			if self._compiler.final_cursor_offset:
-				self._mark_final = self._text_buffer.create_mark(None, 
-														self._text_buffer.get_iter_at_offset(start + self._compiler.final_cursor_offset), 
-														True)
-			else:
-				self._mark_final = self._text_buffer.create_mark(None, itEnd, True)
-			
-			
-			self._selected_placeholder = 0
-			
-			self._activate()
-			
-			self._select_next_placeholder()
-	
-	def _activate(self):
-		"""
-		Listen to TextView signals
-		"""
-		assert not self._active
-		
-		self._handlers = [ 
-				self._text_view.connect("key-press-event", self._on_key_pressed),
-				self._text_view.connect_after("key-release-event", self._on_key_released),
-				self._text_view.connect("button-press-event", self._on_button_pressed) ]
-		self._active = True
-	
-	def _deactivate(self):
-		"""
-		Disconnect from TextView signals
-		"""
-		assert self._active
-		
-		for handler in self._handlers:
-			self._text_view.disconnect(handler)
-		
-		# TODO: delete TextMarks
-			
-		self._active = False
-	
-	def _on_key_pressed(self, text_view, event):
-		"""
-		Jump to the next or to the previous placeholder mark
-		"""
-		assert self._active
-		
-		key = Gdk.keyval_name(event.keyval)
-		
-		if key == self._KEY_TAB:
-			# select next placeholder
-			self._selected_placeholder += 1
-			
-			try:
-				self._select_next_placeholder()
-			except IndexError:
-				# last reached
-				self._leave_template()
-			
-			# swallow event
-			return True
-		
-		elif key == self._KEY_LEFT_SHIFT_TAB:
-			# select previous placeholder
-			self._selected_placeholder -= 1
-			
-			try:
-				self._select_next_placeholder()
-			except IndexError:
-				# first reached
-				self._selected_placeholder = 0
-				
-			# swallow event
-			return True
-		
-		elif key == self._KEY_ESCAPE:
-			# abort
-			self._leave_template()
-	
-	def _update_duplicates(self):
-		"""
-		Copy the text from the current placeholder to its duplicates
-		if present
-		"""
-		
-		placeholders = self._compiler.placeholders
-		selected_i = self._selected_placeholder
-		try:
-			selected_placeholder = placeholders[selected_i]
-		except IndexError:
-			# FIXME: template has been left?
-			return
-		
-		# find duplicates
-		duplicates = []
-		for i in range(len(self._compiler.placeholders)):
-			if i != selected_i and placeholders[i].label == placeholders[selected_i].label:
-				duplicates.append(i)
-				
-		# get current text in placeholder
-		l, r = self._placeholder_marks[selected_i]
-		li = self._text_buffer.get_iter_at_mark(l)
-		ri = self._text_buffer.get_iter_at_mark(r)
-		
-		text = self._text_buffer.get_text(li, ri, False)
-		
-		# copy text to duplicates
-		for i in duplicates:
-			l, r = self._placeholder_marks[i]
-			
-			li = self._text_buffer.get_iter_at_mark(l)
-			ri = self._text_buffer.get_iter_at_mark(r)
-			
-			self._text_buffer.delete(li, ri)
-			self._text_buffer.insert(ri, text)
-	
-	def _on_key_released(self, text_view, event):
-		"""
-		Swallow key events if neccessary
-		"""
-		assert self._active
-		
-		key = Gdk.keyval_name(event.keyval)
-		
-		if key == self._KEY_TAB or key == self._KEY_LEFT_SHIFT_TAB:
-			# swallow event
-			return True
-		else:
-			# check if the cursor has left the template
-			if self._cursor_in_template:
-				if not key == self._KEY_RETURN:  # don't update dupl after Return to keep the placeholder highlightings
-					self._update_duplicates()
-			else:
-				self._leave_template(place_cursor=False)
-			
-	
-	def _on_button_pressed(self, text_view, event):
-		"""
-		Leave template when mouse button is pressed
-		"""
-		assert self._active
-		
-		self._leave_template()
-		
-	def _select_next_placeholder(self):
-		"""
-		Select the next placeholder
-		"""
-		# get stored marks
-		markLeft, markRight = self._placeholder_marks[self._selected_placeholder]
-		
-		# select
-		itLeft = self._text_buffer.get_iter_at_mark(markLeft)
-		itRight = self._text_buffer.get_iter_at_mark(markRight)
-		
-		self._text_buffer.select_range(itLeft, itRight)
-	
-	def _leave_template(self, place_cursor=True):
-		"""
-		Quit template insertion.
-		
-		Disconnect from signals, remove highlight, delete marks and place the cursor
-		at the final position.
-		"""
-		#self._log.debug("_leaveTemplate")
-
-		# remove highlighting
-		self._text_buffer.remove_tag_by_name("placeholder", self._text_buffer.get_start_iter(), 
-									self._text_buffer.get_end_iter())
-		self._text_buffer.remove_tag_by_name("template", self._text_buffer.get_start_iter(), 
-									self._text_buffer.get_end_iter())
-		
-		# move to end cursor position or template end
-		if place_cursor:
-			self._text_buffer.place_cursor(self._text_buffer.get_iter_at_mark(self._mark_final))
-		
-		self._deactivate()
-	
-	@property
-	def _cursor_in_template(self):
-		"""
-		@return: True if the cursor is in the template
-		"""
-		itLeft = self._text_buffer.get_iter_at_mark(self._markStart)
-		itRight = self._text_buffer.get_iter_at_mark(self._markEnd)
-		
-		left = itLeft.get_offset()
-		right = itRight.get_offset()
-		offset = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert()).get_offset()
-		
-		if offset < left or offset > right:
-			return False
-		return True
-	
-	def destroy(self):
-		self._log.debug("destroy")
-		
-		# remove tags
-		table = self._text_buffer.get_tag_table()
-		table.remove(self._tag_template)
-		table.remove(self._tag_placeholder)
-		
-		# unreference editor
-		del self._editor
-		
-		# deactivate
-		if self._active:
-			self._deactivate()
-			
-		
-	
+    """
+    This handles templates for an Editor instance
+    """
+
+    _log = getLogger("TemplateDelegate")
+
+    _KEY_TAB = "Tab"
+    _KEY_LEFT_SHIFT_TAB = "ISO_Left_Tab"
+    _KEY_ESCAPE = "Escape"
+    _KEY_RETURN = "Return"
+
+    def __init__(self, editor):
+        self._editor = editor
+        self._text_buffer = editor.tab_decorator.tab.get_document()
+        self._text_view = editor.tab_decorator.tab.get_view()
+
+        self._compiler = TemplateCompiler()
+
+        # create tags
+        self._tag_template = self._text_buffer.create_tag("template",
+                        background=Preferences().get("template-background-color"))
+        self._tag_placeholder = self._text_buffer.create_tag("placeholder",
+                        background=Preferences().get("placeholder-background-color"),
+                        foreground=Preferences().get("placeholder-foreground-color"))
+
+        self._active = False
+
+    @verbose
+    def insert(self, template):
+        """
+        @param template: a Template instance
+        @raise MalformedTemplateException: from TemplateCompiler.compile
+        """
+        assert type(template) is Template
+
+        # apply indentation
+        expression = template.expression.replace("\n", "\n%s" % self._editor.indentation)
+
+        self._compiler.compile(expression)
+        self._do_insert()
+
+    def _do_insert(self):
+        if len(self._compiler.placeholders) == 0:
+            # template contains no placeholders > just insert plain text
+            #
+            # if it contains a cursor position, we check if there's a selection
+            # if there is one, we surround that selection by the two pieces of the template
+
+            if self._compiler.final_cursor_offset:
+                bounds = self._text_buffer.get_selection_bounds()
+                if len(bounds):
+                    # cursor position and selection > surround selection
+
+                    text = self._compiler.plain
+                    position = self._compiler.final_cursor_offset
+
+                    leftText = text[:position]
+                    rightText = text[position:]
+
+                    # store the selection marks
+                    lMark = self._text_buffer.create_mark(None, bounds[0], False)
+
+                    rMark = self._text_buffer.create_mark(None, bounds[1], True)
+
+                    # insert first piece at the beginning...
+                    self._text_buffer.insert(bounds[0], leftText)
+
+                    # ...and the other one at the end
+                    rIter = self._text_buffer.get_iter_at_mark(rMark)
+                    self._text_buffer.insert(rIter, rightText)
+
+                    # restore selection
+                    lIter = self._text_buffer.get_iter_at_mark(lMark)
+                    rIter = self._text_buffer.get_iter_at_mark(rMark)
+                    self._text_buffer.select_range(lIter, rIter)
+
+                    # delete marks
+                    self._text_buffer.delete_mark(lMark)
+                    self._text_buffer.delete_mark(rMark)
+
+                else:
+                    # cursor position, no selection > insert text and place cursor
+
+                    startOffset = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert()).get_offset()
+                    self._text_buffer.insert_at_cursor(self._compiler.plain)
+                    self._markEnd = self._text_buffer.create_mark(None,
+                            self._text_buffer.get_iter_at_offset(startOffset + self._compiler.final_cursor_offset),  True)
+                    self._text_buffer.place_cursor(self._text_buffer.get_iter_at_mark(self._markEnd))
+
+            else:
+                # no final cursor position > just insert plain text
+
+                self._text_buffer.insert_at_cursor(self._compiler.plain)
+
+        else:
+            # avoid inserting templates into templates
+            if self._active:
+                self._leave_template(place_cursor=False)
+
+            # save template start offset
+            startIter = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert())
+            start = startIter.get_offset()
+            self._markStart = self._text_buffer.create_mark(None, startIter, True)
+
+            # insert template text
+            self._text_buffer.insert_at_cursor(self._compiler.plain)
+
+            # highlight placeholders and save their positions
+
+            self._placeholder_marks = []        # holds the left and right marks of all placeholders in the active template
+
+            for placeholder in self._compiler.placeholders:
+
+                itLeft = self._text_buffer.get_iter_at_offset(start + placeholder.offset)
+                itRight = self._text_buffer.get_iter_at_offset(start + placeholder.offset + len(placeholder.label))
+
+                self._text_buffer.apply_tag_by_name("placeholder", itLeft, itRight)
+
+                markLeft = self._text_buffer.create_mark(None, itLeft, True)
+                markRight = self._text_buffer.create_mark(None, itRight, False)
+
+                self._placeholder_marks.append([markLeft, markRight])
+
+
+            # highlight complete template area
+            itStart = self._text_buffer.get_iter_at_offset(start)
+            itEnd = self._text_buffer.get_iter_at_offset(start + len(self._compiler.plain))
+
+            self._text_buffer.apply_tag_by_name("template", itStart, itEnd)
+
+            self._markEnd = self._text_buffer.create_mark(None, itEnd, True)
+
+            # mark end cursor position or template end
+            if self._compiler.final_cursor_offset:
+                self._mark_final = self._text_buffer.create_mark(None,
+                                                        self._text_buffer.get_iter_at_offset(start + self._compiler.final_cursor_offset),
+                                                        True)
+            else:
+                self._mark_final = self._text_buffer.create_mark(None, itEnd, True)
+
+
+            self._selected_placeholder = 0
+
+            self._activate()
+
+            self._select_next_placeholder()
+
+    def _activate(self):
+        """
+        Listen to TextView signals
+        """
+        assert not self._active
+
+        self._handlers = [
+                self._text_view.connect("key-press-event", self._on_key_pressed),
+                self._text_view.connect_after("key-release-event", self._on_key_released),
+                self._text_view.connect("button-press-event", self._on_button_pressed) ]
+        self._active = True
+
+    def _deactivate(self):
+        """
+        Disconnect from TextView signals
+        """
+        assert self._active
+
+        for handler in self._handlers:
+            self._text_view.disconnect(handler)
+
+        # TODO: delete TextMarks
+
+        self._active = False
+
+    def _on_key_pressed(self, text_view, event):
+        """
+        Jump to the next or to the previous placeholder mark
+        """
+        assert self._active
+
+        key = Gdk.keyval_name(event.keyval)
+
+        if key == self._KEY_TAB:
+            # select next placeholder
+            self._selected_placeholder += 1
+
+            try:
+                self._select_next_placeholder()
+            except IndexError:
+                # last reached
+                self._leave_template()
+
+            # swallow event
+            return True
+
+        elif key == self._KEY_LEFT_SHIFT_TAB:
+            # select previous placeholder
+            self._selected_placeholder -= 1
+
+            try:
+                self._select_next_placeholder()
+            except IndexError:
+                # first reached
+                self._selected_placeholder = 0
+
+            # swallow event
+            return True
+
+        elif key == self._KEY_ESCAPE:
+            # abort
+            self._leave_template()
+
+    def _update_duplicates(self):
+        """
+        Copy the text from the current placeholder to its duplicates
+        if present
+        """
+
+        placeholders = self._compiler.placeholders
+        selected_i = self._selected_placeholder
+        try:
+            selected_placeholder = placeholders[selected_i]
+        except IndexError:
+            # FIXME: template has been left?
+            return
+
+        # find duplicates
+        duplicates = []
+        for i in range(len(self._compiler.placeholders)):
+            if i != selected_i and placeholders[i].label == placeholders[selected_i].label:
+                duplicates.append(i)
+
+        # get current text in placeholder
+        l, r = self._placeholder_marks[selected_i]
+        li = self._text_buffer.get_iter_at_mark(l)
+        ri = self._text_buffer.get_iter_at_mark(r)
+
+        text = self._text_buffer.get_text(li, ri, False)
+
+        # copy text to duplicates
+        for i in duplicates:
+            l, r = self._placeholder_marks[i]
+
+            li = self._text_buffer.get_iter_at_mark(l)
+            ri = self._text_buffer.get_iter_at_mark(r)
+
+            self._text_buffer.delete(li, ri)
+            self._text_buffer.insert(ri, text)
+
+    def _on_key_released(self, text_view, event):
+        """
+        Swallow key events if neccessary
+        """
+        assert self._active
+
+        key = Gdk.keyval_name(event.keyval)
+
+        if key == self._KEY_TAB or key == self._KEY_LEFT_SHIFT_TAB:
+            # swallow event
+            return True
+        else:
+            # check if the cursor has left the template
+            if self._cursor_in_template:
+                if not key == self._KEY_RETURN:  # don't update dupl after Return to keep the placeholder highlightings
+                    self._update_duplicates()
+            else:
+                self._leave_template(place_cursor=False)
+
+
+    def _on_button_pressed(self, text_view, event):
+        """
+        Leave template when mouse button is pressed
+        """
+        assert self._active
+
+        self._leave_template()
+
+    def _select_next_placeholder(self):
+        """
+        Select the next placeholder
+        """
+        # get stored marks
+        markLeft, markRight = self._placeholder_marks[self._selected_placeholder]
+
+        # select
+        itLeft = self._text_buffer.get_iter_at_mark(markLeft)
+        itRight = self._text_buffer.get_iter_at_mark(markRight)
+
+        self._text_buffer.select_range(itLeft, itRight)
+
+    def _leave_template(self, place_cursor=True):
+        """
+        Quit template insertion.
+
+        Disconnect from signals, remove highlight, delete marks and place the cursor
+        at the final position.
+        """
+        #self._log.debug("_leaveTemplate")
+
+        # remove highlighting
+        self._text_buffer.remove_tag_by_name("placeholder", self._text_buffer.get_start_iter(),
+                                    self._text_buffer.get_end_iter())
+        self._text_buffer.remove_tag_by_name("template", self._text_buffer.get_start_iter(),
+                                    self._text_buffer.get_end_iter())
+
+        # move to end cursor position or template end
+        if place_cursor:
+            self._text_buffer.place_cursor(self._text_buffer.get_iter_at_mark(self._mark_final))
+
+        self._deactivate()
+
+    @property
+    def _cursor_in_template(self):
+        """
+        @return: True if the cursor is in the template
+        """
+        itLeft = self._text_buffer.get_iter_at_mark(self._markStart)
+        itRight = self._text_buffer.get_iter_at_mark(self._markEnd)
+
+        left = itLeft.get_offset()
+        right = itRight.get_offset()
+        offset = self._text_buffer.get_iter_at_mark(self._text_buffer.get_insert()).get_offset()
+
+        if offset < left or offset > right:
+            return False
+        return True
+
+    def destroy(self):
+        self._log.debug("destroy")
+
+        # remove tags
+        table = self._text_buffer.get_tag_table()
+        table.remove(self._tag_template)
+        table.remove(self._tag_placeholder)
+
+        # unreference editor
+        del self._editor
+
+        # deactivate
+        if self._active:
+            self._deactivate()
+
+
+
diff --git a/latex/base/windowactivatable.py b/latex/base/windowactivatable.py
index 94cfedd..d1a49b8 100644
--- a/latex/base/windowactivatable.py
+++ b/latex/base/windowactivatable.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -26,7 +26,7 @@ from gi.repository import Gedit, GObject, Gio, Gtk, PeasGtk
 import logging
 import string
 
-logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s	%(name)s - %(message)s")
+logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s    %(name)s - %(message)s")
 
 from ..preferences import Preferences
 from ..preferences.dialog import PreferencesDialog
@@ -38,599 +38,599 @@ from . import File, SideView, BottomView, WindowContext
 from decorators import GeditTabDecorator
 
 class LaTeXWindowActivatable(GObject.Object, Gedit.WindowActivatable, PeasGtk.Configurable):
-	__gtype_name__ =  "LaTeXWindowActivatable"
-
-	"""
-	This class
-	 - manages the GeditTabDecorators
-	 - hooks the actions as menu items and tool items
-	 - installs side and bottom panel views
-	"""
-
-	window = GObject.property(type=Gedit.Window)
-
-	_log = logging.getLogger("LaTeXWindowActivatable")
-
-	# ui definition template for hooking tools in Gedit's ui
-	_tool_ui_template = string.Template("""<ui>
-			<menubar name="MenuBar">
-				<menu name="ToolsMenu" action="Tools">
-					<placeholder name="ToolsOps_1">$items</placeholder>
-				</menu>
-			</menubar>
-		</ui>""")
-
-	def __init__(self):
-		GObject.Object.__init__(self)
-
-	def do_activate(self):
-		"""
-		Called when the window extension is activated
-		"""
-		self._preferences = Preferences()
-		self._tool_preferences = ToolPreferences()
-		self._tool_preferences.connect("tools-changed", self._on_tools_changed)
-		
-		#
-		# initialize context object
-		#
-		self._window_context = WindowContext(self, EDITOR_SCOPE_VIEWS)
-		
-		# the order is important!
-		self._init_actions()
-		self._init_tool_actions()
-		self._init_views()
-		self._init_tab_decorators()
-		
-		# FIXME: find another way to save a document
-		self._save_action = self._ui_manager.get_action("/MenuBar/FileMenu/FileSaveMenu")
-		
-		#
-		# listen to tab signals
-		#
-		self._signal_handlers = [
-				self.window.connect("tab_added", self._on_tab_added),
-				self.window.connect("tab_removed", self._on_tab_removed),
-				self.window.connect("active_tab_changed", self._on_active_tab_changed) ]
-
-	def do_deactivate(self):
-		"""
-		Called when the window extension is deactivated
-		"""
-		# save preferences and stop listening
-		self._preferences.save()
-		self._tool_preferences.save()
-		
-		# destroy tab decorators
-		self._active_tab_decorator = None
-		for tab in self._tab_decorators:
-			self._tab_decorators[tab].destroy()
-		self._tab_decorators = {}
-
-		# disconnect from tab signals
-		for handler in self._signal_handlers:
-			self.window.disconnect(handler)
-		del self._signal_handlers
-
-		# remove all views
-		self.disable()
-		
-		# destroy all window scope views
-		# (the editor scope views are destroyed by the editor)
-		for i in self._window_context.window_scope_views:
-			self._window_context.window_scope_views[i].destroy()
-		self._window_context.window_scope_views = {}
-		
-		# remove toolbar
-		self._toolbar.destroy()
-		
-		# remove tool actions
-		self._ui_manager.remove_ui(self._tool_ui_id)
-		for gtk_action in self._action_handlers:
-			gtk_action.disconnect(self._action_handlers[gtk_action])
-			self._tool_action_group.remove_action(gtk_action)
-		self._ui_manager.remove_action_group(self._tool_action_group)
-		
-		# remove actions
-		self._ui_manager.remove_ui(self._ui_id)
-		for clazz in self._action_objects:
-			self._action_objects[clazz].unhook(self._action_group)
-		self._ui_manager.remove_action_group(self._action_group)
-		
-		# destroy the window context
-		self._window_context.destroy()
-		del self._window_context
-
-	def do_create_configure_widget(self):
-		return PreferencesDialog().dialog
-
-	def _init_views(self):
-		"""
-		"""
-		
-		# selection states for each TabDecorator
-		self._selected_bottom_views = {}
-		self._selected_side_views = {}
-		
-		# currently hooked editor-scope views
-		self._side_views = []
-		self._bottom_views = []
-		
-		# currently hooked window-scope views
-		self._window_side_views = []
-		self._window_bottom_views = []
-	
-		# caches window-scope View instances
-		self._views = {}
-		
-		#
-		# init the ToolView, it's always present
-		#
-		# TODO: position is ignored
-		# 
-		tool_view = ToolView(self._window_context)
-		self._views["ToolView"] = tool_view
-		#fixme put the id!
-		bottom_panel = self.window.get_bottom_panel()
-		bottom_panel.add_item(tool_view, "ToolViewid", tool_view.label, tool_view.icon)
-		#self._window_bottom_views.append(tool_view)
-		
-		# update window context
-		self._window_context.window_scope_views = self._views
-
-	def _init_actions(self):
-		"""
-		Merge the plugin's UI definition with the one of Gedit and hook the actions
-		"""
-		self._ui_manager = self.window.get_ui_manager()
-		self._action_group = Gtk.ActionGroup("LaTeXWindowActivatableActions")
-		self._icon_factory = Gtk.IconFactory()
-		self._icon_factory.add_default()
-		
-		# create action instances, hook them and build up some
-		# hash tables
-		
-		self._action_objects = {}		# name -> Action object
-		self._action_extensions = {}	# extension -> action names
-		
-		for clazz in ACTIONS:
-			action = clazz(icon_factory=self._icon_factory)
-			action.hook(self._action_group, self._window_context)
-			
-			self._action_objects[clazz.__name__] = action
-			
-			for extension in action.extensions:
-				if extension in self._action_extensions.keys():
-					self._action_extensions[extension].append(clazz.__name__)
-				else:
-					self._action_extensions[extension] = [clazz.__name__]
-		
-		# merge ui
-		self._ui_manager.insert_action_group(self._action_group, -1)
-		self._ui_id = self._ui_manager.add_ui_from_string(UI)
-		
-		# hook the toolbar
-		self._toolbar = self._ui_manager.get_widget("/LaTeXToolbar")
-		self._toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)
-		
-		self._main_box = self.window.get_children()[0]
-		self._main_box.pack_start(self._toolbar, False, True, 0)
-		self._main_box.reorder_child(self._toolbar, 2)
-		
-	def _init_tab_decorators(self):
-		"""
-		Look for already open tabs and create decorators for them
-		"""
-		self._tab_decorators = {}
-		self._active_tab_decorator = None
-		active_view = self.window.get_active_view()
-		views = self.window.get_views()
-
-		for view in views:
-			tab = Gedit.Tab.get_from_document(view.get_buffer())
-			decorator = self._create_tab_decorator(tab, init=True)
-			if view is active_view:
-				self._active_tab_decorator = decorator
-		
-		self._log.debug("_init_tab_decorators: initialized %s decorators" % len(views))
-		
-		if len(views) > 0 and not self._active_tab_decorator:
-			self._log.warning("_init_tab_decorators: no active decorator found")
-	
-	def _init_tool_actions(self):
-		"""
-		 - Load defined Tools
-		 - create and init ToolActions from them
-		 - hook them in the window UI
-		 - create a map from extensions to lists of ToolActions
-		"""
-		
-		# add a MenuToolButton with the tools menu to the toolbar afterwards
-		# FIXME: this is quite hacky
-		menu = Gtk.Menu()
-		
-		# this is used for enable/disable actions by name
-		# None stands for every extension
-		self._tool_action_extensions = { None : [] }
-		
-		self._tool_action_group = Gtk.ActionGroup("LaTeXPluginToolActions")
-			
-		items_ui = ""
-		
-		self._action_handlers = {}
-		
-		i = 1					# counting tool actions
-		accel_counter = 1		# counting tool actions without custom accel
-		for tool in self._tool_preferences.tools:
-			# hopefully unique action name
-			name = "Tool%sAction" % i
-			
-			# update extension-tool mapping
-			for extension in tool.extensions:
-				try:
-					self._tool_action_extensions[extension].append(name)
-				except KeyError:
-					# extension not yet mapped
-					self._tool_action_extensions[extension] = [name]
-			
-			# create action
-			action = ToolAction(tool)
-			gtk_action = Gtk.Action(name, action.label, action.tooltip, action.stock_id)
-			self._action_handlers[gtk_action] = gtk_action.connect("activate", lambda gtk_action, action: action.activate(self._window_context), action)
-			
-			if not tool.accelerator is None and len(tool.accelerator) > 0:
-				# TODO: validate accelerator!
-				self._tool_action_group.add_action_with_accel(gtk_action, tool.accelerator)
-			else:
-				self._tool_action_group.add_action_with_accel(gtk_action, "<Ctrl><Alt>%s" % accel_counter)
-				accel_counter += 1
-			
-			# add to MenuToolBar menu
-			# FIXME: GtkWarning: gtk_accel_label_set_accel_closure: assertion `gtk_accel_group_from_accel_closure (accel_closure) != NULL' failed
-			menu.add(gtk_action.create_menu_item())
-			
-			# add UI definition
-			items_ui += """<menuitem action="%s" />""" % name
-			
-			i += 1
-		
-		tool_ui = self._tool_ui_template.substitute({"items" : items_ui})
-		
-		self._ui_manager.insert_action_group(self._tool_action_group, -1)
-		self._tool_ui_id = self._ui_manager.add_ui_from_string(tool_ui)
-		
-		# add a MenuToolButton with the tools menu to the toolbar
-		self._menu_tool_button = Gtk.MenuToolButton.new_from_stock(Gtk.STOCK_CONVERT)
-		self._menu_tool_button.set_menu(menu)
-		self._menu_tool_button.show_all()
-		self._toolbar.insert(self._menu_tool_button, -1)
-	
-	def save_file(self):
-		"""
-		Trigger the 'Save' action
-		
-		(used by ToolAction before tool run)
-		"""
-		self._save_action.activate()
-	
-	def _on_tools_changed(self):
-		self._log.debug("_on_tools_changed")
-		
-		# remove tool actions and ui
-		self._ui_manager.remove_ui(self._tool_ui_id)
-		for gtk_action in self._action_handlers:
-			gtk_action.disconnect(self._action_handlers[gtk_action])
-			self._tool_action_group.remove_action(gtk_action)
-		self._ui_manager.remove_action_group(self._tool_action_group)
-
-		# remove MenuToolButton
-		self._toolbar.remove(self._menu_tool_button)
-		
-		# re-init tool actions
-		self._init_tool_actions()
-		
-		# re-adjust action states
-		self.adjust(self._active_tab_decorator)
-	
-	def activate_tab(self, file):
-		"""
-		Activate the GeditTab containing the given File or open a new
-		tab for it (this is called by the WindowContext)
-		
-		@param file: a File object
-		"""
-		for tab, tab_decorator in self._tab_decorators.iteritems():
-			if tab_decorator.file and tab_decorator.file == file:
-				self.window.set_active_tab(tab)
-				return
-		
-		# not found, open file in a new tab...
-
-		uri = file.uri
-		gfile = Gio.file_new_for_uri(uri)
-
-		if Gedit.utils_is_valid_location(gfile):
-			self._log.debug("GeditWindow.create_tab_from_uri(%s)" % uri)
-			self.window.create_tab_from_location(
-							gfile, Gedit.encoding_get_current(),
-							1, 1, False, True)
-		else:
-			self._log.error("Gedit.utils.uri_is_valid(%s) = False" % uri)
-	
-	def disable(self):
-		"""
-		Called if there are no more tabs after tab_removed
-		"""
-		self._toolbar.hide()
-		
-		# disable all actions
-		for name in self._action_objects.iterkeys():
-			self._action_group.get_action(name).set_visible(False)
-			
-		# disable all tool actions
-		for l in self._tool_action_extensions.values():
-			for name in l:
-				self._tool_action_group.get_action(name).set_sensitive(False)
-				
-		# remove all side views
-		side_views = self._window_side_views + self._side_views
-		for view in side_views:
-			self.window.get_side_panel().remove_item(view)
-			if view in self._side_views: self._side_views.remove(view)
-			if view in self._window_side_views: self._window_side_views.remove(view)
-			
-		# remove all bottom views
-		bottom_views = self._window_bottom_views + self._bottom_views
-		for view in bottom_views:
-			self.window.get_bottom_panel().remove_item(view)
-			if view in self._bottom_views: self._bottom_views.remove(view)
-			if view in self._window_bottom_views: self._window_bottom_views.remove(view)
-	
-	def adjust(self, tab_decorator):
-		"""
-		Adjust actions and views according to the currently active TabDecorator
-		(the file type it contains)
-		
-		Called by 
-		 * _on_active_tab_changed()
-		 * GeditTabDecorator when the Editor instance changes 
-		"""
-
-		# TODO: improve and simplify this!
-		
-		extension = tab_decorator.extension
-		
-		self._log.debug("---------- ADJUST: %s" % (extension))
-		
-		# FIXME: a hack again...
-		# the toolbar should hide when it doesn't contain any visible items
-		latex_extensions = self._preferences.get("latex-extensions").split(",")
-		show_toolbar = self._preferences.get_bool("show-latex-toolbar")
-		if show_toolbar and extension in latex_extensions:
-			self._toolbar.show()
-		else:
-			self._toolbar.hide()
-		
-		#
-		# adjust actions
-		#
-		# FIXME: we always get the state of the new decorator after tab change
-		# but we need to save the one of the old decorator
-		#
-		# FIXME: we are dealing with sets so saving the index as selection state
-		# is nonsense
-		#
-		
-		# disable all actions
-		for name in self._action_objects:
-			self._action_group.get_action(name).set_visible(False)
-		
-		# disable all tool actions
-		for l in self._tool_action_extensions.values():
-			for name in l:
-				self._tool_action_group.get_action(name).set_sensitive(False)
-		
-		
-		# enable the actions for all extensions
-		for name in self._action_extensions[None]:
-			self._action_group.get_action(name).set_visible(True)
-		
-		# enable the actions registered for the extension
-		if extension:
-			try:
-				for name in self._action_extensions[extension]:
-					self._action_group.get_action(name).set_visible(True)
-			except KeyError:
-				pass
-		
-		
-		# enable the tool actions that apply for all extensions
-		for name in self._tool_action_extensions[None]:
-			self._tool_action_group.get_action(name).set_sensitive(True)
-		
-		# enable the tool actions that apply for this extension
-		if extension:
-			try:
-				for name in self._tool_action_extensions[extension]:
-					self._tool_action_group.get_action(name).set_sensitive(True)
-			except KeyError:
-				pass
-		
-		#
-		# adjust editor-scope views
-		#
-		
-		# determine set of side/bottom views BEFORE
-		
-		before_side_views = set(self._side_views)
-		before_bottom_views = set(self._bottom_views)
-		
-		# determine set of side/bottom views AFTER
-		
-		after_side_views = set()
-		after_bottom_views = set()
-		
-		if tab_decorator.editor:
-			editor_views = self._window_context.editor_scope_views[tab_decorator.editor]
-			for id, view in editor_views.iteritems():
-				if isinstance(view, BottomView):
-					after_bottom_views.add(view)
-				elif isinstance(view, SideView):
-					after_side_views.add(view)
-				else:
-					raise RuntimeError("Invalid view type: %s" % view)
-		
-		# remove BEFORE.difference(AFTER)
-		for view in before_side_views.difference(after_side_views):
-			self.window.get_side_panel().remove_item(view)
-			self._side_views.remove(view)
-		
-		for view in before_bottom_views.difference(after_bottom_views):
-			self.window.get_bottom_panel().remove_item(view)
-			self._bottom_views.remove(view)
-		
-		# add AFTER.difference(BEFORE)
-		i = 1
-		for view in after_side_views.difference(before_side_views):
-			i+=1
-			self.window.get_side_panel().add_item(view, "after_side_view_id" + str(i), view.label, view.icon)
-			self._side_views.append(view)
-		i = 1	
-		for view in after_bottom_views.difference(before_bottom_views):
-			i+=1
-			print view.label, view.icon
-			self.window.get_bottom_panel().add_item(view, "bottom_view_id" + str(i),view.label, view.icon)
-			self._bottom_views.append(view)
-			
-		
-		#
-		# adjust window-scope views
-		#
-		
-		# determine set of side/bottom views BEFORE
-		
-		before_window_side_views = set(self._window_side_views)
-		before_window_bottom_views = set(self._window_bottom_views)
-		
-		# determine set of side/bottom views AFTER
-		
-		after_window_side_views = set()
-		after_window_bottom_views = set()
-		
-		try:
-			for id, clazz in WINDOW_SCOPE_VIEWS[extension].iteritems():
-				
-				# find or create View instance
-				view = None
-				try:
-					view = self._views[id]
-				except KeyError:
-					view = clazz.__new__(clazz)
-					clazz.__init__(view, self._window_context)
-					self._views[id] = view
-				
-				if isinstance(view, BottomView):
-					after_window_bottom_views.add(view)
-				elif isinstance(view, SideView):
-					after_window_side_views.add(view)
-				else:
-					raise RuntimeError("Invalid view type: %s" % view)
-		except KeyError:
-			self._log.debug("No window-scope views for this extension")
-			
-		# remove BEFORE.difference(AFTER)
-		for view in before_window_side_views.difference(after_window_side_views):
-			self.window.get_side_panel().remove_item(view)
-			self._window_side_views.remove(view)
-		
-		for view in before_window_bottom_views.difference(after_window_bottom_views):
-			self.window.get_bottom_panel().remove_item(view)
-			self._window_bottom_views.remove(view)
-			
-		# add AFTER.difference(BEFORE)
-		i = 1
-		for view in after_window_side_views.difference(before_window_side_views):
-			i += 1
-			self.window.get_side_panel().add_item(view,"WHATView"+ str(i), view.label, view.icon)
-			self._window_side_views.append(view)
-			
-		for view in after_window_bottom_views.difference(before_window_bottom_views):
-			self.window.get_bottom_panel().add_item(view, view.label, view.icon)
-			self._window_bottom_views.append(view)
-		
-		#
-		# update window context
-		#
-		self._window_context.window_scope_views = self._views
-	
-	def _on_tab_added(self, window, tab):
-		"""
-		A new tab has been added
-		
-		@param window: Gedit.Window object
-		@param tab: Gedit.Tab object
-		"""
-		self._log.debug("tab_added")
-		
-		if tab in self._tab_decorators:
-			self._log.warning("There is already a decorator for tab %s" % tab)
-			return
-		
-		self._create_tab_decorator(tab)
-			
-	def _on_tab_removed(self, window, tab):
-		"""
-		A tab has been closed
-		
-		@param window: GeditWindow
-		@param tab: the closed GeditTab
-		"""
-		self._log.debug("tab_removed")
-		
-		# As we don't call GeditWindowDecorator.adjust() if the new 
-		# tab is not the active one (for example, when opening several 
-		# files at once, see GeditTabDecorator._adjust_editor()), 
-		# it may happen that self._selected_side_views[tab] is not set.
-		if self._tab_decorators[tab] in self._selected_side_views:
-			del self._selected_side_views[self._tab_decorators[tab]]
-		if self._tab_decorators[tab] in self._selected_bottom_views:
-			del self._selected_bottom_views[self._tab_decorators[tab]]
-		
-		self._tab_decorators[tab].destroy()
-		if self._active_tab_decorator == self._tab_decorators[tab]:
-			self._active_tab_decorator = None
-
-		del self._tab_decorators[tab]
-		
-		if len(self._tab_decorators) == 0:
-			# no more tabs
-			self.disable()
-	
-	def _on_active_tab_changed(self, window, tab):
-		"""
-		The active tab has changed
-		
-		@param window: the GeditWindow
-		@param tab: the activated GeditTab
-		"""
-		self._log.debug("active_tab_changed")
-		
-		if tab in self._tab_decorators.keys():
-			decorator = self._tab_decorators[tab]
-		else:
-			# (on Gedit startup 'tab-changed' comes before 'tab-added')
-			# remember: init=True crashes the plugin here!
-			decorator = self._create_tab_decorator(tab)
-		
-		self._active_tab_decorator = decorator
-		
-		# adjust actions and views
-		self.adjust(decorator)
-	
-	def _create_tab_decorator(self, tab, init=False):
-		"""
-		Create a new GeditTabDecorator for a GeditTab
-		"""
-		decorator = GeditTabDecorator(self, tab, init)
-		self._tab_decorators[tab] = decorator
-		return decorator
+    __gtype_name__ =  "LaTeXWindowActivatable"
+
+    """
+    This class
+     - manages the GeditTabDecorators
+     - hooks the actions as menu items and tool items
+     - installs side and bottom panel views
+    """
+
+    window = GObject.property(type=Gedit.Window)
+
+    _log = logging.getLogger("LaTeXWindowActivatable")
+
+    # ui definition template for hooking tools in Gedit's ui
+    _tool_ui_template = string.Template("""<ui>
+            <menubar name="MenuBar">
+                <menu name="ToolsMenu" action="Tools">
+                    <placeholder name="ToolsOps_1">$items</placeholder>
+                </menu>
+            </menubar>
+        </ui>""")
+
+    def __init__(self):
+        GObject.Object.__init__(self)
+
+    def do_activate(self):
+        """
+        Called when the window extension is activated
+        """
+        self._preferences = Preferences()
+        self._tool_preferences = ToolPreferences()
+        self._tool_preferences.connect("tools-changed", self._on_tools_changed)
+
+        #
+        # initialize context object
+        #
+        self._window_context = WindowContext(self, EDITOR_SCOPE_VIEWS)
+
+        # the order is important!
+        self._init_actions()
+        self._init_tool_actions()
+        self._init_views()
+        self._init_tab_decorators()
+
+        # FIXME: find another way to save a document
+        self._save_action = self._ui_manager.get_action("/MenuBar/FileMenu/FileSaveMenu")
+
+        #
+        # listen to tab signals
+        #
+        self._signal_handlers = [
+                self.window.connect("tab_added", self._on_tab_added),
+                self.window.connect("tab_removed", self._on_tab_removed),
+                self.window.connect("active_tab_changed", self._on_active_tab_changed) ]
+
+    def do_deactivate(self):
+        """
+        Called when the window extension is deactivated
+        """
+        # save preferences and stop listening
+        self._preferences.save()
+        self._tool_preferences.save()
+
+        # destroy tab decorators
+        self._active_tab_decorator = None
+        for tab in self._tab_decorators:
+            self._tab_decorators[tab].destroy()
+        self._tab_decorators = {}
+
+        # disconnect from tab signals
+        for handler in self._signal_handlers:
+            self.window.disconnect(handler)
+        del self._signal_handlers
+
+        # remove all views
+        self.disable()
+
+        # destroy all window scope views
+        # (the editor scope views are destroyed by the editor)
+        for i in self._window_context.window_scope_views:
+            self._window_context.window_scope_views[i].destroy()
+        self._window_context.window_scope_views = {}
+
+        # remove toolbar
+        self._toolbar.destroy()
+
+        # remove tool actions
+        self._ui_manager.remove_ui(self._tool_ui_id)
+        for gtk_action in self._action_handlers:
+            gtk_action.disconnect(self._action_handlers[gtk_action])
+            self._tool_action_group.remove_action(gtk_action)
+        self._ui_manager.remove_action_group(self._tool_action_group)
+
+        # remove actions
+        self._ui_manager.remove_ui(self._ui_id)
+        for clazz in self._action_objects:
+            self._action_objects[clazz].unhook(self._action_group)
+        self._ui_manager.remove_action_group(self._action_group)
+
+        # destroy the window context
+        self._window_context.destroy()
+        del self._window_context
+
+    def do_create_configure_widget(self):
+        return PreferencesDialog().dialog
+
+    def _init_views(self):
+        """
+        """
+
+        # selection states for each TabDecorator
+        self._selected_bottom_views = {}
+        self._selected_side_views = {}
+
+        # currently hooked editor-scope views
+        self._side_views = []
+        self._bottom_views = []
+
+        # currently hooked window-scope views
+        self._window_side_views = []
+        self._window_bottom_views = []
+
+        # caches window-scope View instances
+        self._views = {}
+
+        #
+        # init the ToolView, it's always present
+        #
+        # TODO: position is ignored
+        #
+        tool_view = ToolView(self._window_context)
+        self._views["ToolView"] = tool_view
+        #fixme put the id!
+        bottom_panel = self.window.get_bottom_panel()
+        bottom_panel.add_item(tool_view, "ToolViewid", tool_view.label, tool_view.icon)
+        #self._window_bottom_views.append(tool_view)
+
+        # update window context
+        self._window_context.window_scope_views = self._views
+
+    def _init_actions(self):
+        """
+        Merge the plugin's UI definition with the one of Gedit and hook the actions
+        """
+        self._ui_manager = self.window.get_ui_manager()
+        self._action_group = Gtk.ActionGroup("LaTeXWindowActivatableActions")
+        self._icon_factory = Gtk.IconFactory()
+        self._icon_factory.add_default()
+
+        # create action instances, hook them and build up some
+        # hash tables
+
+        self._action_objects = {}        # name -> Action object
+        self._action_extensions = {}    # extension -> action names
+
+        for clazz in ACTIONS:
+            action = clazz(icon_factory=self._icon_factory)
+            action.hook(self._action_group, self._window_context)
+
+            self._action_objects[clazz.__name__] = action
+
+            for extension in action.extensions:
+                if extension in self._action_extensions.keys():
+                    self._action_extensions[extension].append(clazz.__name__)
+                else:
+                    self._action_extensions[extension] = [clazz.__name__]
+
+        # merge ui
+        self._ui_manager.insert_action_group(self._action_group, -1)
+        self._ui_id = self._ui_manager.add_ui_from_string(UI)
+
+        # hook the toolbar
+        self._toolbar = self._ui_manager.get_widget("/LaTeXToolbar")
+        self._toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)
+
+        self._main_box = self.window.get_children()[0]
+        self._main_box.pack_start(self._toolbar, False, True, 0)
+        self._main_box.reorder_child(self._toolbar, 2)
+
+    def _init_tab_decorators(self):
+        """
+        Look for already open tabs and create decorators for them
+        """
+        self._tab_decorators = {}
+        self._active_tab_decorator = None
+        active_view = self.window.get_active_view()
+        views = self.window.get_views()
+
+        for view in views:
+            tab = Gedit.Tab.get_from_document(view.get_buffer())
+            decorator = self._create_tab_decorator(tab, init=True)
+            if view is active_view:
+                self._active_tab_decorator = decorator
+
+        self._log.debug("_init_tab_decorators: initialized %s decorators" % len(views))
+
+        if len(views) > 0 and not self._active_tab_decorator:
+            self._log.warning("_init_tab_decorators: no active decorator found")
+
+    def _init_tool_actions(self):
+        """
+         - Load defined Tools
+         - create and init ToolActions from them
+         - hook them in the window UI
+         - create a map from extensions to lists of ToolActions
+        """
+
+        # add a MenuToolButton with the tools menu to the toolbar afterwards
+        # FIXME: this is quite hacky
+        menu = Gtk.Menu()
+
+        # this is used for enable/disable actions by name
+        # None stands for every extension
+        self._tool_action_extensions = { None : [] }
+
+        self._tool_action_group = Gtk.ActionGroup("LaTeXPluginToolActions")
+
+        items_ui = ""
+
+        self._action_handlers = {}
+
+        i = 1                    # counting tool actions
+        accel_counter = 1        # counting tool actions without custom accel
+        for tool in self._tool_preferences.tools:
+            # hopefully unique action name
+            name = "Tool%sAction" % i
+
+            # update extension-tool mapping
+            for extension in tool.extensions:
+                try:
+                    self._tool_action_extensions[extension].append(name)
+                except KeyError:
+                    # extension not yet mapped
+                    self._tool_action_extensions[extension] = [name]
+
+            # create action
+            action = ToolAction(tool)
+            gtk_action = Gtk.Action(name, action.label, action.tooltip, action.stock_id)
+            self._action_handlers[gtk_action] = gtk_action.connect("activate", lambda gtk_action, action: action.activate(self._window_context), action)
+
+            if not tool.accelerator is None and len(tool.accelerator) > 0:
+                # TODO: validate accelerator!
+                self._tool_action_group.add_action_with_accel(gtk_action, tool.accelerator)
+            else:
+                self._tool_action_group.add_action_with_accel(gtk_action, "<Ctrl><Alt>%s" % accel_counter)
+                accel_counter += 1
+
+            # add to MenuToolBar menu
+            # FIXME: GtkWarning: gtk_accel_label_set_accel_closure: assertion `gtk_accel_group_from_accel_closure (accel_closure) != NULL' failed
+            menu.add(gtk_action.create_menu_item())
+
+            # add UI definition
+            items_ui += """<menuitem action="%s" />""" % name
+
+            i += 1
+
+        tool_ui = self._tool_ui_template.substitute({"items" : items_ui})
+
+        self._ui_manager.insert_action_group(self._tool_action_group, -1)
+        self._tool_ui_id = self._ui_manager.add_ui_from_string(tool_ui)
+
+        # add a MenuToolButton with the tools menu to the toolbar
+        self._menu_tool_button = Gtk.MenuToolButton.new_from_stock(Gtk.STOCK_CONVERT)
+        self._menu_tool_button.set_menu(menu)
+        self._menu_tool_button.show_all()
+        self._toolbar.insert(self._menu_tool_button, -1)
+
+    def save_file(self):
+        """
+        Trigger the 'Save' action
+
+        (used by ToolAction before tool run)
+        """
+        self._save_action.activate()
+
+    def _on_tools_changed(self):
+        self._log.debug("_on_tools_changed")
+
+        # remove tool actions and ui
+        self._ui_manager.remove_ui(self._tool_ui_id)
+        for gtk_action in self._action_handlers:
+            gtk_action.disconnect(self._action_handlers[gtk_action])
+            self._tool_action_group.remove_action(gtk_action)
+        self._ui_manager.remove_action_group(self._tool_action_group)
+
+        # remove MenuToolButton
+        self._toolbar.remove(self._menu_tool_button)
+
+        # re-init tool actions
+        self._init_tool_actions()
+
+        # re-adjust action states
+        self.adjust(self._active_tab_decorator)
+
+    def activate_tab(self, file):
+        """
+        Activate the GeditTab containing the given File or open a new
+        tab for it (this is called by the WindowContext)
+
+        @param file: a File object
+        """
+        for tab, tab_decorator in self._tab_decorators.iteritems():
+            if tab_decorator.file and tab_decorator.file == file:
+                self.window.set_active_tab(tab)
+                return
+
+        # not found, open file in a new tab...
+
+        uri = file.uri
+        gfile = Gio.file_new_for_uri(uri)
+
+        if Gedit.utils_is_valid_location(gfile):
+            self._log.debug("GeditWindow.create_tab_from_uri(%s)" % uri)
+            self.window.create_tab_from_location(
+                            gfile, Gedit.encoding_get_current(),
+                            1, 1, False, True)
+        else:
+            self._log.error("Gedit.utils.uri_is_valid(%s) = False" % uri)
+
+    def disable(self):
+        """
+        Called if there are no more tabs after tab_removed
+        """
+        self._toolbar.hide()
+
+        # disable all actions
+        for name in self._action_objects.iterkeys():
+            self._action_group.get_action(name).set_visible(False)
+
+        # disable all tool actions
+        for l in self._tool_action_extensions.values():
+            for name in l:
+                self._tool_action_group.get_action(name).set_sensitive(False)
+
+        # remove all side views
+        side_views = self._window_side_views + self._side_views
+        for view in side_views:
+            self.window.get_side_panel().remove_item(view)
+            if view in self._side_views: self._side_views.remove(view)
+            if view in self._window_side_views: self._window_side_views.remove(view)
+
+        # remove all bottom views
+        bottom_views = self._window_bottom_views + self._bottom_views
+        for view in bottom_views:
+            self.window.get_bottom_panel().remove_item(view)
+            if view in self._bottom_views: self._bottom_views.remove(view)
+            if view in self._window_bottom_views: self._window_bottom_views.remove(view)
+
+    def adjust(self, tab_decorator):
+        """
+        Adjust actions and views according to the currently active TabDecorator
+        (the file type it contains)
+
+        Called by
+         * _on_active_tab_changed()
+         * GeditTabDecorator when the Editor instance changes
+        """
+
+        # TODO: improve and simplify this!
+
+        extension = tab_decorator.extension
+
+        self._log.debug("---------- ADJUST: %s" % (extension))
+
+        # FIXME: a hack again...
+        # the toolbar should hide when it doesn't contain any visible items
+        latex_extensions = self._preferences.get("latex-extensions").split(",")
+        show_toolbar = self._preferences.get_bool("show-latex-toolbar")
+        if show_toolbar and extension in latex_extensions:
+            self._toolbar.show()
+        else:
+            self._toolbar.hide()
+
+        #
+        # adjust actions
+        #
+        # FIXME: we always get the state of the new decorator after tab change
+        # but we need to save the one of the old decorator
+        #
+        # FIXME: we are dealing with sets so saving the index as selection state
+        # is nonsense
+        #
+
+        # disable all actions
+        for name in self._action_objects:
+            self._action_group.get_action(name).set_visible(False)
+
+        # disable all tool actions
+        for l in self._tool_action_extensions.values():
+            for name in l:
+                self._tool_action_group.get_action(name).set_sensitive(False)
+
+
+        # enable the actions for all extensions
+        for name in self._action_extensions[None]:
+            self._action_group.get_action(name).set_visible(True)
+
+        # enable the actions registered for the extension
+        if extension:
+            try:
+                for name in self._action_extensions[extension]:
+                    self._action_group.get_action(name).set_visible(True)
+            except KeyError:
+                pass
+
+
+        # enable the tool actions that apply for all extensions
+        for name in self._tool_action_extensions[None]:
+            self._tool_action_group.get_action(name).set_sensitive(True)
+
+        # enable the tool actions that apply for this extension
+        if extension:
+            try:
+                for name in self._tool_action_extensions[extension]:
+                    self._tool_action_group.get_action(name).set_sensitive(True)
+            except KeyError:
+                pass
+
+        #
+        # adjust editor-scope views
+        #
+
+        # determine set of side/bottom views BEFORE
+
+        before_side_views = set(self._side_views)
+        before_bottom_views = set(self._bottom_views)
+
+        # determine set of side/bottom views AFTER
+
+        after_side_views = set()
+        after_bottom_views = set()
+
+        if tab_decorator.editor:
+            editor_views = self._window_context.editor_scope_views[tab_decorator.editor]
+            for id, view in editor_views.iteritems():
+                if isinstance(view, BottomView):
+                    after_bottom_views.add(view)
+                elif isinstance(view, SideView):
+                    after_side_views.add(view)
+                else:
+                    raise RuntimeError("Invalid view type: %s" % view)
+
+        # remove BEFORE.difference(AFTER)
+        for view in before_side_views.difference(after_side_views):
+            self.window.get_side_panel().remove_item(view)
+            self._side_views.remove(view)
+
+        for view in before_bottom_views.difference(after_bottom_views):
+            self.window.get_bottom_panel().remove_item(view)
+            self._bottom_views.remove(view)
+
+        # add AFTER.difference(BEFORE)
+        i = 1
+        for view in after_side_views.difference(before_side_views):
+            i+=1
+            self.window.get_side_panel().add_item(view, "after_side_view_id" + str(i), view.label, view.icon)
+            self._side_views.append(view)
+        i = 1
+        for view in after_bottom_views.difference(before_bottom_views):
+            i+=1
+            print view.label, view.icon
+            self.window.get_bottom_panel().add_item(view, "bottom_view_id" + str(i),view.label, view.icon)
+            self._bottom_views.append(view)
+
+
+        #
+        # adjust window-scope views
+        #
+
+        # determine set of side/bottom views BEFORE
+
+        before_window_side_views = set(self._window_side_views)
+        before_window_bottom_views = set(self._window_bottom_views)
+
+        # determine set of side/bottom views AFTER
+
+        after_window_side_views = set()
+        after_window_bottom_views = set()
+
+        try:
+            for id, clazz in WINDOW_SCOPE_VIEWS[extension].iteritems():
+
+                # find or create View instance
+                view = None
+                try:
+                    view = self._views[id]
+                except KeyError:
+                    view = clazz.__new__(clazz)
+                    clazz.__init__(view, self._window_context)
+                    self._views[id] = view
+
+                if isinstance(view, BottomView):
+                    after_window_bottom_views.add(view)
+                elif isinstance(view, SideView):
+                    after_window_side_views.add(view)
+                else:
+                    raise RuntimeError("Invalid view type: %s" % view)
+        except KeyError:
+            self._log.debug("No window-scope views for this extension")
+
+        # remove BEFORE.difference(AFTER)
+        for view in before_window_side_views.difference(after_window_side_views):
+            self.window.get_side_panel().remove_item(view)
+            self._window_side_views.remove(view)
+
+        for view in before_window_bottom_views.difference(after_window_bottom_views):
+            self.window.get_bottom_panel().remove_item(view)
+            self._window_bottom_views.remove(view)
+
+        # add AFTER.difference(BEFORE)
+        i = 1
+        for view in after_window_side_views.difference(before_window_side_views):
+            i += 1
+            self.window.get_side_panel().add_item(view,"WHATView"+ str(i), view.label, view.icon)
+            self._window_side_views.append(view)
+
+        for view in after_window_bottom_views.difference(before_window_bottom_views):
+            self.window.get_bottom_panel().add_item(view, view.label, view.icon)
+            self._window_bottom_views.append(view)
+
+        #
+        # update window context
+        #
+        self._window_context.window_scope_views = self._views
+
+    def _on_tab_added(self, window, tab):
+        """
+        A new tab has been added
+
+        @param window: Gedit.Window object
+        @param tab: Gedit.Tab object
+        """
+        self._log.debug("tab_added")
+
+        if tab in self._tab_decorators:
+            self._log.warning("There is already a decorator for tab %s" % tab)
+            return
+
+        self._create_tab_decorator(tab)
+
+    def _on_tab_removed(self, window, tab):
+        """
+        A tab has been closed
+
+        @param window: GeditWindow
+        @param tab: the closed GeditTab
+        """
+        self._log.debug("tab_removed")
+
+        # As we don't call GeditWindowDecorator.adjust() if the new
+        # tab is not the active one (for example, when opening several
+        # files at once, see GeditTabDecorator._adjust_editor()),
+        # it may happen that self._selected_side_views[tab] is not set.
+        if self._tab_decorators[tab] in self._selected_side_views:
+            del self._selected_side_views[self._tab_decorators[tab]]
+        if self._tab_decorators[tab] in self._selected_bottom_views:
+            del self._selected_bottom_views[self._tab_decorators[tab]]
+
+        self._tab_decorators[tab].destroy()
+        if self._active_tab_decorator == self._tab_decorators[tab]:
+            self._active_tab_decorator = None
+
+        del self._tab_decorators[tab]
+
+        if len(self._tab_decorators) == 0:
+            # no more tabs
+            self.disable()
+
+    def _on_active_tab_changed(self, window, tab):
+        """
+        The active tab has changed
+
+        @param window: the GeditWindow
+        @param tab: the activated GeditTab
+        """
+        self._log.debug("active_tab_changed")
+
+        if tab in self._tab_decorators.keys():
+            decorator = self._tab_decorators[tab]
+        else:
+            # (on Gedit startup 'tab-changed' comes before 'tab-added')
+            # remember: init=True crashes the plugin here!
+            decorator = self._create_tab_decorator(tab)
+
+        self._active_tab_decorator = decorator
+
+        # adjust actions and views
+        self.adjust(decorator)
+
+    def _create_tab_decorator(self, tab, init=False):
+        """
+        Create a new GeditTabDecorator for a GeditTab
+        """
+        decorator = GeditTabDecorator(self, tab, init)
+        self._tab_decorators[tab] = decorator
+        return decorator
diff --git a/latex/bibtex/__init__.py b/latex/bibtex/__init__.py
index ed57ce0..9de43e6 100644
--- a/latex/bibtex/__init__.py
+++ b/latex/bibtex/__init__.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
diff --git a/latex/bibtex/actions.py b/latex/bibtex/actions.py
index b456cfe..ba32e6e 100644
--- a/latex/bibtex/actions.py
+++ b/latex/bibtex/actions.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -30,29 +30,29 @@ from dialogs import InsertBibTeXEntryDialog
 
 
 class BibTeXMenuAction(Action):
-	extensions = [".bib"]
-	label = "BibTeX"
-	stock_id = None
-	accelerator = None
-	tooltip = None
-	
-	def activate(self, context):
-		pass
+    extensions = [".bib"]
+    label = "BibTeX"
+    stock_id = None
+    accelerator = None
+    tooltip = None
+
+    def activate(self, context):
+        pass
 
 
 class BibTeXNewEntryAction(Action):
-	extensions = [".bib"]
-	label = "New BibTeX Entry..."
-	stock_id = None
-	accelerator = None
-	tooltip = "Create a new BibTeX entry"
-	
-	_dialog = None
-	
-	def activate(self, context):
-		if not self._dialog:
-			self._dialog = InsertBibTeXEntryDialog()
-		
-		source = self._dialog.run()
-		if not source is None:
-			context.active_editor.append(source)
\ No newline at end of file
+    extensions = [".bib"]
+    label = "New BibTeX Entry..."
+    stock_id = None
+    accelerator = None
+    tooltip = "Create a new BibTeX entry"
+
+    _dialog = None
+
+    def activate(self, context):
+        if not self._dialog:
+            self._dialog = InsertBibTeXEntryDialog()
+
+        source = self._dialog.run()
+        if not source is None:
+            context.active_editor.append(source)
\ No newline at end of file
diff --git a/latex/bibtex/cache.py b/latex/bibtex/cache.py
index 0b0e97d..635fc53 100644
--- a/latex/bibtex/cache.py
+++ b/latex/bibtex/cache.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -29,83 +29,82 @@ from ..issues import MockIssueHandler
 
 
 class BibTeXDocumentCache(object):
-	"""
-	This is used to cache BibTeX document models.
-	
-	This is only used in the context of code completion and validation for LaTeX documents
-	but not when editing a BibTeX file.
-	
-	Of course, this must be implemented as a singleton class.
-	"""
-	
-	# TODO: serialize the cache on shutdown
-	
-	_log = getLogger("BibTeXDocumentCache")
-	
-	class Entry(object):
-		"""
-		An entry in the cache
-		"""
-		_log = getLogger("bibtex.cache.BibTeXDocumentCache.Entry")
-		
-		def __init__(self, file):
-			self.__file = file
-			self.__parser = BibTeXParser(quiet=True)
-			self.__issue_handler = MockIssueHandler()
-			self.__mtime = 0
-			self.__document = None
-			
-			self.synchronize()
-		
-		@property
-		def modified(self):
-			return (self.__file.mtime > self.__mtime)
-		
-		@property
-		def document(self):
-			return self.__document
-		
-		def synchronize(self):
-			"""
-			Synchronize document model with file contents.
-			
-			This may throw OSError
-			"""
-			# update timestamp
-			self.__mtime = self.__file.mtime
-			
-			# parse
-			self.__document = self.__parser.parse(open(self.__file.path, "r").read(), self.__file, self.__issue_handler)
-	
-	def __new__(cls):
-		if not '_instance' in cls.__dict__:
-			cls._instance = object.__new__(cls)
-		return cls._instance
-	
-	def __init__(self):
-		if not '_ready' in dir(self):
-			self._entries = {}
-			self._ready = True
-	
-	def get_document(self, file):
-		"""
-		Return the (hopefully) cached document model for a given file
-		
-		@param file: a File object
-		"""
-		try:
-			# update entry if necessary
-			entry = self._entries[file.uri]
-			if entry.modified:
-				self._log.debug("File '%s' modified, synchronizing..." % file)
-				entry.synchronize()
-		except KeyError:
-			self._log.debug("Cache fault for '%s'" % file)
-			# create new entry
-			entry = self.Entry(file)
-			self._entries[file.uri] = entry
-			
-		return entry.document
-	
-	
-	
\ No newline at end of file
+    """
+    This is used to cache BibTeX document models.
+
+    This is only used in the context of code completion and validation for LaTeX documents
+    but not when editing a BibTeX file.
+
+    Of course, this must be implemented as a singleton class.
+    """
+
+    # TODO: serialize the cache on shutdown
+
+    _log = getLogger("BibTeXDocumentCache")
+
+    class Entry(object):
+        """
+        An entry in the cache
+        """
+        _log = getLogger("bibtex.cache.BibTeXDocumentCache.Entry")
+
+        def __init__(self, file):
+            self.__file = file
+            self.__parser = BibTeXParser(quiet=True)
+            self.__issue_handler = MockIssueHandler()
+            self.__mtime = 0
+            self.__document = None
+
+            self.synchronize()
+
+        @property
+        def modified(self):
+            return (self.__file.mtime > self.__mtime)
+
+        @property
+        def document(self):
+            return self.__document
+
+        def synchronize(self):
+            """
+            Synchronize document model with file contents.
+
+            This may throw OSError
+            """
+            # update timestamp
+            self.__mtime = self.__file.mtime
+
+            # parse
+            self.__document = self.__parser.parse(open(self.__file.path, "r").read(), self.__file, self.__issue_handler)
+
+    def __new__(cls):
+        if not '_instance' in cls.__dict__:
+            cls._instance = object.__new__(cls)
+        return cls._instance
+
+    def __init__(self):
+        if not '_ready' in dir(self):
+            self._entries = {}
+            self._ready = True
+
+    def get_document(self, file):
+        """
+        Return the (hopefully) cached document model for a given file
+
+        @param file: a File object
+        """
+        try:
+            # update entry if necessary
+            entry = self._entries[file.uri]
+            if entry.modified:
+                self._log.debug("File '%s' modified, synchronizing..." % file)
+                entry.synchronize()
+        except KeyError:
+            self._log.debug("Cache fault for '%s'" % file)
+            # create new entry
+            entry = self.Entry(file)
+            self._entries[file.uri] = entry
+
+        return entry.document
+
+
diff --git a/latex/bibtex/completion.py b/latex/bibtex/completion.py
index 2e40470..e22ef01 100644
--- a/latex/bibtex/completion.py
+++ b/latex/bibtex/completion.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -34,93 +34,93 @@ from parser import BibTeXParser
 
 
 class BibTeXEntryTypeProposal(Proposal):
-	"""
-	"""
-	icon = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/document.png"))
-	
-	_color = Preferences().get("light-foreground-color")
-	
-	def __init__(self, overlap, type):
-		"""
-		@param overlap: the number of overlapping characters
-		@param type: an EntryType
-		"""
-		self._overlap = overlap
-		self._type = type
-		self._details = None
-		self._source = None
-	
-	def _generate(self):
-		"""
-		Generate Template and details string
-		"""
-		template = "@%s{${Identifier}" % self._type.name
-		self._details = "@%s{<span color='%s'>Identifier</span>" % (self._type.name, self._color)
-		for field in self._type.required_fields:
-			template += ",\n\t%s = {${%s}}" % (field.name, field.label)
-			self._details += ",\n\t%s = {<span color='%s'>%s</span>}" % (field.name, self._color, field.label)
-		template += "\n}"
-		self._details += "\n}"
-		self._source = Template(template)
-	
-	@property
-	def source(self):
-		if not self._source:
-			self._generate()
-		return self._source
-	
-	@property
-	def label(self):
-		return self._type.name
-	
-	@property
-	def details(self):
-		if not self._details:
-			self._generate()
-		return self._details
-	
-	@property
-	def overlap(self):
-		return self._overlap
+    """
+    """
+    icon = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/document.png"))
+
+    _color = Preferences().get("light-foreground-color")
+
+    def __init__(self, overlap, type):
+        """
+        @param overlap: the number of overlapping characters
+        @param type: an EntryType
+        """
+        self._overlap = overlap
+        self._type = type
+        self._details = None
+        self._source = None
+
+    def _generate(self):
+        """
+        Generate Template and details string
+        """
+        template = "@%s{${Identifier}" % self._type.name
+        self._details = "@%s{<span color='%s'>Identifier</span>" % (self._type.name, self._color)
+        for field in self._type.required_fields:
+            template += ",\n\t%s = {${%s}}" % (field.name, field.label)
+            self._details += ",\n\t%s = {<span color='%s'>%s</span>}" % (field.name, self._color, field.label)
+        template += "\n}"
+        self._details += "\n}"
+        self._source = Template(template)
+
+    @property
+    def source(self):
+        if not self._source:
+            self._generate()
+        return self._source
+
+    @property
+    def label(self):
+        return self._type.name
+
+    @property
+    def details(self):
+        if not self._details:
+            self._generate()
+        return self._details
+
+    @property
+    def overlap(self):
+        return self._overlap
 
 
 class BibTeXCompletionHandler(ICompletionHandler):
-	"""
-	This implements the BibTeX-specific code completion
-	"""
-	_log = getLogger("BibTeXCompletionHandler")
-	
-	trigger_keys = ["@"]
-	prefix_delimiters = ["@"]
-	
-	def __init__(self):
-		self._model = BibTeXModel()
-		self._parser = BibTeXParser()
-		self._issue_handler = MockIssueHandler()
-	
-	def complete(self, prefix):
-		self._log.debug("complete: '%s'" % prefix)
-		
-		proposals = []
-		
-		if len(prefix) == 1:
-			# propose all entry types
-			types = self._model.types
-			proposals = [BibTeXEntryTypeProposal(1, type) for type in types]
-		else:
-			if prefix[1:].isalpha():
-				type_name_prefix = prefix[1:].lower()
-				overlap = len(type_name_prefix) + 1
-				# prefix is @[a-zA-Z]+
-				types = [type for type in self._model.types if type.name.lower().startswith(type_name_prefix)]
-				proposals = [BibTeXEntryTypeProposal(overlap, type) for type in types]
-			else:
-				# parse prefix
-#				document = self._parser.parse(prefix, None, self._issue_handler)
-#				
-#				print document
-				pass
-		
-		return proposals
-	
-	
+    """
+    This implements the BibTeX-specific code completion
+    """
+    _log = getLogger("BibTeXCompletionHandler")
+
+    trigger_keys = ["@"]
+    prefix_delimiters = ["@"]
+
+    def __init__(self):
+        self._model = BibTeXModel()
+        self._parser = BibTeXParser()
+        self._issue_handler = MockIssueHandler()
+
+    def complete(self, prefix):
+        self._log.debug("complete: '%s'" % prefix)
+
+        proposals = []
+
+        if len(prefix) == 1:
+            # propose all entry types
+            types = self._model.types
+            proposals = [BibTeXEntryTypeProposal(1, type) for type in types]
+        else:
+            if prefix[1:].isalpha():
+                type_name_prefix = prefix[1:].lower()
+                overlap = len(type_name_prefix) + 1
+                # prefix is @[a-zA-Z]+
+                types = [type for type in self._model.types if type.name.lower().startswith(type_name_prefix)]
+                proposals = [BibTeXEntryTypeProposal(overlap, type) for type in types]
+            else:
+                # parse prefix
+#                document = self._parser.parse(prefix, None, self._issue_handler)
+#
+#                print document
+                pass
+
+        return proposals
+
+
diff --git a/latex/bibtex/dialogs.py b/latex/bibtex/dialogs.py
index 7a4167e..68d09e4 100644
--- a/latex/bibtex/dialogs.py
+++ b/latex/bibtex/dialogs.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -31,162 +31,162 @@ from model import BibTeXModel
 
 
 class InsertBibTeXEntryDialog(GladeInterface):
-	
-	filename = find_resource("ui/insert_bibtex_entry.ui")
-	_dialog = None
-	
-	def run(self):
-		dialog = self._getDialog()
-		
-		result = None
-		
-		if dialog.run() == 1:
-			
-			s = "@%s{%s,\n" % (self._activeType.name, self._entryIdent.get_text())
-		
-			# fields
-			f = []
-			for entry, field in self._mapping.iteritems():
-				value = entry.get_text()
-				if len(value):
-					if field == "title":
-						f.append("\t%s = {{%s}}" % (field, value))
-					else:
-						f.append("\t%s = {%s}" % (field, value))
-			s += ",\n".join(f) 
-			
-			# abstract
-			#buf = self._view_abstract.get_buffer()
-			#abs = buf.get_text(buf.get_start_iter(), buf.get_end_iter())
-			#if len(abs):
-			#	s += ",\n\tabstract = {%s}" % abs
-			
-			s += "\n}"
-			
-			result = s
-		
-		dialog.hide()
-		
-		return result
-	
-	def _getDialog(self):
-		if not self._dialog:
-			self._dialog = self.find_widget("dialogInsertBibtexEntry")
-			
-			# get BibTeX model
-			self._model = BibTeXModel()
-			
-			# setup types combobox
-			
-			self._activeType = None
-			self._mapping = {}		# this maps Gtk.Entry objects to field names
-			self._fieldCache = {}	# this is used to restore field values after the type has changed
-			
-			self._storeTypes = Gtk.ListStore(str)
-			for t in self._model.types:
-				self._storeTypes.append([t.name])
-			
-			comboTypes = self.find_widget("comboTypes")
-			comboTypes.set_model(self._storeTypes)
-			cell = Gtk.CellRendererText()
-			comboTypes.pack_start(cell, True)
-			comboTypes.add_attribute(cell, "text", 0)
-			
-			self._boxRequired = self.find_widget("boxRequired")
-			self._boxOptional = self.find_widget("boxOptional")
-			
-			self._entryIdent = self.find_widget("entryIdent")
-			
-			self._buttonOk = self.find_widget("buttonOk")
-			
-			self.connect_signals({ "on_comboTypes_changed" : self._comboTypesChanged,
-								"on_entryIdent_changed" : self._identChanged })
-			
-			comboTypes.set_active(0)
-		
-		return self._dialog
-	
-	def _identChanged(self, entry):
-		enable = bool(len(entry.get_text()))
-		self._buttonOk.set_sensitive(enable)
-	
-	def _comboTypesChanged(self, combo):
-		i = combo.get_active()
-		self._activeType = self._model.types[i]
-		
-		# cache values
-		
-		for entry, fieldName in self._mapping.iteritems():
-			text = entry.get_text()
-			if len(text):
-				self._fieldCache[fieldName] = entry.get_text()
-		
-		# reset mapping
-		
-		self._mapping = {}
-		
-		# required fields
-		
-		tbl_required = Gtk.Table()
-		tbl_required.set_border_width(5)
-		tbl_required.set_row_spacings(5)
-		tbl_required.set_col_spacings(5)
-		i = 0
-		
-		for field in self._activeType.required_fields:
-			label = Gtk.Label(label=field.label + ":")
-			label.set_alignment(0, .5)
-			
-			entry = Gtk.Entry()
-			
-			tbl_required.attach(label, 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL)
-			tbl_required.attach(entry, 1, 2, i, i + 1)
-			
-			self._mapping[entry] = field.name
-			
-			# try to restore field value
-			try:
-				entry.set_text(self._fieldCache[field.name])
-			except KeyError:
-				pass
-			
-			i += 1
-		
-		for child in self._boxRequired.get_children():
-			child.destroy()
-		
-		tbl_required.show_all()
-		self._boxRequired.pack_start(tbl_required, False)
-		
-		# optional fields
-		
-		tbl_optional = Gtk.Table()
-		tbl_optional.set_border_width(5)
-		tbl_optional.set_row_spacings(5)
-		tbl_optional.set_col_spacings(5)
-		i = 0
-		
-		for field in self._activeType.optional_fields:
-			label = Gtk.Label(label=field.label + ":")
-			label.set_alignment(0, .5)
-			
-			entry = Gtk.Entry()
-			
-			tbl_optional.attach(label, 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL)
-			tbl_optional.attach(entry, 1, 2, i, i + 1)
-			
-			self._mapping[entry] = field.name
-			
-			# try to restore field value
-			try:
-				entry.set_text(self._fieldCache[field.name])
-			except KeyError:
-				pass
-			
-			i += 1
-		
-		for child in self._boxOptional.get_children():
-			child.destroy()
-		
-		tbl_optional.show_all()
-		self._boxOptional.pack_start(tbl_optional, False)
+
+    filename = find_resource("ui/insert_bibtex_entry.ui")
+    _dialog = None
+
+    def run(self):
+        dialog = self._getDialog()
+
+        result = None
+
+        if dialog.run() == 1:
+
+            s = "@%s{%s,\n" % (self._activeType.name, self._entryIdent.get_text())
+
+            # fields
+            f = []
+            for entry, field in self._mapping.iteritems():
+                value = entry.get_text()
+                if len(value):
+                    if field == "title":
+                        f.append("\t%s = {{%s}}" % (field, value))
+                    else:
+                        f.append("\t%s = {%s}" % (field, value))
+            s += ",\n".join(f)
+
+            # abstract
+            #buf = self._view_abstract.get_buffer()
+            #abs = buf.get_text(buf.get_start_iter(), buf.get_end_iter())
+            #if len(abs):
+            #    s += ",\n\tabstract = {%s}" % abs
+
+            s += "\n}"
+
+            result = s
+
+        dialog.hide()
+
+        return result
+
+    def _getDialog(self):
+        if not self._dialog:
+            self._dialog = self.find_widget("dialogInsertBibtexEntry")
+
+            # get BibTeX model
+            self._model = BibTeXModel()
+
+            # setup types combobox
+
+            self._activeType = None
+            self._mapping = {}        # this maps Gtk.Entry objects to field names
+            self._fieldCache = {}    # this is used to restore field values after the type has changed
+
+            self._storeTypes = Gtk.ListStore(str)
+            for t in self._model.types:
+                self._storeTypes.append([t.name])
+
+            comboTypes = self.find_widget("comboTypes")
+            comboTypes.set_model(self._storeTypes)
+            cell = Gtk.CellRendererText()
+            comboTypes.pack_start(cell, True)
+            comboTypes.add_attribute(cell, "text", 0)
+
+            self._boxRequired = self.find_widget("boxRequired")
+            self._boxOptional = self.find_widget("boxOptional")
+
+            self._entryIdent = self.find_widget("entryIdent")
+
+            self._buttonOk = self.find_widget("buttonOk")
+
+            self.connect_signals({ "on_comboTypes_changed" : self._comboTypesChanged,
+                                "on_entryIdent_changed" : self._identChanged })
+
+            comboTypes.set_active(0)
+
+        return self._dialog
+
+    def _identChanged(self, entry):
+        enable = bool(len(entry.get_text()))
+        self._buttonOk.set_sensitive(enable)
+
+    def _comboTypesChanged(self, combo):
+        i = combo.get_active()
+        self._activeType = self._model.types[i]
+
+        # cache values
+
+        for entry, fieldName in self._mapping.iteritems():
+            text = entry.get_text()
+            if len(text):
+                self._fieldCache[fieldName] = entry.get_text()
+
+        # reset mapping
+
+        self._mapping = {}
+
+        # required fields
+
+        tbl_required = Gtk.Table()
+        tbl_required.set_border_width(5)
+        tbl_required.set_row_spacings(5)
+        tbl_required.set_col_spacings(5)
+        i = 0
+
+        for field in self._activeType.required_fields:
+            label = Gtk.Label(label=field.label + ":")
+            label.set_alignment(0, .5)
+
+            entry = Gtk.Entry()
+
+            tbl_required.attach(label, 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL)
+            tbl_required.attach(entry, 1, 2, i, i + 1)
+
+            self._mapping[entry] = field.name
+
+            # try to restore field value
+            try:
+                entry.set_text(self._fieldCache[field.name])
+            except KeyError:
+                pass
+
+            i += 1
+
+        for child in self._boxRequired.get_children():
+            child.destroy()
+
+        tbl_required.show_all()
+        self._boxRequired.pack_start(tbl_required, False)
+
+        # optional fields
+
+        tbl_optional = Gtk.Table()
+        tbl_optional.set_border_width(5)
+        tbl_optional.set_row_spacings(5)
+        tbl_optional.set_col_spacings(5)
+        i = 0
+
+        for field in self._activeType.optional_fields:
+            label = Gtk.Label(label=field.label + ":")
+            label.set_alignment(0, .5)
+
+            entry = Gtk.Entry()
+
+            tbl_optional.attach(label, 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL)
+            tbl_optional.attach(entry, 1, 2, i, i + 1)
+
+            self._mapping[entry] = field.name
+
+            # try to restore field value
+            try:
+                entry.set_text(self._fieldCache[field.name])
+            except KeyError:
+                pass
+
+            i += 1
+
+        for child in self._boxOptional.get_children():
+            child.destroy()
+
+        tbl_optional.show_all()
+        self._boxOptional.pack_start(tbl_optional, False)
diff --git a/latex/bibtex/editor.py b/latex/bibtex/editor.py
index ed42430..07d3f2e 100644
--- a/latex/bibtex/editor.py
+++ b/latex/bibtex/editor.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -39,173 +39,173 @@ from validator import BibTeXValidator
 BENCHMARK = True
 
 if BENCHMARK:
-	import time
+    import time
 
 
 class ParseJob(Job):
-	def _run(self, arguments):
-		file = arguments[0]
-		content = arguments[1]
-		self._parser = BibTeXParser()
-		return self._parser.parse(content, file, MockIssueHandler())
+    def _run(self, arguments):
+        file = arguments[0]
+        content = arguments[1]
+        self._parser = BibTeXParser()
+        return self._parser.parse(content, file, MockIssueHandler())
 
 
 class BibTeXEditor(Editor, IIssueHandler, JobChangeListener):
-	
-	_log = getLogger("BibTeXEditor")
-	extensions = [".bib"]
-	
-	@property
-	def completion_handlers(self):
-		self.__bibtex_completion_handler = BibTeXCompletionHandler()
-		return [ self.__bibtex_completion_handler ]
-	
-	def __init__(self, tab_decorator, file):
-		Editor.__init__(self, tab_decorator, file)
-		self._parse_job = None
-		
-	def init(self, file, context):
-		self._log.debug("init(%s)" % file)
-		
-		self._preferences = Preferences()
-		
-		self._file = file
-		self._context = context
-		
-		self.register_marker_type("bibtex-error", self._preferences.get("error-background-color"))
-		self.register_marker_type("bibtex-warning", self._preferences.get("warning-background-color"))
-		
-		self._issue_view = context.find_view(self, "IssueView")
-		self._parser = BibTeXParser()
-		self._validator = BibTeXValidator()
-		self._outline_view = context.find_view(self, "BibTeXOutlineView")
-		
-		self._parse_job = ParseJob()
-		self._parse_job.set_change_listener(self)
-		
-		
-		# initially parse
-		self.__parse()
-	
-	def on_save(self):
-		"""
-		The file has been saved
-		
-		Update models
-		"""
-		self.__parse()
-	
-	
-#	def _on_state_changed(self, state):
-#		#
-#		# job.JobChangeListener._on_state_changed
-#		#
-#		if (state == JobManager.STATE_COMPLETED):
-#			self._log.debug("Parser finished")
-#			
-#			self._document = self._parse_job.get_returned()
-#			
-#			#self._log.debug(str(self._document))
-#			
-#			for entry in self._document.entries:
-#				print entry.key
-#			
-#			# FIXME: gedit crashes here with: 
-#			# gedit: Fatal IO error 11 (Resource temporarily unavailable) on X server :0.0.
-#			
-#			self._log.debug("Validating...")
-#			self._validator.validate(self._document, self._file, self)
-#			self._log.debug("Validating finished")
-#			
-#			self._outline_view.set_outline(self._document)
-#	
-#	
-#	def __parse_async(self):
-#		
-#		# reset highlight
-#		self.remove_markers("bibtex-error")
-#		self.remove_markers("bibtex-warning")
-#		
-#		# reset issues
-#		self._issue_view.clear()
-#		
-#		self._log.debug("Starting parser job")
-#		
-#		self._parse_job.set_argument([self._file, self.content])
-#		self._parse_job.schedule()
-		
-		
-	@verbose
-	def __parse(self):
-		"""
-		"""
-		self._log.debug("__parse")
-		
-		content = self.content
-		
-		# reset highlight
-		self.remove_markers("bibtex-error")
-		self.remove_markers("bibtex-warning")
-		
-		# reset issues
-		self._issue_view.clear()
-		
-#		self.parse(self._file)
-		
-		if BENCHMARK: t = time.clock()
-		
-		# parse document
-		self._document = self._parser.parse(content, self._file, self)
-		
-		if BENCHMARK: self._log.info("BibTeXParser.parse: %f" % (time.clock() - t))
-		
-		self._log.debug("Parsed %s bytes of content" % len(content))
-		
-		# validate
-		if BENCHMARK: t = time.clock()
-		
-		self._validator.validate(self._document, self._file, self)
-		
-		# 0.11
-		if BENCHMARK: self._log.info("BibTeXValidator.validate: %f" % (time.clock() - t))
-		
-		self._outline_view.set_outline(self._document)
-	
-#	def _on_parser_finished(self, model):
-#		"""
-#		"""
-#		self._document = model
-#		self._outline_view.set_outline(self._document)
-	
-	def issue(self, issue):
-		# overriding IIssueHandler.issue
-		
-		self._issue_view.append_issue(issue)
-		
-		if issue.file == self._file:
-			if issue.severity == Issue.SEVERITY_ERROR:
-				self.create_marker("bibtex-error", issue.start, issue.end)
-			elif issue.severity == Issue.SEVERITY_WARNING:
-				self.create_marker("bibtex-warning", issue.start, issue.end)
-	
-	def on_cursor_moved(self, offset):
-		"""
-		The cursor has moved
-		"""
-		if self._preferences.get_bool("outline-connect-to-editor"):
-			self._outline_view.select_path_by_offset(offset)
-				
-	def destroy(self):
-		# unreference the window context
-		del self._context
-		
-		# remove parse listener
-		if self._parse_job != None:
-			self._parse_job.set_change_listener(None)
-
-		Editor.destroy(self)
-		
-	def __del__(self):
-		self._log.debug("Properly destroyed %s" % self)
-	
+
+    _log = getLogger("BibTeXEditor")
+    extensions = [".bib"]
+
+    @property
+    def completion_handlers(self):
+        self.__bibtex_completion_handler = BibTeXCompletionHandler()
+        return [ self.__bibtex_completion_handler ]
+
+    def __init__(self, tab_decorator, file):
+        Editor.__init__(self, tab_decorator, file)
+        self._parse_job = None
+
+    def init(self, file, context):
+        self._log.debug("init(%s)" % file)
+
+        self._preferences = Preferences()
+
+        self._file = file
+        self._context = context
+
+        self.register_marker_type("bibtex-error", self._preferences.get("error-background-color"))
+        self.register_marker_type("bibtex-warning", self._preferences.get("warning-background-color"))
+
+        self._issue_view = context.find_view(self, "IssueView")
+        self._parser = BibTeXParser()
+        self._validator = BibTeXValidator()
+        self._outline_view = context.find_view(self, "BibTeXOutlineView")
+
+        self._parse_job = ParseJob()
+        self._parse_job.set_change_listener(self)
+
+
+        # initially parse
+        self.__parse()
+
+    def on_save(self):
+        """
+        The file has been saved
+
+        Update models
+        """
+        self.__parse()
+
+
+#    def _on_state_changed(self, state):
+#        #
+#        # job.JobChangeListener._on_state_changed
+#        #
+#        if (state == JobManager.STATE_COMPLETED):
+#            self._log.debug("Parser finished")
+#
+#            self._document = self._parse_job.get_returned()
+#
+#            #self._log.debug(str(self._document))
+#
+#            for entry in self._document.entries:
+#                print entry.key
+#
+#            # FIXME: gedit crashes here with:
+#            # gedit: Fatal IO error 11 (Resource temporarily unavailable) on X server :0.0.
+#
+#            self._log.debug("Validating...")
+#            self._validator.validate(self._document, self._file, self)
+#            self._log.debug("Validating finished")
+#
+#            self._outline_view.set_outline(self._document)
+#
+#
+#    def __parse_async(self):
+#
+#        # reset highlight
+#        self.remove_markers("bibtex-error")
+#        self.remove_markers("bibtex-warning")
+#
+#        # reset issues
+#        self._issue_view.clear()
+#
+#        self._log.debug("Starting parser job")
+#
+#        self._parse_job.set_argument([self._file, self.content])
+#        self._parse_job.schedule()
+
+
+    @verbose
+    def __parse(self):
+        """
+        """
+        self._log.debug("__parse")
+
+        content = self.content
+
+        # reset highlight
+        self.remove_markers("bibtex-error")
+        self.remove_markers("bibtex-warning")
+
+        # reset issues
+        self._issue_view.clear()
+
+#        self.parse(self._file)
+
+        if BENCHMARK: t = time.clock()
+
+        # parse document
+        self._document = self._parser.parse(content, self._file, self)
+
+        if BENCHMARK: self._log.info("BibTeXParser.parse: %f" % (time.clock() - t))
+
+        self._log.debug("Parsed %s bytes of content" % len(content))
+
+        # validate
+        if BENCHMARK: t = time.clock()
+
+        self._validator.validate(self._document, self._file, self)
+
+        # 0.11
+        if BENCHMARK: self._log.info("BibTeXValidator.validate: %f" % (time.clock() - t))
+
+        self._outline_view.set_outline(self._document)
+
+#    def _on_parser_finished(self, model):
+#        """
+#        """
+#        self._document = model
+#        self._outline_view.set_outline(self._document)
+
+    def issue(self, issue):
+        # overriding IIssueHandler.issue
+
+        self._issue_view.append_issue(issue)
+
+        if issue.file == self._file:
+            if issue.severity == Issue.SEVERITY_ERROR:
+                self.create_marker("bibtex-error", issue.start, issue.end)
+            elif issue.severity == Issue.SEVERITY_WARNING:
+                self.create_marker("bibtex-warning", issue.start, issue.end)
+
+    def on_cursor_moved(self, offset):
+        """
+        The cursor has moved
+        """
+        if self._preferences.get_bool("outline-connect-to-editor"):
+            self._outline_view.select_path_by_offset(offset)
+
+    def destroy(self):
+        # unreference the window context
+        del self._context
+
+        # remove parse listener
+        if self._parse_job != None:
+            self._parse_job.set_change_listener(None)
+
+        Editor.destroy(self)
+
+    def __del__(self):
+        self._log.debug("Properly destroyed %s" % self)
+
 
diff --git a/latex/bibtex/model.py b/latex/bibtex/model.py
index f849e3a..ea016b8 100644
--- a/latex/bibtex/model.py
+++ b/latex/bibtex/model.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -29,79 +29,78 @@ from ..base.resources import find_resource
 
 
 class Type(object):
-	def __init__(self, name):
-		self.name = name
-		self.required_fields = []
-		self.optional_fields = []
-		
-	def __cmp__(self, other):
-		# FIXME: there must be something simpler...
-		if self.name < other.name:
-			return -1
-		elif self.name > other.name:
-			return 1
-		else:
-			return 0
-		
-		#return self.name.__cmp__(other.name)	str has no __cmp__
+    def __init__(self, name):
+        self.name = name
+        self.required_fields = []
+        self.optional_fields = []
+
+    def __cmp__(self, other):
+        # FIXME: there must be something simpler...
+        if self.name < other.name:
+            return -1
+        elif self.name > other.name:
+            return 1
+        else:
+            return 0
+
+        #return self.name.__cmp__(other.name)    str has no __cmp__
 
 
 class Field(object):
-	def __init__(self, name, label):
-		self.name = name
-		self.label = label
+    def __init__(self, name, label):
+        self.name = name
+        self.label = label
 
 
 class BibTeXModel(object):
-	def __new__(cls):
-		if not '_instance' in cls.__dict__:
-			cls._instance = object.__new__(cls)
-		return cls._instance
-	
-	def __init__(self):
-		if not '_ready' in dir(self):
-			# init object
-			self._fields = {}
-			self._types = {}
-			
-			# parse bibtex.xml
-			self._bibtex = ElementTree.parse(find_resource("bibtex.xml")).getroot()
-			
-			for field_e in self._bibtex.findall("fields/field"):
-				id = field_e.get("id")
-				self._fields[id] = Field(id, field_e.get("_label"))
-			
-			for type_e in self._bibtex.findall("types/type"):
-				id = type_e.get("id")
-				type = Type(id)
-				
-				for required_field_e in type_e.findall("required/field"):
-					field_id = required_field_e.get("id")
-					type.required_fields.append(self._fields[field_id])
-				
-				for optional_field_e in type_e.findall("optional/field"):
-					field_id = optional_field_e.get("id")
-					type.optional_fields.append(self._fields[field_id])
-				
-				self._types[id.lower()] = type
-			
-			self._types_list = None
-			self._ready = True
-	
-	def find_type(self, name):
-		"""
-		Find an entry type by its name
-		"""
-		return self._types[name.lower()]
-	
-	@property
-	def types(self):
-		"""
-		List all entry types in sorted order
-		"""
-		if self._types_list is None:
-			self._types_list = self._types.values()
-			self._types_list.sort()
-		return self._types_list
-		
-		
\ No newline at end of file
+    def __new__(cls):
+        if not '_instance' in cls.__dict__:
+            cls._instance = object.__new__(cls)
+        return cls._instance
+
+    def __init__(self):
+        if not '_ready' in dir(self):
+            # init object
+            self._fields = {}
+            self._types = {}
+
+            # parse bibtex.xml
+            self._bibtex = ElementTree.parse(find_resource("bibtex.xml")).getroot()
+
+            for field_e in self._bibtex.findall("fields/field"):
+                id = field_e.get("id")
+                self._fields[id] = Field(id, field_e.get("_label"))
+
+            for type_e in self._bibtex.findall("types/type"):
+                id = type_e.get("id")
+                type = Type(id)
+
+                for required_field_e in type_e.findall("required/field"):
+                    field_id = required_field_e.get("id")
+                    type.required_fields.append(self._fields[field_id])
+
+                for optional_field_e in type_e.findall("optional/field"):
+                    field_id = optional_field_e.get("id")
+                    type.optional_fields.append(self._fields[field_id])
+
+                self._types[id.lower()] = type
+
+            self._types_list = None
+            self._ready = True
+
+    def find_type(self, name):
+        """
+        Find an entry type by its name
+        """
+        return self._types[name.lower()]
+
+    @property
+    def types(self):
+        """
+        List all entry types in sorted order
+        """
+        if self._types_list is None:
+            self._types_list = self._types.values()
+            self._types_list.sort()
+        return self._types_list
+
diff --git a/latex/bibtex/parser.py b/latex/bibtex/parser.py
index 673d0e0..2bdbe27 100644
--- a/latex/bibtex/parser.py
+++ b/latex/bibtex/parser.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -29,31 +29,31 @@ BibTeX parser and object model
 ## async parser feature
 ##
 #if __name__ == "__main__":
-#	#
-#	# The script has been started in a shell process
-#	#
-#	# Parse the file passed as first argument and write the model
-#	# as a pickled object to STDOUT
-#	#
-#	import sys
-#	
-#	# TODO: fetch issues and pass them together with the model in a special
-#	# transfer object
-#	
-#	plugin_path = sys.argv[1]
-#	filename = sys.argv[2]
-#	
-#	sys.path.append(plugin_path) 
-#	sys.path.append("/home/michael/.gnome2/gedit/plugins")
-#	
-#	from issues import MockIssueHandler
-#	from base import File
-#	
-#	model = BibTeXParser().parse_async(open(filename).read(), filename)
+#    #
+#    # The script has been started in a shell process
+#    #
+#    # Parse the file passed as first argument and write the model
+#    # as a pickled object to STDOUT
+#    #
+#    import sys
+#
+#    # TODO: fetch issues and pass them together with the model in a special
+#    # transfer object
+#
+#    plugin_path = sys.argv[1]
+#    filename = sys.argv[2]
+#
+#    sys.path.append(plugin_path)
+#    sys.path.append("/home/michael/.gnome2/gedit/plugins")
+#
+#    from issues import MockIssueHandler
+#    from base import File
+#
+#    model = BibTeXParser().parse_async(open(filename).read(), filename)
 #else:
-#	#
-#	# normal package code...
-#	#
+#    #
+#    # normal package code...
+#    #
 
 
 from logging import getLogger
@@ -64,26 +64,26 @@ from ..issues import Issue, MockIssueHandler
 from ..preferences import Preferences
 
 
-#	import sys
-#	import os
-#	
-#	print "======== sys.path=%s, cwd=%s" % (sys.path, os.getcwd())
+#    import sys
+#    import os
+#
+#    print "======== sys.path=%s, cwd=%s" % (sys.path, os.getcwd())
 
 
 class Token(object):
-	"""
-	A BibTeX token
-	"""
-	
-	AT, TEXT, COMMA, EQUALS, QUOTE, HASH, CURLY_OPEN, CURLY_CLOSE, ROUND_OPEN, ROUND_CLOSE = range(10)
-	
-	def __init__(self, type, offset, value):
-		self.type = type
-		self.offset = offset
-		self.value = value
-	
-	def __str__(self):
-		return "<Token type='%s' value='%s' @%s>" % (self.type, self.value, self.offset)
+    """
+    A BibTeX token
+    """
+
+    AT, TEXT, COMMA, EQUALS, QUOTE, HASH, CURLY_OPEN, CURLY_CLOSE, ROUND_OPEN, ROUND_CLOSE = range(10)
+
+    def __init__(self, type, offset, value):
+        self.type = type
+        self.offset = offset
+        self.value = value
+
+    def __str__(self):
+        return "<Token type='%s' value='%s' @%s>" % (self.type, self.value, self.offset)
 
 
 from ..util import StringReader
@@ -91,512 +91,512 @@ from ..util import open_info
 
 
 class BibTeXLexer(object):
-	"""
-	BibTeX lexer. We only separate text from special tokens here and
-	apply escaping.
-	"""
-	
-	_TERMINALS_TOKENS = {"@" : Token.AT, "," : Token.COMMA, "=" : Token.EQUALS, 
-						 "{" : Token.CURLY_OPEN, "}" : Token.CURLY_CLOSE, "\"" : Token.QUOTE,
-						 "#" : Token.HASH, "(" : Token.ROUND_OPEN, ")" : Token.ROUND_CLOSE}
-	
-	_TERMINALS = set(_TERMINALS_TOKENS.keys()) 
-	
-	def __init__(self, string):
-		self._reader = StringReader(string)
-	
-	def __iter__(self):
-		return self
-	
-	def next(self):
-		"""
-		Return the next token
-		"""
-		
-		escaping = False
-		textBuilder = None
-		textStart = None
-		
-		while True:
-			c = self._reader.read()
-			
-			if not escaping and c in self._TERMINALS:
-				if textBuilder is not None:
-					self._reader.unread(c)
-					text = "".join(textBuilder)
-					textBuilder = None
-					return Token(Token.TEXT, textStart, text)
-				
-				return Token(self._TERMINALS_TOKENS[c], self._reader.offset - 1, c)
-				
-			else:
-				if textBuilder is None:
-					textStart = self._reader.offset
-					textBuilder = [c]
-				else:
-					textBuilder.append(c)
-				
-				if c == "\\":
-					escaping = True
-				else:
-					escaping = False
+    """
+    BibTeX lexer. We only separate text from special tokens here and
+    apply escaping.
+    """
+
+    _TERMINALS_TOKENS = {"@" : Token.AT, "," : Token.COMMA, "=" : Token.EQUALS,
+                         "{" : Token.CURLY_OPEN, "}" : Token.CURLY_CLOSE, "\"" : Token.QUOTE,
+                         "#" : Token.HASH, "(" : Token.ROUND_OPEN, ")" : Token.ROUND_CLOSE}
+
+    _TERMINALS = set(_TERMINALS_TOKENS.keys())
+
+    def __init__(self, string):
+        self._reader = StringReader(string)
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        """
+        Return the next token
+        """
+
+        escaping = False
+        textBuilder = None
+        textStart = None
+
+        while True:
+            c = self._reader.read()
+
+            if not escaping and c in self._TERMINALS:
+                if textBuilder is not None:
+                    self._reader.unread(c)
+                    text = "".join(textBuilder)
+                    textBuilder = None
+                    return Token(Token.TEXT, textStart, text)
+
+                return Token(self._TERMINALS_TOKENS[c], self._reader.offset - 1, c)
+
+            else:
+                if textBuilder is None:
+                    textStart = self._reader.offset
+                    textBuilder = [c]
+                else:
+                    textBuilder.append(c)
+
+                if c == "\\":
+                    escaping = True
+                else:
+                    escaping = False
 
 
 class BibTeXParser(object):
-	"""
-	A fast and safe BibTeX parser that generates a handy model on the fly.
-	
-	Instead of raising exceptions this parser uses an IIssueHandler.
-	"""
-	
-	_OUTSIDE, _TYPE, _AFTER_TYPE, _AFTER_STRING_TYPE, _KEY, _STRING_KEY, _AFTER_KEY, _AFTER_STRING_KEY, \
-			_STRING_VALUE, _QUOTED_STRING_VALUE, _FIELD_NAME, _AFTER_FIELD_NAME, _FIELD_VALUE, _EMBRACED_FIELD_VALUE, \
-			_QUOTED_FIELD_VALUE = range(15) 
-	
-	def __init__(self, quiet=False):
-		self._quiet = quiet
-		self._max_size_info_shown = False
-		
-		self._state = None
-		self._type = None
-		self._constant = None
-		self._entry = None
-		self._file = None
-		self._closingDelimiter = None
-		self._document = None
-		self._field = None
-		self._value = None
-		self._stack = None
-	
-	#
-	# callables for each state of the parser
-	#
-	
-	def _on_outside(self, token, file, issue_handler):
-		if token.type == Token.AT:
-			self._state = self._TYPE
-	
-	def _on_type(self, token, file, issue_handler):
-		if token.type == Token.TEXT:
-			self._type = token.value.strip()
-			
-			if self._type.lower() == "string" :
-				self._constant = Constant()
-				self._state = self._AFTER_STRING_TYPE
-			elif self._type.lower() in ["preamble", "comment"]:		# simply skip PREAMBLE and COMMENT entries
-				self._state = self._OUTSIDE
-			else:
-				self._entry = Entry()
-				self._entry.type = self._type
-				self._entry.start = token.offset - 2
-				self._state = self._AFTER_TYPE
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> in entry type" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._entry = None
-			self._state = self._OUTSIDE
-	
-	def _on_after_type(self, token, file, issue_handler):
-		if token.type == Token.CURLY_OPEN:
-			self._closingDelimiter = Token.CURLY_CLOSE
-			self._state = self._KEY
-		elif token.type == Token.ROUND_OPEN:
-			self._closingDelimiter = Token.ROUND_CLOSE
-			self._state = self._KEY
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> after entry type" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._entry = None
-			self._state = self._OUTSIDE
-	
-	def _on_after_string_type(self, token, file, issue_handler):
-		if token.type == Token.CURLY_OPEN:
-			self._closingDelimiter = Token.CURLY_CLOSE
-			self._state = self._STRING_KEY
-		elif token.type == Token.ROUND_OPEN:
-			self._closingDelimiter = Token.ROUND_CLOSE
-			self._state = self._STRING_KEY
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> after string type" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._constant = None
-			self._state = self._OUTSIDE
-	
-	def _on_key(self, token, file, issue_handler):
-		if token.type == Token.TEXT:
-			self._entry.key = token.value.strip()
-			self._state = self._AFTER_KEY
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> in entry key" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._entry = None
-			self._state = self._OUTSIDE
-	
-	def _on_string_key(self, token, file, issue_handler):
-		if token.type == Token.TEXT:
-			self._constant.name = token.value.strip()
-			self._state = self._AFTER_STRING_KEY
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> in string key" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._constant = None
-			self._state = self._OUTSIDE
-	
-	def _on_after_key(self, token, file, issue_handler):
-		if token.type == Token.COMMA:
-			self._state = self._FIELD_NAME
-		elif token.type == self._closingDelimiter:
-			self._entry.end = token.offset + 1
-			self._document.entries.append(self._entry)
-			self._state = self._OUTSIDE
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> after entry key" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._entry = None
-			self._state = self._OUTSIDE
-	
-	def _on_after_string_key(self, token, file, issue_handler):
-		if token.type == Token.EQUALS:
-			self._state = self._STRING_VALUE
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> after string key" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._constant = None
-			self._state = self._OUTSIDE
-	
-	def _on_string_value(self, token, file, issue_handler):
-		if token.type == Token.QUOTE:
-			self._state = self._QUOTED_STRING_VALUE
-	
-	def _on_quoted_string_value(self, token):
-		if token.type == Token.TEXT:
-			self._constant.value = token.value
-			self._document.constants.append(self._constant)
-			self._state = self._OUTSIDE
-	
-	def _on_field_name(self, token, file, issue_handler):
-		if token.type == Token.TEXT:
-			
-			if token.value.isspace():
-				return
-			
-			self._field = Field()
-			self._field.name = token.value.strip()
-			self._state = self._AFTER_FIELD_NAME
-		elif token.type == self._closingDelimiter:
-			self._entry.end = token.offset + 1
-			self._document.entries.append(self._entry)
-			self._state = self._OUTSIDE
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> in field name" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._entry = None
-			self._state = self._OUTSIDE
-	
-	def _on_after_field_name(self, token, file, issue_handler):
-		if token.type == Token.EQUALS:
-			self._state = self._FIELD_VALUE
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> after field name" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._entry = None
-			self._state = self._OUTSIDE
-	
-	def _on_field_value(self, token, file, issue_handler):
-		# TODO: we may not recognize something like "author = ," as an error
-				
-		if token.value.isspace():
-			return
-		
-		if token.type == Token.TEXT:
-			self._value = token.value.strip()
-			if self._value.isdigit():
-				self._field.value.append(NumberValue(self._value))
-			else:
-				self._field.value.append(ConstantReferenceValue(self._value))
-		elif token.type == Token.CURLY_OPEN:
-			self._value = ""
-			self._stack = [Token.CURLY_OPEN]
-			self._state = self._EMBRACED_FIELD_VALUE
-		elif token.type == Token.QUOTE:
-			self._value = ""
-			#stack = [Token.QUOTE]
-			self._state = self._QUOTED_FIELD_VALUE
-		elif token.type == Token.COMMA:
-			self._entry.fields.append(self._field)
-			self._state = self._FIELD_NAME
-		elif token.type == self._closingDelimiter:
-			self._entry.fields.append(self._field)
-			self._entry.end = token.offset + 1
-			self._document.entries.append(self._entry)
-			self._state = self._OUTSIDE
-		elif token.type == Token.HASH:
-			pass
-		else:
-			issue_handler.issue(Issue("Unexpected token <b>%s</b> in field value" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-			self._entry = None
-			self._state = self._OUTSIDE
-	
-	def _on_embraced_field_value(self, token, file, issue_handler):
-		if token.type == Token.CURLY_OPEN:
-			self._stack.append(Token.CURLY_OPEN)
-			self._value += token.value
-		elif token.type == Token.CURLY_CLOSE:
-			try:
-				while self._stack[-1] != Token.CURLY_OPEN:
-					self._stack.pop()
-				self._stack.pop()
-				
-				if len(self._stack) == 0:
-					self._field.value.append(StringValue(self._value))
-					self._state = self._FIELD_VALUE
-				else:
-					self._value += token.value
-				
-			except IndexError:
-				issue_handler.issue(Issue("Unexpected token <b>%s</b> in field value" % escape(token.value), 
-									token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
-				self._entry = None
-				self._state = self._OUTSIDE
-		else:
-			self._value += token.value
-	
-	def _on_quoted_field_value(self, token, file, issue_handler):
-		if token.type == Token.QUOTE:
-			self._field.value.append(StringValue(self._value))
-			self._state = self._FIELD_VALUE
-		else:
-			self._value += token.value
-	
-	def parse(self, string, file, issue_handler):
-		"""
-		Parse a BibTeX content
-		@param string: the content to be parsed
-		@param file: the File object containing the BibTeX
-		@param issue_handler: an object implementing IIssueHandler
-		"""
-		self._document = Document()
-		
-		# respect maximum BibTeX file size
-		max_size_kb = int(Preferences().get("maximum-bibtex-size"))
-		length = len(string)
-
-		if length > max_size_kb * 1024:
-			if not self._quiet and not self._max_size_info_shown:
-				open_info("BibTeX file will not be parsed", "The maximum size of BibTeX files to parse is set to %s KB." % max_size_kb)
-				self._max_size_info_shown = True
-			return self._document
-		
-		# parse
-		self._state = self._OUTSIDE
-		
-		#
-		# use this hash table instead of endless if...elif statements
-		#
-		callables = {
-				self._OUTSIDE : self._on_outside,
-				self._TYPE : self._on_type,
-				self._AFTER_TYPE : self._on_after_type,
-				self._AFTER_STRING_TYPE : self._on_after_string_type,
-				self._KEY : self._on_key,
-				self._STRING_KEY : self._on_string_key,
-				self._AFTER_KEY : self._on_after_key,
-				self._AFTER_STRING_KEY : self._on_after_string_key,
-				self._STRING_VALUE : self._on_string_value,
-				self._QUOTED_STRING_VALUE : self._on_quoted_string_value,
-				self._FIELD_NAME : self._on_field_name,
-				self._AFTER_FIELD_NAME : self._on_after_field_name,
-				self._FIELD_VALUE : self._on_field_value,
-				self._EMBRACED_FIELD_VALUE : self._on_embraced_field_value,
-				self._QUOTED_FIELD_VALUE : self._on_quoted_field_value
-		}
-		
-		for token in BibTeXLexer(string):
-			callables[self._state].__call__(token, file, issue_handler)
-
-		return self._document
-	
-	#~ def __del__(self):
-		#~ print "properly destroyed %s" % self
+    """
+    A fast and safe BibTeX parser that generates a handy model on the fly.
+
+    Instead of raising exceptions this parser uses an IIssueHandler.
+    """
+
+    _OUTSIDE, _TYPE, _AFTER_TYPE, _AFTER_STRING_TYPE, _KEY, _STRING_KEY, _AFTER_KEY, _AFTER_STRING_KEY, \
+            _STRING_VALUE, _QUOTED_STRING_VALUE, _FIELD_NAME, _AFTER_FIELD_NAME, _FIELD_VALUE, _EMBRACED_FIELD_VALUE, \
+            _QUOTED_FIELD_VALUE = range(15)
+
+    def __init__(self, quiet=False):
+        self._quiet = quiet
+        self._max_size_info_shown = False
+
+        self._state = None
+        self._type = None
+        self._constant = None
+        self._entry = None
+        self._file = None
+        self._closingDelimiter = None
+        self._document = None
+        self._field = None
+        self._value = None
+        self._stack = None
+
+    #
+    # callables for each state of the parser
+    #
+
+    def _on_outside(self, token, file, issue_handler):
+        if token.type == Token.AT:
+            self._state = self._TYPE
+
+    def _on_type(self, token, file, issue_handler):
+        if token.type == Token.TEXT:
+            self._type = token.value.strip()
+
+            if self._type.lower() == "string" :
+                self._constant = Constant()
+                self._state = self._AFTER_STRING_TYPE
+            elif self._type.lower() in ["preamble", "comment"]:        # simply skip PREAMBLE and COMMENT entries
+                self._state = self._OUTSIDE
+            else:
+                self._entry = Entry()
+                self._entry.type = self._type
+                self._entry.start = token.offset - 2
+                self._state = self._AFTER_TYPE
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> in entry type" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._entry = None
+            self._state = self._OUTSIDE
+
+    def _on_after_type(self, token, file, issue_handler):
+        if token.type == Token.CURLY_OPEN:
+            self._closingDelimiter = Token.CURLY_CLOSE
+            self._state = self._KEY
+        elif token.type == Token.ROUND_OPEN:
+            self._closingDelimiter = Token.ROUND_CLOSE
+            self._state = self._KEY
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> after entry type" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._entry = None
+            self._state = self._OUTSIDE
+
+    def _on_after_string_type(self, token, file, issue_handler):
+        if token.type == Token.CURLY_OPEN:
+            self._closingDelimiter = Token.CURLY_CLOSE
+            self._state = self._STRING_KEY
+        elif token.type == Token.ROUND_OPEN:
+            self._closingDelimiter = Token.ROUND_CLOSE
+            self._state = self._STRING_KEY
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> after string type" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._constant = None
+            self._state = self._OUTSIDE
+
+    def _on_key(self, token, file, issue_handler):
+        if token.type == Token.TEXT:
+            self._entry.key = token.value.strip()
+            self._state = self._AFTER_KEY
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> in entry key" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._entry = None
+            self._state = self._OUTSIDE
+
+    def _on_string_key(self, token, file, issue_handler):
+        if token.type == Token.TEXT:
+            self._constant.name = token.value.strip()
+            self._state = self._AFTER_STRING_KEY
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> in string key" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._constant = None
+            self._state = self._OUTSIDE
+
+    def _on_after_key(self, token, file, issue_handler):
+        if token.type == Token.COMMA:
+            self._state = self._FIELD_NAME
+        elif token.type == self._closingDelimiter:
+            self._entry.end = token.offset + 1
+            self._document.entries.append(self._entry)
+            self._state = self._OUTSIDE
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> after entry key" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._entry = None
+            self._state = self._OUTSIDE
+
+    def _on_after_string_key(self, token, file, issue_handler):
+        if token.type == Token.EQUALS:
+            self._state = self._STRING_VALUE
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> after string key" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._constant = None
+            self._state = self._OUTSIDE
+
+    def _on_string_value(self, token, file, issue_handler):
+        if token.type == Token.QUOTE:
+            self._state = self._QUOTED_STRING_VALUE
+
+    def _on_quoted_string_value(self, token):
+        if token.type == Token.TEXT:
+            self._constant.value = token.value
+            self._document.constants.append(self._constant)
+            self._state = self._OUTSIDE
+
+    def _on_field_name(self, token, file, issue_handler):
+        if token.type == Token.TEXT:
+
+            if token.value.isspace():
+                return
+
+            self._field = Field()
+            self._field.name = token.value.strip()
+            self._state = self._AFTER_FIELD_NAME
+        elif token.type == self._closingDelimiter:
+            self._entry.end = token.offset + 1
+            self._document.entries.append(self._entry)
+            self._state = self._OUTSIDE
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> in field name" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._entry = None
+            self._state = self._OUTSIDE
+
+    def _on_after_field_name(self, token, file, issue_handler):
+        if token.type == Token.EQUALS:
+            self._state = self._FIELD_VALUE
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> after field name" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._entry = None
+            self._state = self._OUTSIDE
+
+    def _on_field_value(self, token, file, issue_handler):
+        # TODO: we may not recognize something like "author = ," as an error
+
+        if token.value.isspace():
+            return
+
+        if token.type == Token.TEXT:
+            self._value = token.value.strip()
+            if self._value.isdigit():
+                self._field.value.append(NumberValue(self._value))
+            else:
+                self._field.value.append(ConstantReferenceValue(self._value))
+        elif token.type == Token.CURLY_OPEN:
+            self._value = ""
+            self._stack = [Token.CURLY_OPEN]
+            self._state = self._EMBRACED_FIELD_VALUE
+        elif token.type == Token.QUOTE:
+            self._value = ""
+            #stack = [Token.QUOTE]
+            self._state = self._QUOTED_FIELD_VALUE
+        elif token.type == Token.COMMA:
+            self._entry.fields.append(self._field)
+            self._state = self._FIELD_NAME
+        elif token.type == self._closingDelimiter:
+            self._entry.fields.append(self._field)
+            self._entry.end = token.offset + 1
+            self._document.entries.append(self._entry)
+            self._state = self._OUTSIDE
+        elif token.type == Token.HASH:
+            pass
+        else:
+            issue_handler.issue(Issue("Unexpected token <b>%s</b> in field value" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+            self._entry = None
+            self._state = self._OUTSIDE
+
+    def _on_embraced_field_value(self, token, file, issue_handler):
+        if token.type == Token.CURLY_OPEN:
+            self._stack.append(Token.CURLY_OPEN)
+            self._value += token.value
+        elif token.type == Token.CURLY_CLOSE:
+            try:
+                while self._stack[-1] != Token.CURLY_OPEN:
+                    self._stack.pop()
+                self._stack.pop()
+
+                if len(self._stack) == 0:
+                    self._field.value.append(StringValue(self._value))
+                    self._state = self._FIELD_VALUE
+                else:
+                    self._value += token.value
+
+            except IndexError:
+                issue_handler.issue(Issue("Unexpected token <b>%s</b> in field value" % escape(token.value),
+                                    token.offset, token.offset + 1, file, Issue.SEVERITY_ERROR))
+                self._entry = None
+                self._state = self._OUTSIDE
+        else:
+            self._value += token.value
+
+    def _on_quoted_field_value(self, token, file, issue_handler):
+        if token.type == Token.QUOTE:
+            self._field.value.append(StringValue(self._value))
+            self._state = self._FIELD_VALUE
+        else:
+            self._value += token.value
+
+    def parse(self, string, file, issue_handler):
+        """
+        Parse a BibTeX content
+        @param string: the content to be parsed
+        @param file: the File object containing the BibTeX
+        @param issue_handler: an object implementing IIssueHandler
+        """
+        self._document = Document()
+
+        # respect maximum BibTeX file size
+        max_size_kb = int(Preferences().get("maximum-bibtex-size"))
+        length = len(string)
+
+        if length > max_size_kb * 1024:
+            if not self._quiet and not self._max_size_info_shown:
+                open_info("BibTeX file will not be parsed", "The maximum size of BibTeX files to parse is set to %s KB." % max_size_kb)
+                self._max_size_info_shown = True
+            return self._document
+
+        # parse
+        self._state = self._OUTSIDE
+
+        #
+        # use this hash table instead of endless if...elif statements
+        #
+        callables = {
+                self._OUTSIDE : self._on_outside,
+                self._TYPE : self._on_type,
+                self._AFTER_TYPE : self._on_after_type,
+                self._AFTER_STRING_TYPE : self._on_after_string_type,
+                self._KEY : self._on_key,
+                self._STRING_KEY : self._on_string_key,
+                self._AFTER_KEY : self._on_after_key,
+                self._AFTER_STRING_KEY : self._on_after_string_key,
+                self._STRING_VALUE : self._on_string_value,
+                self._QUOTED_STRING_VALUE : self._on_quoted_string_value,
+                self._FIELD_NAME : self._on_field_name,
+                self._AFTER_FIELD_NAME : self._on_after_field_name,
+                self._FIELD_VALUE : self._on_field_value,
+                self._EMBRACED_FIELD_VALUE : self._on_embraced_field_value,
+                self._QUOTED_FIELD_VALUE : self._on_quoted_field_value
+        }
+
+        for token in BibTeXLexer(string):
+            callables[self._state].__call__(token, file, issue_handler)
+
+        return self._document
+
+    #~ def __del__(self):
+        #~ print "properly destroyed %s" % self
 
 #
 # BibTeX object model
 #
 
 class Value(object):
-	def __init__(self, text):
-		self.text = text 
-	
-	MAX_MARKUP_LENGTH = 50
-	
-	@property
-	def markup(self):
-		text = self.text
-		
-		# remove braces
-		if text.startswith("{{") and text.endswith("}}"):
-			text = text[2:-2]
-		elif text.startswith("{") and text.endswith("}"):
-			text = text[1:-1]
-		elif text.startswith("\\url{") and text.endswith("}"):
-			text = text[5:-1]
-		
-		# truncate
-		if len(text) > self.MAX_MARKUP_LENGTH:
-			text = text[:self.MAX_MARKUP_LENGTH] + "..."
-		
-		# remove newlines
-		text = text.replace("\n", "")
-		
-		# escape problematic characters
-		text = escape(text)
-		
-		return text
-	
-	def __str__(self):
-		return "<Value text='%s'>" % self.text
+    def __init__(self, text):
+        self.text = text
+
+    MAX_MARKUP_LENGTH = 50
+
+    @property
+    def markup(self):
+        text = self.text
+
+        # remove braces
+        if text.startswith("{{") and text.endswith("}}"):
+            text = text[2:-2]
+        elif text.startswith("{") and text.endswith("}"):
+            text = text[1:-1]
+        elif text.startswith("\\url{") and text.endswith("}"):
+            text = text[5:-1]
+
+        # truncate
+        if len(text) > self.MAX_MARKUP_LENGTH:
+            text = text[:self.MAX_MARKUP_LENGTH] + "..."
+
+        # remove newlines
+        text = text.replace("\n", "")
+
+        # escape problematic characters
+        text = escape(text)
+
+        return text
+
+    def __str__(self):
+        return "<Value text='%s'>" % self.text
 
 
 class NumberValue(Value):
-	def __str__(self):
-		return "<NumberValue text='%s'>" % self.text
+    def __str__(self):
+        return "<NumberValue text='%s'>" % self.text
 
 
 class StringValue(Value):
-	def __str__(self):
-		return "<StringValue text='%s'>" % self.text
+    def __str__(self):
+        return "<StringValue text='%s'>" % self.text
 
 
 class ConstantReferenceValue(Value):
-	@property
-	def markup(self):
-		return "<i>%s</i>" % escape(self.text)
-	
-	def __str__(self):
-		return "<ReferenceValue text='%s'>" % self.text
+    @property
+    def markup(self):
+        return "<i>%s</i>" % escape(self.text)
+
+    def __str__(self):
+        return "<ReferenceValue text='%s'>" % self.text
 
 
 class Field(object):
-	def __init__(self):
-		self.name = None
-		self.value = []
-	
-	@property
-	def valueMarkup(self):
-		return " ".join([v.markup for v in self.value])
-	
-	@property
-	def valueString(self):
-		return " ".join([v.text for v in self.value])
-	
-	def __str__(self):
-		return "<Field name='%s' value='%s' />" % (self.name, self.valueString)
+    def __init__(self):
+        self.name = None
+        self.value = []
+
+    @property
+    def valueMarkup(self):
+        return " ".join([v.markup for v in self.value])
+
+    @property
+    def valueString(self):
+        return " ".join([v.text for v in self.value])
+
+    def __str__(self):
+        return "<Field name='%s' value='%s' />" % (self.name, self.valueString)
 
 
 class Entry(object):
-	def __init__(self):
-		self.type = None
-		self.key = None
-		self.start = None
-		self.end = None
-		self.fields = []
-	
-	def findField(self, name):
-		for field in self.fields:
-			if field.name == name:
-				return field
-		raise KeyError
-	
-	def __str__(self):
-		s = "<Entry type='%s' key='%s'>\n" % (self.type, self.key)
-		for field in self.fields:
-			s += "\t" + str(field) + "\n"
-		s += "</Entry>"
-		return s
+    def __init__(self):
+        self.type = None
+        self.key = None
+        self.start = None
+        self.end = None
+        self.fields = []
+
+    def findField(self, name):
+        for field in self.fields:
+            if field.name == name:
+                return field
+        raise KeyError
+
+    def __str__(self):
+        s = "<Entry type='%s' key='%s'>\n" % (self.type, self.key)
+        for field in self.fields:
+            s += "\t" + str(field) + "\n"
+        s += "</Entry>"
+        return s
 
 
 class Constant(object):
-	"""
-	A BibTeX string constant
-	"""
-	def __init__(self):
-		self.name = None
-		self.value = None
+    """
+    A BibTeX string constant
+    """
+    def __init__(self):
+        self.name = None
+        self.value = None
 
 
 class Document(object):
-	def __init__(self):
-		self.entries = []
-		self.constants = []
-	
-	def __str__(self):
-		s = "<Document>\n"
-		for entry in self.entries:
-			s += str(entry) + "\n"
-		s += "</Document>"
-		return s
-	
-	
-#	#
-#	# async parser feature
-#	#
-#	
-#	# TODO: put the __main__ part in another file
-#	
-#	import pickle
-#	import os
-#	
-#	from ..tools.util import Process
-#	from ..base.resources import PLUGIN_PATH
-#	
-#	# TODO: time pickle.loads() and pickle.dump()
-#	# TODO: support Process.abort()
-#	
-#	class AsyncParserRunner(Process):
-#		
-#		__log = getLogger("AsyncParserRunner")
-#		
-#		def parse(self, file):
-#			self.__pickled_object = None
-#			
-#			source_path = PLUGIN_PATH + "/src"
-#			self.__log.debug("chdir: %s" % source_path)
-#			os.chdir(source_path)
-#			
-#			self.execute("python %s/bibtex/parser.py %s %s" % (source_path, source_path, file.path))
-#		
-#		def _on_stdout(self, text):
-#			# Process._on_stdout
-#			self.__pickled_object = text
-#			
-#		def _on_stderr(self, text):
-#			# Process._on_stderr
-#			self.__log.debug("_on_stderr: %s" % text)
-#		
-#		def _on_abort(self):
-#			# Process._on_abort
-#			pass
-#		
-#		def _on_exit(self, condition):
-#			# Process._on_exit
-#			self.__log.debug("_on_exit")
-#			
-#			model = None
-#			
-#			if condition:
-#				self.__log.error("failed")
-#			else:
-#				model = pickle.loads(self.__pickled_object)
-#			
-#			self._on_parser_finished(model)
-#		
-#		def _on_parser_finished(self, model):
-#			"""
-#			To be overridden by the subclass
-#			"""
-	
-	
-		
-		
-		
+    def __init__(self):
+        self.entries = []
+        self.constants = []
+
+    def __str__(self):
+        s = "<Document>\n"
+        for entry in self.entries:
+            s += str(entry) + "\n"
+        s += "</Document>"
+        return s
+
+
+#    #
+#    # async parser feature
+#    #
+#
+#    # TODO: put the __main__ part in another file
+#
+#    import pickle
+#    import os
+#
+#    from ..tools.util import Process
+#    from ..base.resources import PLUGIN_PATH
+#
+#    # TODO: time pickle.loads() and pickle.dump()
+#    # TODO: support Process.abort()
+#
+#    class AsyncParserRunner(Process):
+#
+#        __log = getLogger("AsyncParserRunner")
+#
+#        def parse(self, file):
+#            self.__pickled_object = None
+#
+#            source_path = PLUGIN_PATH + "/src"
+#            self.__log.debug("chdir: %s" % source_path)
+#            os.chdir(source_path)
+#
+#            self.execute("python %s/bibtex/parser.py %s %s" % (source_path, source_path, file.path))
+#
+#        def _on_stdout(self, text):
+#            # Process._on_stdout
+#            self.__pickled_object = text
+#
+#        def _on_stderr(self, text):
+#            # Process._on_stderr
+#            self.__log.debug("_on_stderr: %s" % text)
+#
+#        def _on_abort(self):
+#            # Process._on_abort
+#            pass
+#
+#        def _on_exit(self, condition):
+#            # Process._on_exit
+#            self.__log.debug("_on_exit")
+#
+#            model = None
+#
+#            if condition:
+#                self.__log.error("failed")
+#            else:
+#                model = pickle.loads(self.__pickled_object)
+#
+#            self._on_parser_finished(model)
+#
+#        def _on_parser_finished(self, model):
+#            """
+#            To be overridden by the subclass
+#            """
+
+
+
+
+
diff --git a/latex/bibtex/validator.py b/latex/bibtex/validator.py
index bcbe87e..cb69552 100644
--- a/latex/bibtex/validator.py
+++ b/latex/bibtex/validator.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -29,61 +29,60 @@ from model import BibTeXModel
 
 
 class BibTeXValidator:
-	"""
-	This checks for
-	 - duplicate entry names
-	 - duplicate fields
-	 - missing required fields *
-	 - unused fields *
-	
-	*) relies on the BibTeX model definition
-	"""
-	
-	_log = getLogger("BibTeXValidator")
-	
-	def __init__(self):
-		self._model = BibTeXModel()
-	
-	def validate(self, document, file, issue_handler):
-		"""
-		@param document: a bibtex.parser.Document object
-		@param issue_handler: an object implementing IIssueHandler
-		"""
-		entry_keys = []
-		for entry in document.entries:
-			# check for duplicate keys
-			if entry.key in entry_keys:
-				issue_handler.issue(Issue("Duplicate key <b>%s</b>" % entry.key, entry.start, entry.end, file, Issue.SEVERITY_ERROR))
-			else:
-				entry_keys.append(entry.key)
-			
-			field_names = []
-			for field in entry.fields:
-				# check for duplicate fields
-				if field.name in field_names:
-					issue_handler.issue(Issue("Duplicate field <b>%s</b>" % field.name, entry.start, entry.end, file, Issue.SEVERITY_ERROR))
-				else:
-					field_names.append(field.name)
-			
-			try:
-				# check for missing required fields
-				required_field_names = set(map(lambda f: f.name, self._model.find_type(entry.type).required_fields))
-				missing_field_names = required_field_names.difference(set(field_names))
-				if len(missing_field_names) > 0:
-					issue_handler.issue(Issue("Possibly missing field(s): <b>%s</b>" % ",".join(missing_field_names), entry.start, entry.end, file, Issue.SEVERITY_WARNING))
-					
-				# check for unused fields
-				optional_field_names = set(map(lambda f: f.name, self._model.find_type(entry.type).optional_fields))
-				unused_field_names = set(field_names).difference(optional_field_names.union(required_field_names))
-				if len(unused_field_names) > 0:
-					issue_handler.issue(Issue("Possibly unused field(s): <b>%s</b>" % ",".join(unused_field_names), entry.start, entry.end, file, Issue.SEVERITY_WARNING))
-			except KeyError:
-				#self._log.debug("Type not found: %s" % entry.type)
-				pass
-
-		
-		
-		
-		
-		
-		
\ No newline at end of file
+    """
+    This checks for
+     - duplicate entry names
+     - duplicate fields
+     - missing required fields *
+     - unused fields *
+
+    *) relies on the BibTeX model definition
+    """
+
+    _log = getLogger("BibTeXValidator")
+
+    def __init__(self):
+        self._model = BibTeXModel()
+
+    def validate(self, document, file, issue_handler):
+        """
+        @param document: a bibtex.parser.Document object
+        @param issue_handler: an object implementing IIssueHandler
+        """
+        entry_keys = []
+        for entry in document.entries:
+            # check for duplicate keys
+            if entry.key in entry_keys:
+                issue_handler.issue(Issue("Duplicate key <b>%s</b>" % entry.key, entry.start, entry.end, file, Issue.SEVERITY_ERROR))
+            else:
+                entry_keys.append(entry.key)
+
+            field_names = []
+            for field in entry.fields:
+                # check for duplicate fields
+                if field.name in field_names:
+                    issue_handler.issue(Issue("Duplicate field <b>%s</b>" % field.name, entry.start, entry.end, file, Issue.SEVERITY_ERROR))
+                else:
+                    field_names.append(field.name)
+
+            try:
+                # check for missing required fields
+                required_field_names = set(map(lambda f: f.name, self._model.find_type(entry.type).required_fields))
+                missing_field_names = required_field_names.difference(set(field_names))
+                if len(missing_field_names) > 0:
+                    issue_handler.issue(Issue("Possibly missing field(s): <b>%s</b>" % ",".join(missing_field_names), entry.start, entry.end, file, Issue.SEVERITY_WARNING))
+
+                # check for unused fields
+                optional_field_names = set(map(lambda f: f.name, self._model.find_type(entry.type).optional_fields))
+                unused_field_names = set(field_names).difference(optional_field_names.union(required_field_names))
+                if len(unused_field_names) > 0:
+                    issue_handler.issue(Issue("Possibly unused field(s): <b>%s</b>" % ",".join(unused_field_names), entry.start, entry.end, file, Issue.SEVERITY_WARNING))
+            except KeyError:
+                #self._log.debug("Type not found: %s" % entry.type)
+                pass
+
+
+
+
+
+
diff --git a/latex/bibtex/views.py b/latex/bibtex/views.py
index e0d256b..ee5e538 100644
--- a/latex/bibtex/views.py
+++ b/latex/bibtex/views.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -41,256 +41,256 @@ GROUP_NONE, GROUP_TYPE, GROUP_AUTHOR, GROUP_YEAR = 1, 2, 3, 4
 
 
 class BibTeXOutlineView(BaseOutlineView):
-	"""
-	Special outline view for BibTeX
-	"""
-
-	_log = getLogger("BibTeXOutlineView")
-	
-	def __init__(self, context, editor):
-		BaseOutlineView.__init__(self, context, editor)
-		self._handlers = {}
-	
-	def init(self, context):
-		BaseOutlineView.init(self, context)
-		
-		self._grouping = GROUP_NONE
-		
-		# add grouping controls to toolbar
-		
-		self._item_none = Gtk.RadioMenuItem(None, "No Grouping")
-		self._item_type = Gtk.RadioMenuItem(self._item_none, "Group by Type")
-		self._item_author = Gtk.RadioMenuItem(self._item_none, "Group by Author")
-		self._item_year = Gtk.RadioMenuItem(self._item_none, "Group by Year")
-		
-		self._preferences = Preferences()
-		
-		grouping = self._preferences.get("BibtexOutlineGrouping", GROUP_NONE)
-		if grouping == GROUP_NONE:
-			self._item_none.set_active(True)
-		elif grouping == GROUP_TYPE:
-			self._item_type.set_active(True)
-		elif grouping == GROUP_AUTHOR:
-			self._item_author.set_active(True)
-		elif grouping == GROUP_YEAR:
-			self._item_year.set_active(True)
-		
-		self._handlers[self._item_none] = self._item_none.connect("toggled", self._on_grouping_toggled)
-		self._handlers[self._item_type] = self._item_type.connect("toggled", self._on_grouping_toggled)
-		self._handlers[self._item_author] = self._item_author.connect("toggled", self._on_grouping_toggled)
-		self._handlers[self._item_year] = self._item_year.connect("toggled", self._on_grouping_toggled)
-
-		menu = Gtk.Menu()
-		menu.add(self._item_none)
-		menu.add(self._item_type)
-		menu.add(self._item_author)
-		menu.add(self._item_year)
-		menu.show_all()
-
-		tool_button = Gtk.MenuToolButton(Gtk.STOCK_SORT_DESCENDING)
-		tool_button.set_menu(menu)
-		tool_button.show()
-		
-		self._toolbar.insert(tool_button, -1)
-	
-	def _on_grouping_toggled(self, toggle_button):
-		if self._item_none.get_active():
-			self._grouping = GROUP_NONE
-		elif self._item_type.get_active():
-			self._grouping = GROUP_TYPE
-		elif self._item_author.get_active():
-			self._grouping = GROUP_AUTHOR
-		elif self._item_year.get_active():
-			self._grouping = GROUP_YEAR
-		
-		if self._outline:
-			self._update()
-	
-	def _update(self):
-		#t = time.clock()
-		self._offset_map = OutlineOffsetMap()
-		OutlineConverter().convert(self._store, self._outline, self._offset_map, self._grouping)
-		#dt = time.clock() - t
-		#self._log.debug("OutlineConverter.convert: %fs" % dt)
-	
-	def set_outline(self, outline):
-		"""
-		Display the given model
-		"""
-		self._outline = outline
-		
-		self.assure_init()
-		self._save_state()
-		self._update()
-		self._restore_state()
-		
-	def _on_node_selected(self, node):
-		"""
-		An outline node has been selected
-		"""
-		if isinstance(node, Entry):
-			self._editor.select(node.start, node.end)
-			
-	def destroy(self):
-		for obj in self._handlers:
-			obj.disconnect(self._handlers[obj])
-		BaseOutlineView.destroy(self)
+    """
+    Special outline view for BibTeX
+    """
+
+    _log = getLogger("BibTeXOutlineView")
+
+    def __init__(self, context, editor):
+        BaseOutlineView.__init__(self, context, editor)
+        self._handlers = {}
+
+    def init(self, context):
+        BaseOutlineView.init(self, context)
+
+        self._grouping = GROUP_NONE
+
+        # add grouping controls to toolbar
+
+        self._item_none = Gtk.RadioMenuItem(None, "No Grouping")
+        self._item_type = Gtk.RadioMenuItem(self._item_none, "Group by Type")
+        self._item_author = Gtk.RadioMenuItem(self._item_none, "Group by Author")
+        self._item_year = Gtk.RadioMenuItem(self._item_none, "Group by Year")
+
+        self._preferences = Preferences()
+
+        grouping = self._preferences.get("BibtexOutlineGrouping", GROUP_NONE)
+        if grouping == GROUP_NONE:
+            self._item_none.set_active(True)
+        elif grouping == GROUP_TYPE:
+            self._item_type.set_active(True)
+        elif grouping == GROUP_AUTHOR:
+            self._item_author.set_active(True)
+        elif grouping == GROUP_YEAR:
+            self._item_year.set_active(True)
+
+        self._handlers[self._item_none] = self._item_none.connect("toggled", self._on_grouping_toggled)
+        self._handlers[self._item_type] = self._item_type.connect("toggled", self._on_grouping_toggled)
+        self._handlers[self._item_author] = self._item_author.connect("toggled", self._on_grouping_toggled)
+        self._handlers[self._item_year] = self._item_year.connect("toggled", self._on_grouping_toggled)
+
+        menu = Gtk.Menu()
+        menu.add(self._item_none)
+        menu.add(self._item_type)
+        menu.add(self._item_author)
+        menu.add(self._item_year)
+        menu.show_all()
+
+        tool_button = Gtk.MenuToolButton(Gtk.STOCK_SORT_DESCENDING)
+        tool_button.set_menu(menu)
+        tool_button.show()
+
+        self._toolbar.insert(tool_button, -1)
+
+    def _on_grouping_toggled(self, toggle_button):
+        if self._item_none.get_active():
+            self._grouping = GROUP_NONE
+        elif self._item_type.get_active():
+            self._grouping = GROUP_TYPE
+        elif self._item_author.get_active():
+            self._grouping = GROUP_AUTHOR
+        elif self._item_year.get_active():
+            self._grouping = GROUP_YEAR
+
+        if self._outline:
+            self._update()
+
+    def _update(self):
+        #t = time.clock()
+        self._offset_map = OutlineOffsetMap()
+        OutlineConverter().convert(self._store, self._outline, self._offset_map, self._grouping)
+        #dt = time.clock() - t
+        #self._log.debug("OutlineConverter.convert: %fs" % dt)
+
+    def set_outline(self, outline):
+        """
+        Display the given model
+        """
+        self._outline = outline
+
+        self.assure_init()
+        self._save_state()
+        self._update()
+        self._restore_state()
+
+    def _on_node_selected(self, node):
+        """
+        An outline node has been selected
+        """
+        if isinstance(node, Entry):
+            self._editor.select(node.start, node.end)
+
+    def destroy(self):
+        for obj in self._handlers:
+            obj.disconnect(self._handlers[obj])
+        BaseOutlineView.destroy(self)
 
 
 class OutlineConverter(object):
-	"""
-	This converts a BibTeX document to a Gtk.TreeStore and realizes the 
-	grouping feature
-	"""
-	
-	_ICON_ENTRY = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/document.png"))
-	_ICON_FIELD = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/field.png"))
-	_ICON_AUTHOR = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/users.png"))
-	_ICON_YEAR = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/calendar.png"))
-	_ICON_TYPE = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/documents.png"))
-	
-	def convert(self, tree_store, document, offset_map, grouping=GROUP_NONE):
-		"""
-		Convert a BibTeX document model into a Gtk.TreeStore
-		
-		@param tree_store: the Gtk.TreeStore to fill
-		@param document: the BibTeX document model (bibtex.parser.Document object)
-		@param offset_map: the OutlineOffsetMap object to be filled 
-		@param grouping: the grouping to use: GROUP_NONE|GROUP_TYPE|GROUP_AUTHOR|GROUP_YEAR
-		"""
-		
-		color = Preferences().get("light-foreground-color")
-		
-		tree_store.clear()
-		
-		if grouping == GROUP_TYPE:
-			# group by entry type
-			
-			groups = {}		# maps lower case entry type names to lists of entries
-			
-			# collect
-			for entry in document.entries:
-				try:
-					entryList = groups[entry.type]
-					entryList.append(entry)
-				except KeyError:
-					groups[entry.type] = [entry]
-			
-			# sort by type
-			entryTypes = groups.keys()
-			entryTypes.sort()
-			
-			# build tree
-			for entryType in entryTypes:
-				entries = groups[entryType]
-				
-				parentType = tree_store.append(None, ["%s <span color='%s'>%s</span>" % (escape(entryType), color, len(entries)), self._ICON_TYPE, None])
-				
-				for entry in entries:
-					parentEntry = tree_store.append(parentType, [escape(entry.key), self._ICON_ENTRY, entry])
-					
-					offset_map.put(entry.start, tree_store.get_path(parentEntry))
-					
-					for field in entry.fields:
-						tree_store.append(parentEntry, ["<span color='%s'>%s</span> %s" % (color, escape(field.name), field.valueMarkup),
-											self._ICON_FIELD, field])
-		
-		elif grouping == GROUP_YEAR:
-			# group by year
-			
-			NO_YEAR_IDENT = "<i>n/a</i>"
-			
-			groups = {}
-			
-			# collect
-			for entry in document.entries:
-				try:
-					year = str(entry.findField("year").valueString)
-				except KeyError:
-					# no year, so put this in an extra group
-					year = NO_YEAR_IDENT
-					
-				try:
-					entries = groups[year]
-					entries.append(entry)
-				except KeyError:
-					groups[year] = [entry]
-			
-			# sort by year
-			years = groups.keys()
-			years.sort()
-			
-			# build tree
-			for year in years:
-				entries = groups[year]
-				
-				parentYear = tree_store.append(None, ["%s <span color='%s'>%s</span>" % (year, color, len(entries)), self._ICON_YEAR, None])
-				
-				for entry in entries:
-					parentEntry = tree_store.append(parentYear, ["%s <span color='%s'>%s</span>" % (escape(entry.key), color, escape(entry.type)),
-															 self._ICON_ENTRY, entry])
-					
-					offset_map.put(entry.start, tree_store.get_path(parentEntry))
-					
-					for field in entry.fields:
-						tree_store.append(parentEntry, ["<span color='%s'>%s</span> %s" % (color, escape(field.name), field.valueMarkup),
-											self._ICON_FIELD, field])
-		
-		elif grouping == GROUP_AUTHOR:
-			
-			NA_IDENT = "Unknown Author"
-			
-			groups = {}
-			
-			# group
-			for entry in document.entries:
-				# split list of authors
-				try:
-					authorValue = str(entry.findField("author").valueString)
-					authors = [a.strip() for a in authorValue.split(" and ")]
-				except KeyError:
-					# no year, so put this in an extra group
-					authors = [NA_IDENT]
-				
-				# add to group(s)
-				for author in authors:
-					try:
-						entries = groups[author]
-						entries.append(entry)
-					except KeyError:
-						groups[author] = [entry]
-			
-			# sort
-			authors = groups.keys()
-			authors.sort()
-			
-			# build tree
-			for author in authors:
-				entries = groups[author]
-				
-				parent = tree_store.append(None, ["%s <span color='%s'>%s</span>" % (escape(author), color, len(entries)), self._ICON_AUTHOR, None])
-				
-				for entry in entries:
-					parentEntry = tree_store.append(parent, ["%s <span color='%s'>%s</span>" % (escape(entry.key), color, escape(entry.type)),
-															 self._ICON_ENTRY, entry])
-					
-					offset_map.put(entry.start, tree_store.get_path(parentEntry))
-					
-					for field in entry.fields:
-						tree_store.append(parentEntry, ["<span color='%s'>%s</span> %s" % (color, escape(field.name), field.valueMarkup),
-											self._ICON_FIELD, field])
-		
-		else:
-			# no grouping, display entries and fields in a tree
-			
-			for entry in document.entries:
-				parent = tree_store.append(None, ["%s <span color='%s'>%s</span>" % (escape(entry.key), color, escape(entry.type)), self._ICON_ENTRY, entry])
-				
-				offset_map.put(entry.start, tree_store.get_path(parent))
-				
-				for field in entry.fields:
-					tree_store.append(parent, ["<span color='%s'>%s</span> %s" % (color, escape(field.name), field.valueMarkup), self._ICON_FIELD, field])
-				
-				
-				
-				
+    """
+    This converts a BibTeX document to a Gtk.TreeStore and realizes the
+    grouping feature
+    """
+
+    _ICON_ENTRY = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/document.png"))
+    _ICON_FIELD = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/field.png"))
+    _ICON_AUTHOR = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/users.png"))
+    _ICON_YEAR = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/calendar.png"))
+    _ICON_TYPE = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/documents.png"))
+
+    def convert(self, tree_store, document, offset_map, grouping=GROUP_NONE):
+        """
+        Convert a BibTeX document model into a Gtk.TreeStore
+
+        @param tree_store: the Gtk.TreeStore to fill
+        @param document: the BibTeX document model (bibtex.parser.Document object)
+        @param offset_map: the OutlineOffsetMap object to be filled
+        @param grouping: the grouping to use: GROUP_NONE|GROUP_TYPE|GROUP_AUTHOR|GROUP_YEAR
+        """
+
+        color = Preferences().get("light-foreground-color")
+
+        tree_store.clear()
+
+        if grouping == GROUP_TYPE:
+            # group by entry type
+
+            groups = {}        # maps lower case entry type names to lists of entries
+
+            # collect
+            for entry in document.entries:
+                try:
+                    entryList = groups[entry.type]
+                    entryList.append(entry)
+                except KeyError:
+                    groups[entry.type] = [entry]
+
+            # sort by type
+            entryTypes = groups.keys()
+            entryTypes.sort()
+
+            # build tree
+            for entryType in entryTypes:
+                entries = groups[entryType]
+
+                parentType = tree_store.append(None, ["%s <span color='%s'>%s</span>" % (escape(entryType), color, len(entries)), self._ICON_TYPE, None])
+
+                for entry in entries:
+                    parentEntry = tree_store.append(parentType, [escape(entry.key), self._ICON_ENTRY, entry])
+
+                    offset_map.put(entry.start, tree_store.get_path(parentEntry))
+
+                    for field in entry.fields:
+                        tree_store.append(parentEntry, ["<span color='%s'>%s</span> %s" % (color, escape(field.name), field.valueMarkup),
+                                            self._ICON_FIELD, field])
+
+        elif grouping == GROUP_YEAR:
+            # group by year
+
+            NO_YEAR_IDENT = "<i>n/a</i>"
+
+            groups = {}
+
+            # collect
+            for entry in document.entries:
+                try:
+                    year = str(entry.findField("year").valueString)
+                except KeyError:
+                    # no year, so put this in an extra group
+                    year = NO_YEAR_IDENT
+
+                try:
+                    entries = groups[year]
+                    entries.append(entry)
+                except KeyError:
+                    groups[year] = [entry]
+
+            # sort by year
+            years = groups.keys()
+            years.sort()
+
+            # build tree
+            for year in years:
+                entries = groups[year]
+
+                parentYear = tree_store.append(None, ["%s <span color='%s'>%s</span>" % (year, color, len(entries)), self._ICON_YEAR, None])
+
+                for entry in entries:
+                    parentEntry = tree_store.append(parentYear, ["%s <span color='%s'>%s</span>" % (escape(entry.key), color, escape(entry.type)),
+                                                             self._ICON_ENTRY, entry])
+
+                    offset_map.put(entry.start, tree_store.get_path(parentEntry))
+
+                    for field in entry.fields:
+                        tree_store.append(parentEntry, ["<span color='%s'>%s</span> %s" % (color, escape(field.name), field.valueMarkup),
+                                            self._ICON_FIELD, field])
+
+        elif grouping == GROUP_AUTHOR:
+
+            NA_IDENT = "Unknown Author"
+
+            groups = {}
+
+            # group
+            for entry in document.entries:
+                # split list of authors
+                try:
+                    authorValue = str(entry.findField("author").valueString)
+                    authors = [a.strip() for a in authorValue.split(" and ")]
+                except KeyError:
+                    # no year, so put this in an extra group
+                    authors = [NA_IDENT]
+
+                # add to group(s)
+                for author in authors:
+                    try:
+                        entries = groups[author]
+                        entries.append(entry)
+                    except KeyError:
+                        groups[author] = [entry]
+
+            # sort
+            authors = groups.keys()
+            authors.sort()
+
+            # build tree
+            for author in authors:
+                entries = groups[author]
+
+                parent = tree_store.append(None, ["%s <span color='%s'>%s</span>" % (escape(author), color, len(entries)), self._ICON_AUTHOR, None])
+
+                for entry in entries:
+                    parentEntry = tree_store.append(parent, ["%s <span color='%s'>%s</span>" % (escape(entry.key), color, escape(entry.type)),
+                                                             self._ICON_ENTRY, entry])
+
+                    offset_map.put(entry.start, tree_store.get_path(parentEntry))
+
+                    for field in entry.fields:
+                        tree_store.append(parentEntry, ["<span color='%s'>%s</span> %s" % (color, escape(field.name), field.valueMarkup),
+                                            self._ICON_FIELD, field])
+
+        else:
+            # no grouping, display entries and fields in a tree
+
+            for entry in document.entries:
+                parent = tree_store.append(None, ["%s <span color='%s'>%s</span>" % (escape(entry.key), color, escape(entry.type)), self._ICON_ENTRY, entry])
+
+                offset_map.put(entry.start, tree_store.get_path(parent))
+
+                for field in entry.fields:
+                    tree_store.append(parent, ["<span color='%s'>%s</span> %s" % (color, escape(field.name), field.valueMarkup), self._ICON_FIELD, field])
+
+
+
+
diff --git a/latex/issues.py b/latex/issues.py
index 378e2c7..fe7f54f 100644
--- a/latex/issues.py
+++ b/latex/issues.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -26,128 +26,127 @@ from logging import getLogger
 
 
 class IIssueHandler(object):
-	"""
-	A class implementing this interface handles issues
-	"""
-	
-	def clear(self):
-		"""
-		Remove all partitions and issues
-		"""
-		raise NotImplementedError
-	
-	def issue(self, issue):
-		"""
-		An issue has occured
-		"""
-		raise NotImplementedError
-	
-	
+    """
+    A class implementing this interface handles issues
+    """
+
+    def clear(self):
+        """
+        Remove all partitions and issues
+        """
+        raise NotImplementedError
+
+    def issue(self, issue):
+        """
+        An issue has occured
+        """
+        raise NotImplementedError
+
+
 class MockIssueHandler(IIssueHandler):
-	"""
-	This is used by the BibTeXDocumentCache
-	"""
-	__log = getLogger("MockIssueHandler")
-	
-	def clear(self):
-		pass
-	
-	def issue(self, issue):
-		self.__log.debug(str(issue))
+    """
+    This is used by the BibTeXDocumentCache
+    """
+    __log = getLogger("MockIssueHandler")
+
+    def clear(self):
+        pass
+
+    def issue(self, issue):
+        self.__log.debug(str(issue))
 
 
 class IStructuredIssueHandler(object):
-	def clear(self):
-		"""
-		Remove all partitions and issues
-		"""
-		raise NotImplementedError
-	
-	def add_partition(self, label, state, parent_partition_id):
-		"""
-		Add a new partition
-		
-		@param label: a label used in the UI
-		@param state: the initial state descriptor for the partition
-		@param parent_partition_id: the partition under which this one should be 
-				created (None for top-level)
-		
-		@return: a unique id for the partition
-		"""
-		raise NotImplementedError
-	
-	def set_partition_state(self, partition_id, state):
-		"""
-		@param partition_id: a partition id as returned by add_partition
-		@param state: any string
-		"""
-		raise NotImplementedError
-	
-	def set_abort_enabled(self, enabled, method):
-		"""
-		@param enabled: if True a job may be aborted
-		@param method: a method that is may be called to abort a running job
-		"""
-		raise NotImplementedError
-	
-	def append_issues(self, partition_id, issues):
-		"""
-		An issue occured
-		
-		@param issue: an Issue object
-		@param partition: a partition id as returned by add_partition
-		"""
-		raise NotImplementedError
-	
+    def clear(self):
+        """
+        Remove all partitions and issues
+        """
+        raise NotImplementedError
+
+    def add_partition(self, label, state, parent_partition_id):
+        """
+        Add a new partition
+
+        @param label: a label used in the UI
+        @param state: the initial state descriptor for the partition
+        @param parent_partition_id: the partition under which this one should be
+                created (None for top-level)
+
+        @return: a unique id for the partition
+        """
+        raise NotImplementedError
+
+    def set_partition_state(self, partition_id, state):
+        """
+        @param partition_id: a partition id as returned by add_partition
+        @param state: any string
+        """
+        raise NotImplementedError
+
+    def set_abort_enabled(self, enabled, method):
+        """
+        @param enabled: if True a job may be aborted
+        @param method: a method that is may be called to abort a running job
+        """
+        raise NotImplementedError
+
+    def append_issues(self, partition_id, issues):
+        """
+        An issue occured
+
+        @param issue: an Issue object
+        @param partition: a partition id as returned by add_partition
+        """
+        raise NotImplementedError
+
 
 class MockStructuredIssueHandler(IStructuredIssueHandler):
-	"""
-	Used by the PreviewRenderer
-	"""
-	def clear(self):
-		pass
-	
-	def add_partition(self, label, state, parent_partition_id):
-		pass
-	
-	def set_partition_state(self, partition_id, state):
-		pass
-	
-	def append_issues(self, partition_id, issues):
-		pass
-	
-	def set_abort_enabled(self, enabled, method):
-		pass
+    """
+    Used by the PreviewRenderer
+    """
+    def clear(self):
+        pass
+
+    def add_partition(self, label, state, parent_partition_id):
+        pass
+
+    def set_partition_state(self, partition_id, state):
+        pass
+
+    def append_issues(self, partition_id, issues):
+        pass
+
+    def set_abort_enabled(self, enabled, method):
+        pass
 
 
 class Issue(object):
-	"""
-	An issue can be a warning, an error, an info or a task that occures or is
-	recognized during parsing and validation of a source file
-	"""
-	
-	SEVERITY_WARNING, SEVERITY_ERROR, SEVERITY_INFO, SEVERITY_TASK = 1, 2, 3, 4
-	
-	POSITION_OFFSET, POSITION_LINE = 1, 2
-	
-	def __init__(self, message, start, end, file, severity, position_type=POSITION_OFFSET):
-		"""
-		@param message: a str in Pango markup
-		@param start: the start offset of the issue
-		@param end: the end offset
-		@param file: the File object representing the file the issue occured in
-		@param severity: one of SEVERITY_*
-		"""
-		self.message = message
-		self.start = start
-		self.end = end
-		self.file = file
-		self.severity = severity
-		self.position_type = position_type
-		
-	def __str__(self):
-		return "Issue{'%s', %s, %s, %s, %s}" % (self.message, self.start, self.end, self.file, self.severity)
-	
-	
-	
-	
\ No newline at end of file
+    """
+    An issue can be a warning, an error, an info or a task that occures or is
+    recognized during parsing and validation of a source file
+    """
+
+    SEVERITY_WARNING, SEVERITY_ERROR, SEVERITY_INFO, SEVERITY_TASK = 1, 2, 3, 4
+
+    POSITION_OFFSET, POSITION_LINE = 1, 2
+
+    def __init__(self, message, start, end, file, severity, position_type=POSITION_OFFSET):
+        """
+        @param message: a str in Pango markup
+        @param start: the start offset of the issue
+        @param end: the end offset
+        @param file: the File object representing the file the issue occured in
+        @param severity: one of SEVERITY_*
+        """
+        self.message = message
+        self.start = start
+        self.end = end
+        self.file = file
+        self.severity = severity
+        self.position_type = position_type
+
+    def __str__(self):
+        return "Issue{'%s', %s, %s, %s, %s}" % (self.message, self.start, self.end, self.file, self.severity)
+
+
+
diff --git a/latex/latex/__init__.py b/latex/latex/__init__.py
index b9b1edb..f3f8817 100644
--- a/latex/latex/__init__.py
+++ b/latex/latex/__init__.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -23,14 +23,14 @@ latex
 """
 
 class LaTeXSource(object):
-	"""
-	This connects a portion of source with some required packages
-	"""
-	def __init__(self, source, packages=[]):
-		self.source = source
-		self.packages = packages
-		
-		
+    """
+    This connects a portion of source with some required packages
+    """
+    def __init__(self, source, packages=[]):
+        self.source = source
+        self.packages = packages
+
+
 from logging import getLogger
 
 # TODO: use ElementTree
@@ -41,68 +41,68 @@ from ..base import File
 
 
 class PropertyFile(dict):
-	"""
-	A property file is a hidden XML file that holds meta data for exactly one file.
-	It can be used to store the master file of a LaTeX document fragment.
-	"""
-	
-	__log = getLogger("PropertyFile")
-	
-	# TODO: insert value as TEXT node
-	
-	def __init__(self, file):
-		"""
-		Create or load the property file for a given File
-		"""
-		self.__file = File("%s/.%s.properties.xml" % (file.dirname, file.basename))
-		
-		try:
-			self.__dom = minidom.parse(self.__file.path)
-			
-			for property_node in self.__dom.getElementsByTagName("property"):
-				k = property_node.getAttribute("key")
-				v = property_node.getAttribute("value")
-				self.__setitem__(k, v)
-				
-		except IOError:
-			self.__log.debug("File %s not found, creating empty one" % self.__file)
-			
-			self.__dom = minidom.getDOMImplementation().createDocument(None, "properties", None)
-		
-		except ExpatError, e:
-			self.__log.error("Error parsing %s: %s" % (self.__file, e))
-		
-	
-	def __find_node(self, k):
-		for node in self.__dom.getElementsByTagName("property"):
-			if node.getAttribute("key") == str(k):
-				return node
-		raise KeyError
-	
-	def __getitem__(self, k):
-		return self.__find_node(k).getAttribute("value")
-	
-	def __setitem__(self, k, v):
-		try:
-			self.__find_node(k).setAttribute("value", str(v))
-		except KeyError:
-			node = self.__dom.createElement("property")
-			node.setAttribute("key", str(k))
-			node.setAttribute("value", str(v))
-			self.__dom.documentElement.appendChild(node)
-	
-	def save(self):
-		filename = self.__file.path
-		
-		if self.__file.exists:
-			mode = "w"
-		else:
-			mode = "a"
-		
-		try:
-			f = open(filename, mode)
-			f.write(self.__dom.toxml())
-			f.close()
-			self.__log.debug("Saved to %s" % filename)
-		except IOError, e:
-			self.__log.error("Error saving %s: %s" % (filename, e))
\ No newline at end of file
+    """
+    A property file is a hidden XML file that holds meta data for exactly one file.
+    It can be used to store the master file of a LaTeX document fragment.
+    """
+
+    __log = getLogger("PropertyFile")
+
+    # TODO: insert value as TEXT node
+
+    def __init__(self, file):
+        """
+        Create or load the property file for a given File
+        """
+        self.__file = File("%s/.%s.properties.xml" % (file.dirname, file.basename))
+
+        try:
+            self.__dom = minidom.parse(self.__file.path)
+
+            for property_node in self.__dom.getElementsByTagName("property"):
+                k = property_node.getAttribute("key")
+                v = property_node.getAttribute("value")
+                self.__setitem__(k, v)
+
+        except IOError:
+            self.__log.debug("File %s not found, creating empty one" % self.__file)
+
+            self.__dom = minidom.getDOMImplementation().createDocument(None, "properties", None)
+
+        except ExpatError, e:
+            self.__log.error("Error parsing %s: %s" % (self.__file, e))
+
+
+    def __find_node(self, k):
+        for node in self.__dom.getElementsByTagName("property"):
+            if node.getAttribute("key") == str(k):
+                return node
+        raise KeyError
+
+    def __getitem__(self, k):
+        return self.__find_node(k).getAttribute("value")
+
+    def __setitem__(self, k, v):
+        try:
+            self.__find_node(k).setAttribute("value", str(v))
+        except KeyError:
+            node = self.__dom.createElement("property")
+            node.setAttribute("key", str(k))
+            node.setAttribute("value", str(v))
+            self.__dom.documentElement.appendChild(node)
+
+    def save(self):
+        filename = self.__file.path
+
+        if self.__file.exists:
+            mode = "w"
+        else:
+            mode = "a"
+
+        try:
+            f = open(filename, mode)
+            f.write(self.__dom.toxml())
+            f.close()
+            self.__log.debug("Saved to %s" % filename)
+        except IOError, e:
+            self.__log.error("Error saving %s: %s" % (filename, e))
\ No newline at end of file
diff --git a/latex/latex/actions.py b/latex/latex/actions.py
index 32f1b1b..c190e79 100644
--- a/latex/latex/actions.py
+++ b/latex/latex/actions.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -34,522 +34,522 @@ from ..tools import ToolRunner
 from .editor import LaTeXEditor
 from .parser import LaTeXParser, Node
 from .dialogs import UseBibliographyDialog, InsertGraphicsDialog, InsertTableDialog, \
-					InsertListingDialog, BuildImageDialog, SaveAsTemplateDialog, \
-					NewDocumentDialog, ChooseMasterDialog
+                    InsertListingDialog, BuildImageDialog, SaveAsTemplateDialog, \
+                    NewDocumentDialog, ChooseMasterDialog
 from . import LaTeXSource, PropertyFile
 
 class LaTeXAction(Action):
-	extensions = Preferences().get("latex-extensions").split(",")
+    extensions = Preferences().get("latex-extensions").split(",")
 
 
 class LaTeXIconAction(IconAction):
-	extensions = Preferences().get("latex-extensions").split(",")
+    extensions = Preferences().get("latex-extensions").split(",")
 
 
 class LaTeXTemplateAction(LaTeXIconAction):
-	"""
-	Utility base class for quickly defining Actions inserting a LaTeX template
-	"""
-	accelerator = None
-	
-	icon_name = None
-	template_source = None
-	packages = []
-	
-	@property
-	def icon(self):
-		return File(find_resource("icons/%s.png" % self.icon_name))
-	
-	def activate(self, context):
-		context.active_editor.insert(LaTeXSource(Template(self.template_source), self.packages))
+    """
+    Utility base class for quickly defining Actions inserting a LaTeX template
+    """
+    accelerator = None
+
+    icon_name = None
+    template_source = None
+    packages = []
+
+    @property
+    def icon(self):
+        return File(find_resource("icons/%s.png" % self.icon_name))
+
+    def activate(self, context):
+        context.active_editor.insert(LaTeXSource(Template(self.template_source), self.packages))
 
 
 class LaTeXMenuAction(LaTeXAction):
-	
-	label = "LaTeX"
-	stock_id = None
-	accelerator = None
-	tooltip = None
-	
-	def activate(self, context):
-		pass
+
+    label = "LaTeX"
+    stock_id = None
+    accelerator = None
+    tooltip = None
+
+    def activate(self, context):
+        pass
 
 
 class LaTeXNewAction(Action):
-	label = "New LaTeX Document..."
-	stock_id = Gtk.STOCK_NEW
-	accelerator = "<Ctrl><Alt>N"
-	tooltip = "Create a new LaTeX document"
-	
-	_dialog = None
-	
-	def activate(self, context):
-		if not self._dialog:
-			self._dialog = NewDocumentDialog()
-		
-		# we may not open the empty file and insert a Temlate here
-		# because WindowContext.activate_editor calls gedit.Window.create_tab_from_uri 
-		# which is async
-		
-		if self._dialog.run() == 1:
-			file = self._dialog.file
-			file.create(self._dialog.source)
-			context.activate_editor(file)
+    label = "New LaTeX Document..."
+    stock_id = Gtk.STOCK_NEW
+    accelerator = "<Ctrl><Alt>N"
+    tooltip = "Create a new LaTeX document"
+
+    _dialog = None
+
+    def activate(self, context):
+        if not self._dialog:
+            self._dialog = NewDocumentDialog()
+
+        # we may not open the empty file and insert a Temlate here
+        # because WindowContext.activate_editor calls gedit.Window.create_tab_from_uri
+        # which is async
+
+        if self._dialog.run() == 1:
+            file = self._dialog.file
+            file.create(self._dialog.source)
+            context.activate_editor(file)
 
 
 class LaTeXChooseMasterAction(LaTeXAction):
-	_log = getLogger("LaTeXChooseMasterAction")
-	
-	label = "Choose Master Document..."
-	stock_id = None
-	accelerator = None
-	tooltip = None
-	
-	def activate(self, context):
-		editor = context.active_editor
-		assert type(editor) is LaTeXEditor
-		
-		file = editor._file		# FIXME: access to private member
-		
-		# load property file
-		property_file = PropertyFile(file)
-		
-		master_filename = ChooseMasterDialog().run(file.dirname)
-		if master_filename:
-			# relativize the master filename
-			master_filename = File(master_filename).relativize(file.dirname, True)
-			
-			property_file["MasterFilename"] = master_filename
-			property_file.save()
-		
-		
+    _log = getLogger("LaTeXChooseMasterAction")
+
+    label = "Choose Master Document..."
+    stock_id = None
+    accelerator = None
+    tooltip = None
+
+    def activate(self, context):
+        editor = context.active_editor
+        assert type(editor) is LaTeXEditor
+
+        file = editor._file        # FIXME: access to private member
+
+        # load property file
+        property_file = PropertyFile(file)
+
+        master_filename = ChooseMasterDialog().run(file.dirname)
+        if master_filename:
+            # relativize the master filename
+            master_filename = File(master_filename).relativize(file.dirname, True)
+
+            property_file["MasterFilename"] = master_filename
+            property_file.save()
+
+
 class LaTeXCloseEnvironmentAction(LaTeXIconAction):
-	_log = getLogger("LaTeXCloseEnvironmentAction")
-	
-	label = "Close Nearest Environment"
-	icon = File(find_resource("icons/close_env.png"))
-	accelerator = "<Ctrl><Alt>E"
-	tooltip = "Close the nearest TeX environment at left of the cursor"
-	
-	def activate(self, context):
-		# FIXME: use the document model of the Editor
-		
-		editor = context.active_editor
-			
-		assert type(editor) is LaTeXEditor
-		
-		# push environments on stack and find nearest one to close
-		
-		try:
-			self._stack = []
-			self._find_open_environments(LaTeXParser().parse(editor.content_at_left_of_cursor, None, MockIssueHandler()))
-			
-			if len(self._stack) > 0:
-				editor.insert("\\end{%s}" % self._stack[-1])
-			else:
-				self._log.debug("No environment to close")
-		except ValueError:
-			self._log.debug("Environments are malformed")
-		
-	def _find_open_environments(self, parent_node):
-		for node in parent_node:
-			recurse = True
-			if node.type == Node.COMMAND:
-				if node.value == "begin":
-					# push environment on stack
-					environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-					self._stack.append(environ)
-					
-				elif node.value == "end":
-					# pop from stack
-					environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-					try:
-						top_environ = self._stack.pop()
-						if top_environ != environ:
-							raise ValueError()
-					except IndexError:
-						raise ValueError()
-				
-				elif node.value == "newcommand":
-					recurse = False
-					
-			if recurse:
-				self._find_open_environments(node)
-
-		
+    _log = getLogger("LaTeXCloseEnvironmentAction")
+
+    label = "Close Nearest Environment"
+    icon = File(find_resource("icons/close_env.png"))
+    accelerator = "<Ctrl><Alt>E"
+    tooltip = "Close the nearest TeX environment at left of the cursor"
+
+    def activate(self, context):
+        # FIXME: use the document model of the Editor
+
+        editor = context.active_editor
+
+        assert type(editor) is LaTeXEditor
+
+        # push environments on stack and find nearest one to close
+
+        try:
+            self._stack = []
+            self._find_open_environments(LaTeXParser().parse(editor.content_at_left_of_cursor, None, MockIssueHandler()))
+
+            if len(self._stack) > 0:
+                editor.insert("\\end{%s}" % self._stack[-1])
+            else:
+                self._log.debug("No environment to close")
+        except ValueError:
+            self._log.debug("Environments are malformed")
+
+    def _find_open_environments(self, parent_node):
+        for node in parent_node:
+            recurse = True
+            if node.type == Node.COMMAND:
+                if node.value == "begin":
+                    # push environment on stack
+                    environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                    self._stack.append(environ)
+
+                elif node.value == "end":
+                    # pop from stack
+                    environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                    try:
+                        top_environ = self._stack.pop()
+                        if top_environ != environ:
+                            raise ValueError()
+                    except IndexError:
+                        raise ValueError()
+
+                elif node.value == "newcommand":
+                    recurse = False
+
+            if recurse:
+                self._find_open_environments(node)
+
+
 class LaTeXUseBibliographyAction(LaTeXIconAction):
-	_log = getLogger("LaTeXUseBibliographyAction")
-	
-	label = "Use Bibliography..."
-	icon = File(find_resource("icons/bib.png"))
-	accelerator = None
-	tooltip = "Use Bibliography"
-	
-	_dialog = None
-	
-	def activate(self, context):
-		if not self._dialog:
-			self._dialog = UseBibliographyDialog()
-			
-		source = self._dialog.run_dialog(context.active_editor.edited_file)
-		if source:
-			editor = context.active_editor
-			
-			assert type(editor) is LaTeXEditor
-			
-			editor.insert_at_position(source + "\n\n", LaTeXEditor.POSITION_BIBLIOGRAPHY)
-	
+    _log = getLogger("LaTeXUseBibliographyAction")
+
+    label = "Use Bibliography..."
+    icon = File(find_resource("icons/bib.png"))
+    accelerator = None
+    tooltip = "Use Bibliography"
+
+    _dialog = None
+
+    def activate(self, context):
+        if not self._dialog:
+            self._dialog = UseBibliographyDialog()
+
+        source = self._dialog.run_dialog(context.active_editor.edited_file)
+        if source:
+            editor = context.active_editor
+
+            assert type(editor) is LaTeXEditor
+
+            editor.insert_at_position(source + "\n\n", LaTeXEditor.POSITION_BIBLIOGRAPHY)
+
 
 class LaTeXFontFamilyAction(LaTeXIconAction):
-	menu_tool_action = True
-	
-	label = "Font Family"
-	accelerator = None
-	tooltip = "Font Family"
-	icon = File(find_resource("icons/bf.png"))
-	
-	def activate(self, context):
-		pass
+    menu_tool_action = True
+
+    label = "Font Family"
+    accelerator = None
+    tooltip = "Font Family"
+    icon = File(find_resource("icons/bf.png"))
+
+    def activate(self, context):
+        pass
 
 
 class LaTeXFontFamilyMenuAction(LaTeXAction):
-	label = "Font Family"
-	accelerator = None
-	tooltip = "Font Family"
-	stock_id = None
-	
-	def activate(self, context):
-		pass
+    label = "Font Family"
+    accelerator = None
+    tooltip = "Font Family"
+    stock_id = None
+
+    def activate(self, context):
+        pass
 
 
 class LaTeXBoldAction(LaTeXTemplateAction):
-	label = "Bold"
-	tooltip = "Bold"
-	icon_name = "bf"
-	template_source = "\\textbf{$_}"
+    label = "Bold"
+    tooltip = "Bold"
+    icon_name = "bf"
+    template_source = "\\textbf{$_}"
 
 
 class LaTeXItalicAction(LaTeXTemplateAction):
-	label = "Italic"
-	tooltip = "Italic"
-	icon_name = "it"
-	template_source = "\\textit{$_}"
-	
+    label = "Italic"
+    tooltip = "Italic"
+    icon_name = "it"
+    template_source = "\\textit{$_}"
+
 
 class LaTeXEmphasizeAction(LaTeXTemplateAction):
-	label = "Emphasize"
-	tooltip = "Emphasize"
-	icon_name = "it"
-	template_source = "\\emph{$_}"
-	
-	
+    label = "Emphasize"
+    tooltip = "Emphasize"
+    icon_name = "it"
+    template_source = "\\emph{$_}"
+
+
 class LaTeXUnderlineAction(LaTeXTemplateAction):
-	label = "Underline"
-	tooltip = "Underline"
-	icon_name = "underline"
-	template_source = "\\underline{$_}"
-	
-	
+    label = "Underline"
+    tooltip = "Underline"
+    icon_name = "underline"
+    template_source = "\\underline{$_}"
+
+
 class LaTeXSmallCapitalsAction(LaTeXTemplateAction):
-	label = "Small Capitals"
-	tooltip = "Small Capitals"
-	icon_name = "sc"
-	template_source = "\\textsc{$_}"
-	
-	
+    label = "Small Capitals"
+    tooltip = "Small Capitals"
+    icon_name = "sc"
+    template_source = "\\textsc{$_}"
+
+
 class LaTeXRomanAction(LaTeXTemplateAction):
-	label = "Roman"
-	tooltip = "Roman"
-	icon_name = "rm"
-	template_source = "\\textrm{$_}"
-	
-	
+    label = "Roman"
+    tooltip = "Roman"
+    icon_name = "rm"
+    template_source = "\\textrm{$_}"
+
+
 class LaTeXSansSerifAction(LaTeXTemplateAction):
-	label = "Sans Serif"
-	tooltip = "Sans Serif"
-	icon_name = "sf"
-	template_source = "\\textsf{$_}"
-	
-	
+    label = "Sans Serif"
+    tooltip = "Sans Serif"
+    icon_name = "sf"
+    template_source = "\\textsf{$_}"
+
+
 class LaTeXTypewriterAction(LaTeXTemplateAction):
-	label = "Typewriter"
-	tooltip = "Typewriter"
-	icon_name = "tt"
-	template_source = "\\texttt{$_}"
+    label = "Typewriter"
+    tooltip = "Typewriter"
+    icon_name = "tt"
+    template_source = "\\texttt{$_}"
 
 
 class LaTeXBlackboardBoldAction(LaTeXTemplateAction):
-	label = "Blackboard Bold"
-	tooltip = "Blackboard Bold"
-	icon_name = "bb"
-	packages = ["amsmath"]
-	template_source = "\ensuremath{\mathbb{$_}}"
-	
-	
+    label = "Blackboard Bold"
+    tooltip = "Blackboard Bold"
+    icon_name = "bb"
+    packages = ["amsmath"]
+    template_source = "\ensuremath{\mathbb{$_}}"
+
+
 class LaTeXCaligraphyAction(LaTeXTemplateAction):
-	label = "Caligraphy"
-	tooltip = "Caligraphy"
-	icon_name = "cal"
-	template_source = "\ensuremath{\mathcal{$_}}"
+    label = "Caligraphy"
+    tooltip = "Caligraphy"
+    icon_name = "cal"
+    template_source = "\ensuremath{\mathcal{$_}}"
 
 
 class LaTeXFrakturAction(LaTeXTemplateAction):
-	label = "Fraktur"
-	tooltip = "Fraktur"
-	icon_name = "frak"
-	packages = ["amsmath"]
-	template_source = "\ensuremath{\mathfrak{$_}}"
+    label = "Fraktur"
+    tooltip = "Fraktur"
+    icon_name = "frak"
+    packages = ["amsmath"]
+    template_source = "\ensuremath{\mathfrak{$_}}"
 
 
 class LaTeXItemizeAction(LaTeXTemplateAction):
-	label = "Itemize"
-	tooltip = "Itemize"
-	icon_name = "itemize"
-	template_source = "\\begin{itemize}\n\t\\item $_\n\\end{itemize}"
+    label = "Itemize"
+    tooltip = "Itemize"
+    icon_name = "itemize"
+    template_source = "\\begin{itemize}\n\t\\item $_\n\\end{itemize}"
 
 
 class LaTeXEnumerateAction(LaTeXTemplateAction):
-	label = "Enumerate"
-	tooltip = "Enumerate"
-	icon_name = "enumerate"
-	template_source = "\\begin{enumerate}\n\t\\item $_\n\\end{enumerate}"
+    label = "Enumerate"
+    tooltip = "Enumerate"
+    icon_name = "enumerate"
+    template_source = "\\begin{enumerate}\n\t\\item $_\n\\end{enumerate}"
 
 
 class LaTeXDescriptionAction(LaTeXTemplateAction):
-	label = "Description"
-	tooltip = "Description"
-	icon_name = "description"
-	template_source = "\\begin{description}\n\t\\item[$_]\n\\end{description}"
-	
+    label = "Description"
+    tooltip = "Description"
+    icon_name = "description"
+    template_source = "\\begin{description}\n\t\\item[$_]\n\\end{description}"
+
 
 class LaTeXStructureAction(LaTeXIconAction):
-	menu_tool_action = True
-	
-	label = "Structure"
-	accelerator = None
-	tooltip = "Structure"
-	icon = File(find_resource("icons/section.png"))
-	
-	def activate(self, context):
-		pass
+    menu_tool_action = True
+
+    label = "Structure"
+    accelerator = None
+    tooltip = "Structure"
+    icon = File(find_resource("icons/section.png"))
+
+    def activate(self, context):
+        pass
 
 
 class LaTeXStructureMenuAction(LaTeXAction):
-	label = "Structure"
-	accelerator = None
-	tooltip = "Structure"
-	stock_id = None
-	
-	def activate(self, context):
-		pass
+    label = "Structure"
+    accelerator = None
+    tooltip = "Structure"
+    stock_id = None
+
+    def activate(self, context):
+        pass
 
 
 class LaTeXPartAction(LaTeXTemplateAction):
-	label = "Part"
-	tooltip = "Part"
-	icon_name = "part"
-	template_source = "\\part{$_}"
+    label = "Part"
+    tooltip = "Part"
+    icon_name = "part"
+    template_source = "\\part{$_}"
 
 
 class LaTeXChapterAction(LaTeXTemplateAction):
-	label = "Chapter"
-	tooltip = "Chapter"
-	icon_name = "chapter"
-	template_source = "\\chapter{$_}"
+    label = "Chapter"
+    tooltip = "Chapter"
+    icon_name = "chapter"
+    template_source = "\\chapter{$_}"
+
 
-		
 class LaTeXSectionAction(LaTeXTemplateAction):
-	label = "Section"
-	tooltip = "Section"
-	icon_name = "section"
-	template_source = "\\section{$_}"
-		
+    label = "Section"
+    tooltip = "Section"
+    icon_name = "section"
+    template_source = "\\section{$_}"
+
 
 class LaTeXSubsectionAction(LaTeXTemplateAction):
-	label = "Subsection"
-	tooltip = "Subsection"
-	icon_name = "subsection"
-	template_source = "\\subsection{$_}"
-		
+    label = "Subsection"
+    tooltip = "Subsection"
+    icon_name = "subsection"
+    template_source = "\\subsection{$_}"
+
 
 class LaTeXParagraphAction(LaTeXTemplateAction):
-	label = "Paragraph"
-	tooltip = "Paragraph"
-	icon_name = "paragraph"
-	template_source = "\\paragraph{$_}"
-		
-		
+    label = "Paragraph"
+    tooltip = "Paragraph"
+    icon_name = "paragraph"
+    template_source = "\\paragraph{$_}"
+
+
 class LaTeXSubparagraphAction(LaTeXTemplateAction):
-	label = "Subparagraph"
-	tooltip = "Subparagraph"
-	icon_name = "paragraph"
-	template_source = "\\subparagraph{$_}"
-	
-	
+    label = "Subparagraph"
+    tooltip = "Subparagraph"
+    icon_name = "paragraph"
+    template_source = "\\subparagraph{$_}"
+
+
 class LaTeXGraphicsAction(LaTeXIconAction):
-	label = "Insert Graphics"
-	accelerator = None
-	tooltip = "Insert Graphics"
-	icon = File(find_resource("icons/graphics.png"))
-	
-	dialog = None
-	
-	def activate(self, context):
-		if not self.dialog:
-			self.dialog = InsertGraphicsDialog()
-		source = self.dialog.run(context.active_editor.edited_file)
-		if source:
-			context.active_editor.insert(source)
+    label = "Insert Graphics"
+    accelerator = None
+    tooltip = "Insert Graphics"
+    icon = File(find_resource("icons/graphics.png"))
+
+    dialog = None
+
+    def activate(self, context):
+        if not self.dialog:
+            self.dialog = InsertGraphicsDialog()
+        source = self.dialog.run(context.active_editor.edited_file)
+        if source:
+            context.active_editor.insert(source)
 
 
 class LaTeXTableAction(LaTeXIconAction):
-	label = "Insert Table or Matrix"
-	accelerator = None
-	tooltip = "Insert Table or Matrix"
-	icon = File(find_resource("icons/table.png"))
-	
-	dialog = None
-	
-	def activate(self, context):
-		if not self.dialog:
-			self.dialog = InsertTableDialog()
-		source = self.dialog.run()
-		if source:
-			context.active_editor.insert(source)
-	
-	
+    label = "Insert Table or Matrix"
+    accelerator = None
+    tooltip = "Insert Table or Matrix"
+    icon = File(find_resource("icons/table.png"))
+
+    dialog = None
+
+    def activate(self, context):
+        if not self.dialog:
+            self.dialog = InsertTableDialog()
+        source = self.dialog.run()
+        if source:
+            context.active_editor.insert(source)
+
+
 class LaTeXListingAction(LaTeXIconAction):
-	label = "Insert Source Code Listing"
-	accelerator = None
-	tooltip = "Insert Source Code Listing"
-	icon = File(find_resource("icons/listing.png"))
-	
-	dialog = None
-	
-	def activate(self, context):
-		if not self.dialog:
-			self.dialog = InsertListingDialog()
-		source = self.dialog.run(context.active_editor.edited_file)
-		if source:
-			context.active_editor.insert(source)
+    label = "Insert Source Code Listing"
+    accelerator = None
+    tooltip = "Insert Source Code Listing"
+    icon = File(find_resource("icons/listing.png"))
+
+    dialog = None
+
+    def activate(self, context):
+        if not self.dialog:
+            self.dialog = InsertListingDialog()
+        source = self.dialog.run(context.active_editor.edited_file)
+        if source:
+            context.active_editor.insert(source)
 
 
 class LaTeXBuildImageAction(LaTeXIconAction):
-	label = "Build Image"
-	accelerator = None
-	tooltip = "Build an image from the LaTeX document"
-	icon = File(find_resource("icons/build-image.png"))
-	
-	dialog = None
-	
-	def activate(self, context):
-		if not self.dialog:
-			self.dialog = BuildImageDialog()
-			
-		tool = self.dialog.run()
-		if tool is not None:
-			tool_view = context.find_view(None, "ToolView")
-			
-			if context.active_editor:
-				ToolRunner().run(context.active_editor.file, tool, tool_view)
-
-			
+    label = "Build Image"
+    accelerator = None
+    tooltip = "Build an image from the LaTeX document"
+    icon = File(find_resource("icons/build-image.png"))
+
+    dialog = None
+
+    def activate(self, context):
+        if not self.dialog:
+            self.dialog = BuildImageDialog()
+
+        tool = self.dialog.run()
+        if tool is not None:
+            tool_view = context.find_view(None, "ToolView")
+
+            if context.active_editor:
+                ToolRunner().run(context.active_editor.file, tool, tool_view)
+
+
 class LaTeXJustifyLeftAction(LaTeXTemplateAction):
-	label = "Justify Left"
-	tooltip = "Justify Left"
-	icon_name = "justify-left"
-	template_source = "\\begin{flushleft}$_\\end{flushleft}"
-	
-	
+    label = "Justify Left"
+    tooltip = "Justify Left"
+    icon_name = "justify-left"
+    template_source = "\\begin{flushleft}$_\\end{flushleft}"
+
+
 class LaTeXJustifyCenterAction(LaTeXTemplateAction):
-	label = "Justify Center"
-	tooltip = "Justify Center"
-	icon_name = "justify-center"
-	template_source = "\\begin{center}$_\\end{center}"
+    label = "Justify Center"
+    tooltip = "Justify Center"
+    icon_name = "justify-center"
+    template_source = "\\begin{center}$_\\end{center}"
 
 
 class LaTeXJustifyRightAction(LaTeXTemplateAction):
-	label = "Justify Right"
-	tooltip = "Justify Right"
-	icon_name = "justify-right"
-	template_source = "\\begin{flushright}$_\\end{flushright}"
-	
+    label = "Justify Right"
+    tooltip = "Justify Right"
+    icon_name = "justify-right"
+    template_source = "\\begin{flushright}$_\\end{flushright}"
+
 
 class LaTeXMathMenuAction(LaTeXAction):
-	label = "Math"
-	accelerator = None
-	tooltip = "Math"
-	stock_id = None
-	
-	def activate(self, context):
-		pass
+    label = "Math"
+    accelerator = None
+    tooltip = "Math"
+    stock_id = None
+
+    def activate(self, context):
+        pass
 
 
 class LaTeXMathAction(LaTeXTemplateAction):
-	menu_tool_action = True
-	
-	label = "Mathematical Environment"
-	tooltip = "Mathematical Environment"
-	icon_name = "math"
-	template_source = "$ $_ $"
-	
-	
+    menu_tool_action = True
+
+    label = "Mathematical Environment"
+    tooltip = "Mathematical Environment"
+    icon_name = "math"
+    template_source = "$ $_ $"
+
+
 class LaTeXDisplayMathAction(LaTeXTemplateAction):
-	label = "Centered Formula"
-	tooltip = "Centered Formula"
-	icon_name = "displaymath"
-	template_source = "\\[ $_ \\]"
-	
-	
+    label = "Centered Formula"
+    tooltip = "Centered Formula"
+    icon_name = "displaymath"
+    template_source = "\\[ $_ \\]"
+
+
 class LaTeXEquationAction(LaTeXTemplateAction):
-	label = "Numbered Equation"
-	tooltip = "Numbered Equation"
-	icon_name = "equation"
-	template_source = """\\begin{equation}
-	$_
+    label = "Numbered Equation"
+    tooltip = "Numbered Equation"
+    icon_name = "equation"
+    template_source = """\\begin{equation}
+    $_
 \\end{equation}"""
 
 
 class LaTeXUnEqnArrayAction(LaTeXTemplateAction):
-	label = "Array of Equations"
-	tooltip = "Array of Equations"
-	icon_name = "uneqnarray"
-	packages = ["amsmath"]
-	template_source = """\\begin{align*}
-	$_
+    label = "Array of Equations"
+    tooltip = "Array of Equations"
+    icon_name = "uneqnarray"
+    packages = ["amsmath"]
+    template_source = """\\begin{align*}
+    $_
 \\end{align*}"""
 
 
 class LaTeXEqnArrayAction(LaTeXTemplateAction):
-	label = "Numbered Array of Equations"
-	tooltip = "Numbered Array of Equations"
-	icon_name = "eqnarray"
-	packages = ["amsmath"]
-	template_source = """\\begin{align}
-	$_
+    label = "Numbered Array of Equations"
+    tooltip = "Numbered Array of Equations"
+    icon_name = "eqnarray"
+    packages = ["amsmath"]
+    template_source = """\\begin{align}
+    $_
 \\end{align}"""
-	
-	
+
+
 class LaTeXSaveAsTemplateAction(LaTeXAction):
-	label = "Save As Template..."
-	accelerator = None
-	tooltip = "Save the current document as a template"
-	stock_id = Gtk.STOCK_SAVE_AS
-	
-	def activate(self, context):
-		dialog = SaveAsTemplateDialog()
-		file = dialog.run()
-		
-		content = context.active_editor.content
-		
-		fo = open(file.path, "w")
-		fo.write(content)
-		fo.close
-	
-	
-	
-	
-	
-	
-	
-		
+    label = "Save As Template..."
+    accelerator = None
+    tooltip = "Save the current document as a template"
+    stock_id = Gtk.STOCK_SAVE_AS
+
+    def activate(self, context):
+        dialog = SaveAsTemplateDialog()
+        file = dialog.run()
+
+        content = context.active_editor.content
+
+        fo = open(file.path, "w")
+        fo.write(content)
+        fo.close
+
+
+
+
+
+
+
+
diff --git a/latex/latex/archive.py b/latex/latex/archive.py
index a361a0f..be7efc9 100644
--- a/latex/latex/archive.py
+++ b/latex/latex/archive.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -28,86 +28,86 @@ from ..issues import MockIssueHandler
 
 
 class Dependency:
-	def __init__(self, original_filename):
-		self.original_filename = original_filename
+    def __init__(self, original_filename):
+        self.original_filename = original_filename
 
 
 class LaTeXArchiver:
-	def archive(self, file):
-		"""
-		@param file: the master document File object 
-		"""
-		self._file = file
-		self._scan()
-	
-	def _scan(self):
-		"""
-		Scan the document for dependencies
-		"""
-		self._dependencies = []
-		scanner = LaTeXDependencyScanner()
-		for filename in scanner.scan(self._file):
-			self._dependencies.append(Dependency(filename))
-	
-	def _copy_to_sandbox(self):
-		"""
-		Copy the document and its dependencies to a sandbox folder
-		"""
-		pass
-	
-	def _repair_references(self):
-		"""
-		Repair eventually broken absolute paths
-		"""
-		pass
-	
-	def _pack(self):
-		"""
-		Pack the document and its deps into an archive
-		"""
-		pass
+    def archive(self, file):
+        """
+        @param file: the master document File object
+        """
+        self._file = file
+        self._scan()
+
+    def _scan(self):
+        """
+        Scan the document for dependencies
+        """
+        self._dependencies = []
+        scanner = LaTeXDependencyScanner()
+        for filename in scanner.scan(self._file):
+            self._dependencies.append(Dependency(filename))
+
+    def _copy_to_sandbox(self):
+        """
+        Copy the document and its dependencies to a sandbox folder
+        """
+        pass
+
+    def _repair_references(self):
+        """
+        Repair eventually broken absolute paths
+        """
+        pass
+
+    def _pack(self):
+        """
+        Pack the document and its deps into an archive
+        """
+        pass
 
 
 class LaTeXDependencyScanner:
-	"""
-	This analyzes a document and recognizes its dependent files
-	
-	@deprecated: 
-	"""
-	def __init__(self):
-		self._parser = LaTeXParser()
-		self._expander = LaTeXReferenceExpander()
-	
-	def scan(self, file):
-		# parse
-		content = open(file.path, "r").read()
-		self._document = self._parser.parse(content, file, MockIssueHandler())
-		self._expander.expand(self._document, file, MockIssueHandler(), None)
-		# search
-		self._filenames = []
-		self._search(self._document)
-		
-		# TODO: add all filenames that match a regex
-		
-		return self._filenames
-	
-	def _search(self, parent):
-		# search the model for all \in* commands
-		for node in parent:
-			if node.type == Node.COMMAND:
-				if node.value.startswith("in"):
-					try:
-						argument = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						# TODO: match against regex for filenames and create File object
-						if argument.startswith("/"):
-							filename = argument
-						else:
-							filename = node.file.dirname + "/" + argument
-						if node.value in ["include", "input"]:
-							filename += ".tex"
-						self._filenames.append(filename)
-					except IndexError:
-						pass
-			self._search(node)
-		
-		
+    """
+    This analyzes a document and recognizes its dependent files
+
+    @deprecated:
+    """
+    def __init__(self):
+        self._parser = LaTeXParser()
+        self._expander = LaTeXReferenceExpander()
+
+    def scan(self, file):
+        # parse
+        content = open(file.path, "r").read()
+        self._document = self._parser.parse(content, file, MockIssueHandler())
+        self._expander.expand(self._document, file, MockIssueHandler(), None)
+        # search
+        self._filenames = []
+        self._search(self._document)
+
+        # TODO: add all filenames that match a regex
+
+        return self._filenames
+
+    def _search(self, parent):
+        # search the model for all \in* commands
+        for node in parent:
+            if node.type == Node.COMMAND:
+                if node.value.startswith("in"):
+                    try:
+                        argument = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        # TODO: match against regex for filenames and create File object
+                        if argument.startswith("/"):
+                            filename = argument
+                        else:
+                            filename = node.file.dirname + "/" + argument
+                        if node.value in ["include", "input"]:
+                            filename += ".tex"
+                        self._filenames.append(filename)
+                    except IndexError:
+                        pass
+            self._search(node)
+
+
diff --git a/latex/latex/cache.py b/latex/latex/cache.py
index 94520cf..1a10dd5 100644
--- a/latex/latex/cache.py
+++ b/latex/latex/cache.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -31,127 +31,126 @@ from ..issues import IIssueHandler
 
 
 class CacheIssueHandler(IIssueHandler):
-	"""
-	This is used to catch the issues occuring while parsing a document
-	on cache fault.
-	"""
-	def __init__(self):
-		self.issues = []
-	
-	def clear(self):
-		self.issues = []
-	
-	def issue(self, issue):
-		self.issues.append(issue)
+    """
+    This is used to catch the issues occuring while parsing a document
+    on cache fault.
+    """
+    def __init__(self):
+        self.issues = []
+
+    def clear(self):
+        self.issues = []
+
+    def issue(self, issue):
+        self.issues.append(issue)
 
 
 class LaTeXDocumentCache(object):
-	"""
-	This caches LaTeX document models. It used to speed up 
-	the LaTeXReferenceExpander.
-	"""
-	
-	# FIXME: we need a global character set
-	
-	# TODO: serialize the cache on shutdown
-	
-	_log = getLogger("LaTeXDocumentCache")
-	
-	class Entry(object):
-		"""
-		An entry in the cache
-		"""
-		_log = getLogger("LaTeXDocumentCache.Entry")
-		
-		def __init__(self, file, charset):
-			self.__file = file
-			self.__parser = LaTeXParser()
-			self.__issue_handler = CacheIssueHandler()
-			self.__mtime = 0
-			self.__document = None
-			self.__charset = charset
-			
-			self.synchronize()
-		
-		@property
-		def modified(self):
-			return (self.__file.mtime > self.__mtime)
-		
-		@property
-		def document(self):
-			return self.__document
-		
-		@property
-		def issues(self):
-			return self.__issue_handler.issues
-		
-		def synchronize(self):
-			"""
-			Synchronize document model with file contents.
-			
-			@raise OSError: if the file is not found
-			"""
-			# update timestamp
-			self.__mtime = self.__file.mtime
-			
-			# clear previous data
-			self.__issue_handler.clear()
-			if self.__document != None:
-				self.__document.destroy()
-				self.__document = None
-
-			# read file
-			try:
-				f = open(self.__file.path, "r")
-				try:
-					content = f.read()
-				finally:
-					f.close()
-			except IOError:
-				return
-
-			if self.__charset is not None:
-				content = content.decode(self.__charset)
-			
-			# parse
-			self.__document = self.__parser.parse(content, self.__file, self.__issue_handler)
-	
-	def __new__(cls):
-		if not '_instance' in cls.__dict__:
-			cls._instance = object.__new__(cls)
-		return cls._instance
-	
-	def __init__(self):
-		if not '_ready' in dir(self):
-			self._entries = {}
-			self._ready = True
-	
-	def get_document(self, file, charset, issue_handler):
-		"""
-		Return the (hopefully) cached document model for a given file
-		
-		@param file: a File object
-		@param charset: character set
-		@param issue_handler: an IIssueHandler to use
-		"""
-		try:
-			# update entry if necessary
-			entry = self._entries[file.uri]
-			self._log.debug("Reading '%s' from cache" % file)
-			if entry.modified:
-				self._log.debug("File '%s' modified, synchronizing..." % file)
-				entry.synchronize()
-		except KeyError:
-			self._log.debug("Cache fault for '%s'" % file)
-			# create new entry
-			entry = self.Entry(file, charset)
-			self._entries[file.uri] = entry
-		
-		# pass cached issues to the issue handler
-		for issue in entry.issues:
-			issue_handler.issue(issue)
-		
-		return entry.document
-	
-	
-	
\ No newline at end of file
+    """
+    This caches LaTeX document models. It used to speed up
+    the LaTeXReferenceExpander.
+    """
+
+    # FIXME: we need a global character set
+
+    # TODO: serialize the cache on shutdown
+
+    _log = getLogger("LaTeXDocumentCache")
+
+    class Entry(object):
+        """
+        An entry in the cache
+        """
+        _log = getLogger("LaTeXDocumentCache.Entry")
+
+        def __init__(self, file, charset):
+            self.__file = file
+            self.__parser = LaTeXParser()
+            self.__issue_handler = CacheIssueHandler()
+            self.__mtime = 0
+            self.__document = None
+            self.__charset = charset
+
+            self.synchronize()
+
+        @property
+        def modified(self):
+            return (self.__file.mtime > self.__mtime)
+
+        @property
+        def document(self):
+            return self.__document
+
+        @property
+        def issues(self):
+            return self.__issue_handler.issues
+
+        def synchronize(self):
+            """
+            Synchronize document model with file contents.
+
+            @raise OSError: if the file is not found
+            """
+            # update timestamp
+            self.__mtime = self.__file.mtime
+
+            # clear previous data
+            self.__issue_handler.clear()
+            if self.__document != None:
+                self.__document.destroy()
+                self.__document = None
+
+            # read file
+            try:
+                f = open(self.__file.path, "r")
+                try:
+                    content = f.read()
+                finally:
+                    f.close()
+            except IOError:
+                return
+
+            if self.__charset is not None:
+                content = content.decode(self.__charset)
+
+            # parse
+            self.__document = self.__parser.parse(content, self.__file, self.__issue_handler)
+
+    def __new__(cls):
+        if not '_instance' in cls.__dict__:
+            cls._instance = object.__new__(cls)
+        return cls._instance
+
+    def __init__(self):
+        if not '_ready' in dir(self):
+            self._entries = {}
+            self._ready = True
+
+    def get_document(self, file, charset, issue_handler):
+        """
+        Return the (hopefully) cached document model for a given file
+
+        @param file: a File object
+        @param charset: character set
+        @param issue_handler: an IIssueHandler to use
+        """
+        try:
+            # update entry if necessary
+            entry = self._entries[file.uri]
+            self._log.debug("Reading '%s' from cache" % file)
+            if entry.modified:
+                self._log.debug("File '%s' modified, synchronizing..." % file)
+                entry.synchronize()
+        except KeyError:
+            self._log.debug("Cache fault for '%s'" % file)
+            # create new entry
+            entry = self.Entry(file, charset)
+            self._entries[file.uri] = entry
+
+        # pass cached issues to the issue handler
+        for issue in entry.issues:
+            issue_handler.issue(issue)
+
+        return entry.document
+
+
diff --git a/latex/latex/completion.py b/latex/latex/completion.py
index fad7bb1..f334bef 100644
--- a/latex/latex/completion.py
+++ b/latex/latex/completion.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -32,62 +32,62 @@ from ..base import ICompletionHandler, Proposal, Template
 
 
 class LaTeXCommandProposal(Proposal):
-	"""
-	A proposal inserting a Template when activated
-	"""
-	icon = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/i_command.png"))
-	
-	def __init__(self, overlap, template, label):
-		self._template = template
-		self._label = label
-		self._overlap = overlap
-	
-	@property
-	def source(self):
-		return self._template
-	
-	@property
-	def label(self):
-		return self._label
-	
-	@property
-	def details(self):
-		return None
-	
-	@property
-	def overlap(self):
-		return self._overlap
-	
+    """
+    A proposal inserting a Template when activated
+    """
+    icon = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/i_command.png"))
+
+    def __init__(self, overlap, template, label):
+        self._template = template
+        self._label = label
+        self._overlap = overlap
+
+    @property
+    def source(self):
+        return self._template
+
+    @property
+    def label(self):
+        return self._label
+
+    @property
+    def details(self):
+        return None
+
+    @property
+    def overlap(self):
+        return self._overlap
+
 
 class LaTeXChoiceProposal(Proposal):
-	"""
-	A proposal inserting a simple string when activated
-	"""
-	icon = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/i_choice.png"))
-	
-	def __init__(self, overlap, source, label, details):
-		self._source = source
-		self._details = details
-		self._overlap = overlap
-		self._label = label
-	
-	@property
-	def source(self):
-		return self._source
-	
-	@property
-	def label(self):
-		return self._label
-	
-	@property
-	def details(self):
-		return self._details
-	
-	@property
-	def overlap(self):
-		return self._overlap
-	
-	
+    """
+    A proposal inserting a simple string when activated
+    """
+    icon = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/i_choice.png"))
+
+    def __init__(self, overlap, source, label, details):
+        self._source = source
+        self._details = details
+        self._overlap = overlap
+        self._label = label
+
+    @property
+    def source(self):
+        return self._source
+
+    @property
+    def label(self):
+        return self._label
+
+    @property
+    def details(self):
+        return self._details
+
+    @property
+    def overlap(self):
+        return self._overlap
+
+
 from model import LanguageModelFactory, Command, Choice, MandatoryArgument, OptionalArgument
 from parser import PrefixParser, Node
 
@@ -95,303 +95,303 @@ from ..bibtex.cache import BibTeXDocumentCache
 
 
 class LaTeXCompletionHandler(ICompletionHandler):
-	"""
-	This implements the LaTeX-specific code completion
-	"""
-	_log = getLogger("LaTeXCompletionHandler")
-	
-	trigger_keys = ["backslash", "braceleft"]
-	prefix_delimiters = ["\\"]
-	
-	def __init__(self):
-		self._log.debug("init")
-		
-		self._language_model = LanguageModelFactory().create_language_model()
-		self._bibtex_document_cache = BibTeXDocumentCache()
-		
-	def set_outline(self, outline):
-		"""
-		Process a LaTeX outline model
-		
-		@param outline: a latex.outline.Outline instance
-		"""
-		
-		# labels
-		label_choices = [Choice(None, label.value) for label in outline.labels]
-		self._language_model.fill_placeholder("Labels", label_choices)
-		
-		# colors
-		color_choices = [Choice(None, color) for color in outline.colors]
-		self._language_model.fill_placeholder("Colors", color_choices)
-		
-		# newcommands
-		newcommands = []
-		for n in outline.newcommands:
-			command = Command(None, n.value)
-			for i in range(n.numOfArgs):
-				command.children.append(MandatoryArgument(None, "#%s" % (i + 1)))
-			newcommands.append(command)
-		self._language_model.set_newcommands(newcommands)
-		
-		# newenvironments
-		newenvironments = []
-		for n in outline.newenvironments:
-			choice = Choice(None, n.value)
-			newenvironments.append(choice)
-		self._language_model.fill_placeholder("Newenvironments", newenvironments)
-		
-		#
-		# bibtex entries
-		#
-		try:
-			
-			entry_choices = []
-			
-			for bib_file in outline.bibliographies:
-				try:
-					bibtex_document = self._bibtex_document_cache.get_document(bib_file)
-					
-					# generate choices from entries
-					
-					for entry in bibtex_document.entries:
-						
-						# build table data for DetailsPopup
-						rows = []
-						for field in entry.fields:
-							rows.append([field.name, field.valueMarkup])
-						
-						entry_choices.append(Choice(None, entry.key, rows))
-						
-				except OSError:
-					# BibTeX file not found
-					self._log.error("Not found: %s" % bib_file)
-					
-			# attach to placeholders in CommandStore
-			self._language_model.fill_placeholder("Bibitems", entry_choices)
-				
-		except IOError:
-			self._log.debug("Failed to provide BibTeX completion due to IOError")
-			
-	
-	def set_neighbors(self, tex_files, bib_files, graphic_files):
-		"""
-		Populate the lists of neighbor files
-		
-		@param tex_files: list of neighbor TeX files
-		@param bib_files: list of neighbor BibTeX files
-		@param graphic_files: list of neighbor graphics
-		"""
-		tex_choices = [Choice(None, file.shortbasename) for file in tex_files]
-		self._language_model.fill_placeholder("TexFiles", tex_choices)
-		
-		bib_choices = [Choice(None, file.shortbasename) for file in bib_files]
-		self._language_model.fill_placeholder("BibFiles", bib_choices)
-		
-		graphic_choices = [Choice(None, file.basename) for file in graphic_files]
-		self._language_model.fill_placeholder("ImageFiles", graphic_choices)
-	
-	def complete(self, prefix):
-		"""
-		Try to complete a given prefix
-		"""
-		self._log.debug("complete: '%s'" % prefix)
-		
-		#proposals = [LaTeXTemplateProposal(Template("Hello[${One}][${Two}][${Three}]"), "Hello[Some]"), LaTeXProposal("\\world")]
-		
-		fragment = Node(Node.DOCUMENT)
-		parser = PrefixParser()
-		
-		try:
-			parser.parse(prefix, fragment)
-			
-			modelParser = PrefixModelParser(self._language_model)
-			proposals = modelParser.parse(fragment)	
-			
-			self._log.debug("Generated %s proposals" % len(proposals))
-			
-			return proposals
-			
-		except Exception, e:
-			self._log.debug(e)
-		
-		return []
-
-	def __del__(self):
-		self._log.debug("Properly destroyed %s" % self)
-		
+    """
+    This implements the LaTeX-specific code completion
+    """
+    _log = getLogger("LaTeXCompletionHandler")
+
+    trigger_keys = ["backslash", "braceleft"]
+    prefix_delimiters = ["\\"]
+
+    def __init__(self):
+        self._log.debug("init")
+
+        self._language_model = LanguageModelFactory().create_language_model()
+        self._bibtex_document_cache = BibTeXDocumentCache()
+
+    def set_outline(self, outline):
+        """
+        Process a LaTeX outline model
+
+        @param outline: a latex.outline.Outline instance
+        """
+
+        # labels
+        label_choices = [Choice(None, label.value) for label in outline.labels]
+        self._language_model.fill_placeholder("Labels", label_choices)
+
+        # colors
+        color_choices = [Choice(None, color) for color in outline.colors]
+        self._language_model.fill_placeholder("Colors", color_choices)
+
+        # newcommands
+        newcommands = []
+        for n in outline.newcommands:
+            command = Command(None, n.value)
+            for i in range(n.numOfArgs):
+                command.children.append(MandatoryArgument(None, "#%s" % (i + 1)))
+            newcommands.append(command)
+        self._language_model.set_newcommands(newcommands)
+
+        # newenvironments
+        newenvironments = []
+        for n in outline.newenvironments:
+            choice = Choice(None, n.value)
+            newenvironments.append(choice)
+        self._language_model.fill_placeholder("Newenvironments", newenvironments)
+
+        #
+        # bibtex entries
+        #
+        try:
+
+            entry_choices = []
+
+            for bib_file in outline.bibliographies:
+                try:
+                    bibtex_document = self._bibtex_document_cache.get_document(bib_file)
+
+                    # generate choices from entries
+
+                    for entry in bibtex_document.entries:
+
+                        # build table data for DetailsPopup
+                        rows = []
+                        for field in entry.fields:
+                            rows.append([field.name, field.valueMarkup])
+
+                        entry_choices.append(Choice(None, entry.key, rows))
+
+                except OSError:
+                    # BibTeX file not found
+                    self._log.error("Not found: %s" % bib_file)
+
+            # attach to placeholders in CommandStore
+            self._language_model.fill_placeholder("Bibitems", entry_choices)
+
+        except IOError:
+            self._log.debug("Failed to provide BibTeX completion due to IOError")
+
+
+    def set_neighbors(self, tex_files, bib_files, graphic_files):
+        """
+        Populate the lists of neighbor files
+
+        @param tex_files: list of neighbor TeX files
+        @param bib_files: list of neighbor BibTeX files
+        @param graphic_files: list of neighbor graphics
+        """
+        tex_choices = [Choice(None, file.shortbasename) for file in tex_files]
+        self._language_model.fill_placeholder("TexFiles", tex_choices)
+
+        bib_choices = [Choice(None, file.shortbasename) for file in bib_files]
+        self._language_model.fill_placeholder("BibFiles", bib_choices)
+
+        graphic_choices = [Choice(None, file.basename) for file in graphic_files]
+        self._language_model.fill_placeholder("ImageFiles", graphic_choices)
+
+    def complete(self, prefix):
+        """
+        Try to complete a given prefix
+        """
+        self._log.debug("complete: '%s'" % prefix)
+
+        #proposals = [LaTeXTemplateProposal(Template("Hello[${One}][${Two}][${Three}]"), "Hello[Some]"), LaTeXProposal("\\world")]
+
+        fragment = Node(Node.DOCUMENT)
+        parser = PrefixParser()
+
+        try:
+            parser.parse(prefix, fragment)
+
+            modelParser = PrefixModelParser(self._language_model)
+            proposals = modelParser.parse(fragment)
+
+            self._log.debug("Generated %s proposals" % len(proposals))
+
+            return proposals
+
+        except Exception, e:
+            self._log.debug(e)
+
+        return []
+
+    def __del__(self):
+        self._log.debug("Properly destroyed %s" % self)
+
 
 from ..preferences import Preferences
 from . import LaTeXSource
 
 
 class PrefixModelParser(object):
-	"""
-	This parses the dcoument model of a prefix and generates proposals accordingly
-	
-	This is used by the LatexProposalGenerator class
-	"""
-	
-	# FIXME: Completion doesn't work at \includegraphics[]{_} but at \includegraphics{_} 
-	
-	_log = getLogger("PrefixModelParser")
-	
-	def __init__(self, language_model):
-		self.__language_model = language_model
-		self.__light_foreground = Preferences().get("light-foreground-color")
-	
-	def __create_proposals_from_commands(self, commands, overlap):
-		"""
-		Generate proposals for commands
-		"""
-		proposals = []
-		
-		for command in commands:
-			label = command.name
-			templateSource = "\\" + command.name
-			
-			for argument in command.children:
-				if type(argument) is MandatoryArgument:
-					label += "{<span color='%s'>%s</span>}" % (self.__light_foreground, argument.label)
-					templateSource += "{${%s}}" % argument.label
-				elif type(argument) is OptionalArgument:
-					label += "[<span color='%s'>%s</span>]" % (self.__light_foreground, argument.label)
-					templateSource += "[${%s}]" % argument.label
-			
-			if command.package:
-				label += " <small><b>%s</b></small>" % command.package
-			
-			# workaround for latex.model.Element.package may be None
-			# TODO: latex.model.Element.package should be a list of packages
-			if command.package is None:
-				packages = []
-			else:
-				packages = [command.package]
-			proposal = LaTeXCommandProposal(overlap, LaTeXSource(Template(templateSource), packages), label)
-			proposals.append(proposal)
-		
-		return proposals
-	
-	def __create_proposals_from_choices(self, choices, overlap):
-		"""
-		Generate proposals for argument choices
-		"""
-		proposals = []
-		
-		for choice in choices:
-			label = choice.value
-			if choice.package:
-				label += " <small><b>%s</b></small>" % choice.package
-			
-			# see above
-			if choice.package is None:
-				packages = []
-			else:
-				packages = [choice.package]
-			proposal = LaTeXChoiceProposal(overlap, LaTeXSource(choice.value, packages), label, choice.details)
-			proposals.append(proposal)
-		
-		return proposals
-	
-	def parse(self, prefixFragment):
-		"""
-		Returns choices
-		"""
-		
-		# root node of the prefix model must be COMMAND
-		commandNode = prefixFragment[-1]
-		if commandNode.type != Node.COMMAND:
-			return []
-		
-		commandName = commandNode.value
-		
-		if len(commandNode) == 0:
-			# command has no arguments...
-			
-			if len(commandName) == 0:
-				# no name, so propose all commands
-				commands = self.__language_model.commands.values()
-				overlap = 1	    # only "\"
-			else:
-				commands = self.__language_model.find_command(commandName)
-				
-				if len(commands) == 1 and commands[0].name == commandName:
-					# don't propose when only one command is found and that one
-					# matches the typed one
-					return []
-				
-				overlap = len(commandName) + 1 	    # "\begi"
-				
-			return self.__create_proposals_from_commands(commands, overlap)
-		
-		# ...command has arguments
-		
-		try:
-			self._log.debug(commandNode.xml)
-			
-			# find the language model of the command
-			storedCommand = self.__language_model.commands[commandName]
-		
-		
-			try:
-				argumentNode, storedArgument = self.__match_argument(commandNode, storedCommand)
-			except Exception, e:
-				self._log.error(e)
-				return []
-
-				
-			choices = storedArgument.children
-			
-			# filter argument matching the already typed argument text
-			
-			argumentValue = argumentNode.innerText
-			
-			if len(argumentValue):
-				choices = [choice for choice in choices if choice.value.startswith(argumentValue)]
-				overlap = len(argumentValue)
-			else:
-				overlap = 0
-			
-			return self.__create_proposals_from_choices(choices, overlap)
-			
-		except KeyError:
-			self._log.debug("Command not found: %s" % commandName)
-			return []
-	
-	def __match_argument(self, command, model_command):
-		"""
-		@param command: the parsed command Node
-		@param model_command: the according model command
-		@return: (matched argument, model argument)
-		"""
-		# push the arguments of the model command on a stack
-		model_argument_stack = []
-		model_argument_stack.extend(model_command.children)
-		model_argument_stack.reverse()
-		
-		for argument in command:
-			if argument.type == Node.MANDATORY_ARGUMENT:
-				
-				# optional arguments in the model may be skipped
-				while True:
-					try:
-						model_argument = model_argument_stack.pop()
-						if model_argument.type != Node.OPTIONAL_ARGUMENT:
-							break
-					except IndexError:
-						# no more optional arguments to skip - signatures can't match
-						raise Exception("Signatures don't match")
-				
-				if not argument.closed:
-					return (argument, model_argument)
-			
-			elif argument.type == Node.OPTIONAL_ARGUMENT:
-				model_argument = model_argument_stack.pop()
-				
-				if model_argument.type != Node.OPTIONAL_ARGUMENT:
-					raise Exception("Signatures don't match")
-				
-				if not argument.closed:
-					return (argument, model_argument)
-				
-		raise Exception("No matching model argument found")
+    """
+    This parses the dcoument model of a prefix and generates proposals accordingly
+
+    This is used by the LatexProposalGenerator class
+    """
+
+    # FIXME: Completion doesn't work at \includegraphics[]{_} but at \includegraphics{_}
+
+    _log = getLogger("PrefixModelParser")
+
+    def __init__(self, language_model):
+        self.__language_model = language_model
+        self.__light_foreground = Preferences().get("light-foreground-color")
+
+    def __create_proposals_from_commands(self, commands, overlap):
+        """
+        Generate proposals for commands
+        """
+        proposals = []
+
+        for command in commands:
+            label = command.name
+            templateSource = "\\" + command.name
+
+            for argument in command.children:
+                if type(argument) is MandatoryArgument:
+                    label += "{<span color='%s'>%s</span>}" % (self.__light_foreground, argument.label)
+                    templateSource += "{${%s}}" % argument.label
+                elif type(argument) is OptionalArgument:
+                    label += "[<span color='%s'>%s</span>]" % (self.__light_foreground, argument.label)
+                    templateSource += "[${%s}]" % argument.label
+
+            if command.package:
+                label += " <small><b>%s</b></small>" % command.package
+
+            # workaround for latex.model.Element.package may be None
+            # TODO: latex.model.Element.package should be a list of packages
+            if command.package is None:
+                packages = []
+            else:
+                packages = [command.package]
+            proposal = LaTeXCommandProposal(overlap, LaTeXSource(Template(templateSource), packages), label)
+            proposals.append(proposal)
+
+        return proposals
+
+    def __create_proposals_from_choices(self, choices, overlap):
+        """
+        Generate proposals for argument choices
+        """
+        proposals = []
+
+        for choice in choices:
+            label = choice.value
+            if choice.package:
+                label += " <small><b>%s</b></small>" % choice.package
+
+            # see above
+            if choice.package is None:
+                packages = []
+            else:
+                packages = [choice.package]
+            proposal = LaTeXChoiceProposal(overlap, LaTeXSource(choice.value, packages), label, choice.details)
+            proposals.append(proposal)
+
+        return proposals
+
+    def parse(self, prefixFragment):
+        """
+        Returns choices
+        """
+
+        # root node of the prefix model must be COMMAND
+        commandNode = prefixFragment[-1]
+        if commandNode.type != Node.COMMAND:
+            return []
+
+        commandName = commandNode.value
+
+        if len(commandNode) == 0:
+            # command has no arguments...
+
+            if len(commandName) == 0:
+                # no name, so propose all commands
+                commands = self.__language_model.commands.values()
+                overlap = 1        # only "\"
+            else:
+                commands = self.__language_model.find_command(commandName)
+
+                if len(commands) == 1 and commands[0].name == commandName:
+                    # don't propose when only one command is found and that one
+                    # matches the typed one
+                    return []
+
+                overlap = len(commandName) + 1         # "\begi"
+
+            return self.__create_proposals_from_commands(commands, overlap)
+
+        # ...command has arguments
+
+        try:
+            self._log.debug(commandNode.xml)
+
+            # find the language model of the command
+            storedCommand = self.__language_model.commands[commandName]
+
+
+            try:
+                argumentNode, storedArgument = self.__match_argument(commandNode, storedCommand)
+            except Exception, e:
+                self._log.error(e)
+                return []
+
+
+            choices = storedArgument.children
+
+            # filter argument matching the already typed argument text
+
+            argumentValue = argumentNode.innerText
+
+            if len(argumentValue):
+                choices = [choice for choice in choices if choice.value.startswith(argumentValue)]
+                overlap = len(argumentValue)
+            else:
+                overlap = 0
+
+            return self.__create_proposals_from_choices(choices, overlap)
+
+        except KeyError:
+            self._log.debug("Command not found: %s" % commandName)
+            return []
+
+    def __match_argument(self, command, model_command):
+        """
+        @param command: the parsed command Node
+        @param model_command: the according model command
+        @return: (matched argument, model argument)
+        """
+        # push the arguments of the model command on a stack
+        model_argument_stack = []
+        model_argument_stack.extend(model_command.children)
+        model_argument_stack.reverse()
+
+        for argument in command:
+            if argument.type == Node.MANDATORY_ARGUMENT:
+
+                # optional arguments in the model may be skipped
+                while True:
+                    try:
+                        model_argument = model_argument_stack.pop()
+                        if model_argument.type != Node.OPTIONAL_ARGUMENT:
+                            break
+                    except IndexError:
+                        # no more optional arguments to skip - signatures can't match
+                        raise Exception("Signatures don't match")
+
+                if not argument.closed:
+                    return (argument, model_argument)
+
+            elif argument.type == Node.OPTIONAL_ARGUMENT:
+                model_argument = model_argument_stack.pop()
+
+                if model_argument.type != Node.OPTIONAL_ARGUMENT:
+                    raise Exception("Signatures don't match")
+
+                if not argument.closed:
+                    return (argument, model_argument)
+
+        raise Exception("No matching model argument found")
 
diff --git a/latex/latex/dialogs.py b/latex/latex/dialogs.py
index 4b726c7..75e6222 100644
--- a/latex/latex/dialogs.py
+++ b/latex/latex/dialogs.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -42,1149 +42,1149 @@ from .listing import LanguagesParser
 from . import LaTeXSource
 
 class AbstractProxy(object):
-	"""
-	This may simplify the use of a widget and it serves for state saving
-	using the plugins preferences.
-	"""
-	def __init__(self, widget, key):
-		"""
-		@param widget: the widget to proxy
-		@param key: a preferences key to use for state saving
-		"""
-		self._widget = widget
-		self._key = key
-		self._preferences = Preferences()
-	
-	def restore(self, default):
-		"""
-		Restore the last state of the proxied widget
-		
-		@param default: a default value if no state is found
-		"""
-		raise NotImplementedError
-	
-	def save(self):
-		"""
-		Save the state of the proxied widget
-		"""
-		self._preferences.set(self._key, self.value)
-	
-	@property
-	def value(self):
-		"""
-		@return: the current value of the proxied widget
-		"""
-		raise NotImplementedError
+    """
+    This may simplify the use of a widget and it serves for state saving
+    using the plugins preferences.
+    """
+    def __init__(self, widget, key):
+        """
+        @param widget: the widget to proxy
+        @param key: a preferences key to use for state saving
+        """
+        self._widget = widget
+        self._key = key
+        self._preferences = Preferences()
+
+    def restore(self, default):
+        """
+        Restore the last state of the proxied widget
+
+        @param default: a default value if no state is found
+        """
+        raise NotImplementedError
+
+    def save(self):
+        """
+        Save the state of the proxied widget
+        """
+        self._preferences.set(self._key, self.value)
+
+    @property
+    def value(self):
+        """
+        @return: the current value of the proxied widget
+        """
+        raise NotImplementedError
 
 
 class ComboBoxProxy(AbstractProxy):
-	"""
-	This proxies a ComboBox widget:
-	
-	p = ComboBoxProxy(self.find_widget("myCombo"), "SomeSetting")
-	p.add_option("thing_1", "First Thing")
-	p.add_option("thing_2", "Second Thing")
-	p.restore("thing_1")
-	...
-	"""
-	def __init__(self, widget, key):
-		AbstractProxy.__init__(self, widget, key)
-		
-		self._store = Gtk.ListStore(str, str)			# value, label
-		self._widget.set_model(self._store)
-		cell = Gtk.CellRendererText()
-		self._widget.pack_start(cell, True)
-		self._widget.add_attribute(cell, "markup", 1)
-		
-		self._options = []
-	
-	def restore(self, default):
-		restored_value = self._preferences.get(self._key, default)
-		restored_index = 0
-		i = 0
-		for value, label in self._options:
-			if value == restored_value:
-				restored_index = i
-				break
-			i += 1
-		self._widget.set_active(restored_index)
-		
-		self._widget.connect("changed", self._on_changed)
-	
-	def _on_changed(self, combobox):
-		self.save()
-	
-	# accepts(object, String, String, bool)
-	def add_option(self, value, label, show_value=True):
-		"""
-		Add an option to the widget
-		
-		@param value: a unique value
-		@param label: a label text that may contain markup
-		"""
-		self._options.append((value, label))
-		
-		label_markup = ""
-		
-		if show_value:
-			if not value is None and len(value) > 0:
-				label_markup = "%s <span color='%s'>%s</span>" % (value, self._preferences.get("light-foreground-color"), label)
-			else:
-				label_markup = "<span color='%s'>%s</span>" % (self._preferences.get("light-foreground-color"), label)
-		else:
-			label_markup = label
-
-		self._store.append([value, label_markup])
-	
-	@property
-	def value(self):
-		index = self._widget.get_active()
-		return self._options[index][0]
-	
-	
+    """
+    This proxies a ComboBox widget:
+
+    p = ComboBoxProxy(self.find_widget("myCombo"), "SomeSetting")
+    p.add_option("thing_1", "First Thing")
+    p.add_option("thing_2", "Second Thing")
+    p.restore("thing_1")
+    ...
+    """
+    def __init__(self, widget, key):
+        AbstractProxy.__init__(self, widget, key)
+
+        self._store = Gtk.ListStore(str, str)            # value, label
+        self._widget.set_model(self._store)
+        cell = Gtk.CellRendererText()
+        self._widget.pack_start(cell, True)
+        self._widget.add_attribute(cell, "markup", 1)
+
+        self._options = []
+
+    def restore(self, default):
+        restored_value = self._preferences.get(self._key, default)
+        restored_index = 0
+        i = 0
+        for value, label in self._options:
+            if value == restored_value:
+                restored_index = i
+                break
+            i += 1
+        self._widget.set_active(restored_index)
+
+        self._widget.connect("changed", self._on_changed)
+
+    def _on_changed(self, combobox):
+        self.save()
+
+    # accepts(object, String, String, bool)
+    def add_option(self, value, label, show_value=True):
+        """
+        Add an option to the widget
+
+        @param value: a unique value
+        @param label: a label text that may contain markup
+        """
+        self._options.append((value, label))
+
+        label_markup = ""
+
+        if show_value:
+            if not value is None and len(value) > 0:
+                label_markup = "%s <span color='%s'>%s</span>" % (value, self._preferences.get("light-foreground-color"), label)
+            else:
+                label_markup = "<span color='%s'>%s</span>" % (self._preferences.get("light-foreground-color"), label)
+        else:
+            label_markup = label
+
+        self._store.append([value, label_markup])
+
+    @property
+    def value(self):
+        index = self._widget.get_active()
+        return self._options[index][0]
+
+
 class EntryProxy(AbstractProxy):
-	def __init__(self, widget, key):
-		AbstractProxy.__init__(self, widget, key)
-	
-	def restore(self, default):
-		self._widget.set_text(self._preferences.get(self._key, default))
-	
-	@property
-	def value(self):
-		return self._widget.get_text()
-	
+    def __init__(self, widget, key):
+        AbstractProxy.__init__(self, widget, key)
+
+    def restore(self, default):
+        self._widget.set_text(self._preferences.get(self._key, default))
+
+    @property
+    def value(self):
+        return self._widget.get_text()
+
 
 class ChooseMasterDialog(GladeInterface):
-	"""
-	Dialog for choosing a master file to a LaTeX fragment file
-	"""
-	filename = find_resource("ui/choose_master_dialog.ui")
-	
-	def run(self, folder):
-		"""
-		Runs the dialog and returns the selected filename
-		
-		@param folder: a folder to initially place the file chooser
-		"""
-		dialog = self.find_widget("dialogSelectMaster")
-		file_chooser_button = self.find_widget("filechooserbutton")
-		file_chooser_button.set_current_folder(folder)
-		
-		if dialog.run() == 1:
-			filename = file_chooser_button.get_filename()
-		else:
-			filename = None
-		dialog.hide()
-		
-		return filename
+    """
+    Dialog for choosing a master file to a LaTeX fragment file
+    """
+    filename = find_resource("ui/choose_master_dialog.ui")
+
+    def run(self, folder):
+        """
+        Runs the dialog and returns the selected filename
+
+        @param folder: a folder to initially place the file chooser
+        """
+        dialog = self.find_widget("dialogSelectMaster")
+        file_chooser_button = self.find_widget("filechooserbutton")
+        file_chooser_button.set_current_folder(folder)
+
+        if dialog.run() == 1:
+            filename = file_chooser_button.get_filename()
+        else:
+            filename = None
+        dialog.hide()
+
+        return filename
 
 class NewDocumentDialog(GladeInterface):
-	"""
-	Dialog for creating the body of a new LaTeX document
-	"""
-	filename = find_resource("ui/new_document_template_dialog.ui")
-	
-	_log = logging.getLogger("NewDocumentWizard")
-	
-	_PAPER_SIZES = (
-		("a4paper", "A4"),
-		("a5paper", "A5"),
-		("b5paper", "B5"),
-		("executivepaper", "US-Executive"),
-		("legalbl_paper", "US-Legal"),
-		("letterpaper", "US-Letter") )
-	
-	_DEFAULT_FONT_FAMILIES =  (
-		("\\rmdefault", "<span font_family=\"serif\">Roman</span>"),
-		("\\sfdefault", "<span font_family=\"sans\">Sans Serif</span>"),
-		("\\ttdefault", "<span font_family=\"monospace\">Typerwriter</span>") )
-	
-	_LOCALE_MAPPINGS = {
-		"af"    : "afrikaans",
-		"af_ZA" : "afrikaans",
-		"sq"	: "albanian",
-		"sq_AL" : "albanian",
-		"eu"	: "basque",
-		"eu_ES" : "basque",
-		"id"	: "bahasai",
-		"id_ID" : "bahasai",
-		"ms"	: "bahasam",
-		"ms_MY" : "bahasam",
-		"br"	: "breton",
-		"bg"	: "bulgarian",
-		"bg_BG" : "bulgarian",
-		"ca"	: "catalan",
-		"ca_ES" : "catalan",
-		"hr"	: "croatian",
-		"hr_HR" : "croatian",
-		"cz"	: "czech",
-		"da"	: "danish",
-		"da_DK" : "danish",
-		"nl"	: "dutch",
-		"nl_BE" : "dutch",
-		"nl_NL" : "dutch",
-		"eo"	: "esperanto",
-		"et"	: "estonian",
-		"et_EE" : "estonian",
-		"en"	: "USenglish",
-		"en_AU" : "australian",
-		"en_CA" : "canadian",
-		"en_GB" : "UKenglish",
-		"en_US" : "USenglish",
-		"en_ZA" : "UKenglish",
-		"fi"	: "finnish",
-		"fi_FI"	: "finnish",
-		"fr"    : "frenchb",
-		"fr_FR" : "frenchb",
-		"fr_CA" : "canadien",
-		"gl"	: "galician",
-		"gl_ES" : "galician",
-		"el"	: "greek",
-		"el_GR" : "greek",
-		"he"	: "hebrew",
-		"he_IL" : "hebrew",
-		"hu"	: "magyar",
-		"hu_HU" : "magyar",
-		"is"	: "icelandic",
-		"is_IS" : "icelandic",
-		"it"	: "italian",
-		"it_CH" : "italian",
-		"it_IT" : "italian",
-		"ga"	: "irish",
-		"la"    : "latin",
-		"dsb"	: "lsorbian",
-		"de_DE" : "ngermanb",
-		"de_AT" : "naustrian",
-		"de_CH" : "ngermanb",
-		"nb"	: "norsk",
-		"no"	: "norsk",
-		"no_NO" : "norsk",
-		"no_NY" : "norsk",
-		"nn"	: "nynorsk",
-		"nn_NO" : "nynorsk",
-		"pl"	: "polish",
-		"pl_PL" : "polish",
-		"pt"	: "portuguese",
-		"pt_BR" : "brazilian",
-		"pt_PT" : "portuguese",
-		"ro"	: "romanian",
-		"ro_RO" : "romanian",
-		"ru"    : "russianb",
-		"ru_RU" : "russianb",
-		"gd"	: "scottish",
-		"se"	: "samin",
-		"se_NO" : "samin",
-		"sr"	: "serbian",
-		"sr_YU" : "serbian",
-		"sl"	: "slovene",
-		"sl_SL" : "slovene",
-		"sl_SI"	: "slovene",
-		"sk"	: "slovak",
-		"sk_SK" : "slovak",
-		"es"	: "spanish",
-		"es_AR" : "spanish",
-		"es_CL" : "spanish",
-		"es_CO" : "spanish",
-		"es_DO" : "spanish",
-		"es_EC" : "spanish",
-		"es_ES" : "spanish",
-		"es_GT" : "spanish",
-		"es_HN" : "spanish",
-		"es_LA" : "spanish",
-		"es_MX" : "spanish",
-		"es_NI" : "spanish",
-		"es_PA" : "spanish",
-		"es_PE" : "spanish",
-		"es_PR" : "spanish",
-		"es_SV" : "spanish",
-		"es_UY" : "spanish",
-		"es_VE" : "spanish",
-		"es_UY" : "spanish",
-		"sv"	: "swedish",
-		"sv_SE" : "swedish",
-		"sv_SV" : "swedish",
-		"tr"	: "turkish",
-		"tr_TR"	: "turkish",
-		"uk"	: "ukraineb",
-		"uk_UA" : "ukraineb",
-		"hsb"	: "usorbian",
-		"cy"	: "welsh",
-		"cy_GB" : "welsh"
-	}
-		
-	dialog = None
-	
-	def get_dialog(self):
-		"""
-		Build and return the dialog
-		"""
-		if self.dialog == None:
-			preferences = Preferences()
-			environment = Environment()
-			
-			self.dialog = self.find_widget("dialogNewDocument")
-			
-			#
-			# file
-			#
-			self._entry_name = self.find_widget("entryName")
-			self._button_dir = self.find_widget("buttonDirectory")
-			self._button_dir.set_action(Gtk.FileChooserAction.SELECT_FOLDER)
-			
-			#
-			# templates
-			#
-			self._proxy_template = ComboBoxProxy(self.find_widget("comboTemplate"), "RecentTemplate")
-
-			folder = Folder(preferences.TEMPLATE_DIR)
-
-			templates = folder.files
-			templates.sort()
-			for template in templates:
-				self._proxy_template.add_option(template.path, template.shortbasename, show_value=False)
-			self._proxy_template.restore("Default")
-			
-			#
-			# metadata
-			#
-			self._entry_title = self.find_widget("entryTitle")
-			self._entry_author = self.find_widget("entryAuthor")
-			self._entry_author.set_text(preferences.get("RecentAuthor", environment.username))
-			self._radio_date_custom = self.find_widget("radioCustom")
-			self._entry_date = self.find_widget("entryDate")
-			
-			#
-			# document classes
-			#
-			self._proxy_document_class = ComboBoxProxy(self.find_widget("comboClass"), "RecentDocumentClass")
-			document_classes = environment.document_classes
-			document_classes.sort(key=lambda x: x.name.lower())
-			for c in document_classes:
-				self._proxy_document_class.add_option(c.name, c.label)
-			self._proxy_document_class.restore("article")
-				
-			#
-			# paper
-			#
-			self._proxy_paper_size = ComboBoxProxy(self.find_widget("comboPaperSize"), "RecentPaperSize")
-			self._proxy_paper_size.add_option("", "Default")
-			for size, label in self._PAPER_SIZES:
-				self._proxy_paper_size.add_option(size, label)
-			self._proxy_paper_size.restore("")
-			
-			
-			self._check_landscape = self.find_widget("checkLandscape")
-			self._check_landscape.set_active(preferences.get_bool("RecentPaperLandscape", False))
-			
-			#
-			# font size
-			#
-			self._radio_font_user = self.find_widget("radioFontUser")
-			self._spin_font_size = self.find_widget("spinFontSize")
-			self._labelFontSize = self.find_widget("labelFontSize")
-			
-			#
-			# font family
-			#
-			self._proxy_font_family = ComboBoxProxy(self.find_widget("comboFontFamily"), "RecentDefaultFontFamily")
-			for command, label in self._DEFAULT_FONT_FAMILIES:
-				self._proxy_font_family.add_option(command, label, False)
-			self._proxy_font_family.restore("\\rmdefault")
-			
-			
-			#
-			# input encodings
-			#
-			self._proxy_encoding = ComboBoxProxy(self.find_widget("comboEncoding"), "RecentInputEncoding")
-			input_encodings = environment.input_encodings
-			input_encodings.sort(key=lambda x: x.name.lower())
-			for e in input_encodings:
-				self._proxy_encoding.add_option(e.name, e.label)
-			self._proxy_encoding.restore("utf8")
-			
-			#
-			# babel packages
-			#
-			self._proxy_babel = ComboBoxProxy(self.find_widget("comboBabel"), "RecentBabelPackage")
-			language_definitions = environment.language_definitions
-			language_definitions.sort(key=lambda x: x.name.lower())
-			for l in language_definitions:
-				self._proxy_babel.add_option(l.name, l.label)
-				
-			try:
-				self._proxy_babel.restore(self._LOCALE_MAPPINGS[environment.language_code])
-			except Exception, e:
-				self._log.error("Failed to guess babel package: %s" % e)
-				self._proxy_babel.restore("english")
-			
-			#
-			# connect signals
-			#
-			self.connect_signals({ "on_radioCustom_toggled" : self._on_custom_date_toggled,
-								   "on_radioFontUser_toggled" : self._on_user_font_toggled })
-
-		return self.dialog
-	
-	def _on_custom_date_toggled(self, toggle_button):
-		self._entry_date.set_sensitive(toggle_button.get_active())
-		
-	def _on_user_font_toggled(self, toggle_button):
-		self._spin_font_size.set_sensitive(toggle_button.get_active())
-		self._labelFontSize.set_sensitive(toggle_button.get_active())
-	
-	@property
-	def source(self):
-		"""
-		Compose and return the source resulting from the dialog
-		"""
-		# document class options
-		documentOptions = []
-		
-		if self._radio_font_user.get_active():
-			documentOptions.append("%spt" % self._spin_font_size.get_value_as_int())
-		
-#		paperSize = self._store_paper_size[self._combo_paper_size.get_active()][0]
-#		if len(paperSize) > 0:
-#			documentOptions.append(paperSize)
-		paperSize = self._proxy_paper_size.value
-		if paperSize != "":
-			documentOptions.append(paperSize)
-		
-		if self._check_landscape.get_active():
-			documentOptions.append("landscape")
-		
-		if len(documentOptions) > 0:
-			documentOptions = "[" + ",".join(documentOptions) + "]"
-		else:
-			documentOptions = ""
-		
-		
-#		documentClass = self._store_class[self._combo_class.get_active()][0]
-		documentClass = self._proxy_document_class.value
-
-		title = self._entry_title.get_text()
-		author = self._entry_author.get_text()
-#		babelPackage = self._store_babel[self._combo_babel.get_active()][0]
-#		inputEncoding = self._store_encoding[self._combo_encoding.get_active()][0]
-		babelPackage = self._proxy_babel.value
-		inputEncoding = self._proxy_encoding.value
-		
-		if self._radio_date_custom.get_active():
-			date = self._entry_date.get_text()
-		else:
-			date = "\\today"
-		
-		if self.find_widget("checkPDFMeta").get_active():
-			ifpdf = "\n\\usepackage{ifpdf}\n\\usepackage{hyperref}"
-			
-			# pdfinfo is discouraged because it
-			#  - doesn't support special characters like umlauts
-			#  - is not supported by XeTeX
-			# see http://sourceforge.net/tracker/index.php?func=detail&aid=2809478&group_id=204144&atid=988431
-			
-#			pdfinfo = """
+    """
+    Dialog for creating the body of a new LaTeX document
+    """
+    filename = find_resource("ui/new_document_template_dialog.ui")
+
+    _log = logging.getLogger("NewDocumentWizard")
+
+    _PAPER_SIZES = (
+        ("a4paper", "A4"),
+        ("a5paper", "A5"),
+        ("b5paper", "B5"),
+        ("executivepaper", "US-Executive"),
+        ("legalbl_paper", "US-Legal"),
+        ("letterpaper", "US-Letter") )
+
+    _DEFAULT_FONT_FAMILIES =  (
+        ("\\rmdefault", "<span font_family=\"serif\">Roman</span>"),
+        ("\\sfdefault", "<span font_family=\"sans\">Sans Serif</span>"),
+        ("\\ttdefault", "<span font_family=\"monospace\">Typerwriter</span>") )
+
+    _LOCALE_MAPPINGS = {
+        "af"    : "afrikaans",
+        "af_ZA" : "afrikaans",
+        "sq"    : "albanian",
+        "sq_AL" : "albanian",
+        "eu"    : "basque",
+        "eu_ES" : "basque",
+        "id"    : "bahasai",
+        "id_ID" : "bahasai",
+        "ms"    : "bahasam",
+        "ms_MY" : "bahasam",
+        "br"    : "breton",
+        "bg"    : "bulgarian",
+        "bg_BG" : "bulgarian",
+        "ca"    : "catalan",
+        "ca_ES" : "catalan",
+        "hr"    : "croatian",
+        "hr_HR" : "croatian",
+        "cz"    : "czech",
+        "da"    : "danish",
+        "da_DK" : "danish",
+        "nl"    : "dutch",
+        "nl_BE" : "dutch",
+        "nl_NL" : "dutch",
+        "eo"    : "esperanto",
+        "et"    : "estonian",
+        "et_EE" : "estonian",
+        "en"    : "USenglish",
+        "en_AU" : "australian",
+        "en_CA" : "canadian",
+        "en_GB" : "UKenglish",
+        "en_US" : "USenglish",
+        "en_ZA" : "UKenglish",
+        "fi"    : "finnish",
+        "fi_FI"    : "finnish",
+        "fr"    : "frenchb",
+        "fr_FR" : "frenchb",
+        "fr_CA" : "canadien",
+        "gl"    : "galician",
+        "gl_ES" : "galician",
+        "el"    : "greek",
+        "el_GR" : "greek",
+        "he"    : "hebrew",
+        "he_IL" : "hebrew",
+        "hu"    : "magyar",
+        "hu_HU" : "magyar",
+        "is"    : "icelandic",
+        "is_IS" : "icelandic",
+        "it"    : "italian",
+        "it_CH" : "italian",
+        "it_IT" : "italian",
+        "ga"    : "irish",
+        "la"    : "latin",
+        "dsb"    : "lsorbian",
+        "de_DE" : "ngermanb",
+        "de_AT" : "naustrian",
+        "de_CH" : "ngermanb",
+        "nb"    : "norsk",
+        "no"    : "norsk",
+        "no_NO" : "norsk",
+        "no_NY" : "norsk",
+        "nn"    : "nynorsk",
+        "nn_NO" : "nynorsk",
+        "pl"    : "polish",
+        "pl_PL" : "polish",
+        "pt"    : "portuguese",
+        "pt_BR" : "brazilian",
+        "pt_PT" : "portuguese",
+        "ro"    : "romanian",
+        "ro_RO" : "romanian",
+        "ru"    : "russianb",
+        "ru_RU" : "russianb",
+        "gd"    : "scottish",
+        "se"    : "samin",
+        "se_NO" : "samin",
+        "sr"    : "serbian",
+        "sr_YU" : "serbian",
+        "sl"    : "slovene",
+        "sl_SL" : "slovene",
+        "sl_SI"    : "slovene",
+        "sk"    : "slovak",
+        "sk_SK" : "slovak",
+        "es"    : "spanish",
+        "es_AR" : "spanish",
+        "es_CL" : "spanish",
+        "es_CO" : "spanish",
+        "es_DO" : "spanish",
+        "es_EC" : "spanish",
+        "es_ES" : "spanish",
+        "es_GT" : "spanish",
+        "es_HN" : "spanish",
+        "es_LA" : "spanish",
+        "es_MX" : "spanish",
+        "es_NI" : "spanish",
+        "es_PA" : "spanish",
+        "es_PE" : "spanish",
+        "es_PR" : "spanish",
+        "es_SV" : "spanish",
+        "es_UY" : "spanish",
+        "es_VE" : "spanish",
+        "es_UY" : "spanish",
+        "sv"    : "swedish",
+        "sv_SE" : "swedish",
+        "sv_SV" : "swedish",
+        "tr"    : "turkish",
+        "tr_TR"    : "turkish",
+        "uk"    : "ukraineb",
+        "uk_UA" : "ukraineb",
+        "hsb"    : "usorbian",
+        "cy"    : "welsh",
+        "cy_GB" : "welsh"
+    }
+
+    dialog = None
+
+    def get_dialog(self):
+        """
+        Build and return the dialog
+        """
+        if self.dialog == None:
+            preferences = Preferences()
+            environment = Environment()
+
+            self.dialog = self.find_widget("dialogNewDocument")
+
+            #
+            # file
+            #
+            self._entry_name = self.find_widget("entryName")
+            self._button_dir = self.find_widget("buttonDirectory")
+            self._button_dir.set_action(Gtk.FileChooserAction.SELECT_FOLDER)
+
+            #
+            # templates
+            #
+            self._proxy_template = ComboBoxProxy(self.find_widget("comboTemplate"), "RecentTemplate")
+
+            folder = Folder(preferences.TEMPLATE_DIR)
+
+            templates = folder.files
+            templates.sort()
+            for template in templates:
+                self._proxy_template.add_option(template.path, template.shortbasename, show_value=False)
+            self._proxy_template.restore("Default")
+
+            #
+            # metadata
+            #
+            self._entry_title = self.find_widget("entryTitle")
+            self._entry_author = self.find_widget("entryAuthor")
+            self._entry_author.set_text(preferences.get("RecentAuthor", environment.username))
+            self._radio_date_custom = self.find_widget("radioCustom")
+            self._entry_date = self.find_widget("entryDate")
+
+            #
+            # document classes
+            #
+            self._proxy_document_class = ComboBoxProxy(self.find_widget("comboClass"), "RecentDocumentClass")
+            document_classes = environment.document_classes
+            document_classes.sort(key=lambda x: x.name.lower())
+            for c in document_classes:
+                self._proxy_document_class.add_option(c.name, c.label)
+            self._proxy_document_class.restore("article")
+
+            #
+            # paper
+            #
+            self._proxy_paper_size = ComboBoxProxy(self.find_widget("comboPaperSize"), "RecentPaperSize")
+            self._proxy_paper_size.add_option("", "Default")
+            for size, label in self._PAPER_SIZES:
+                self._proxy_paper_size.add_option(size, label)
+            self._proxy_paper_size.restore("")
+
+
+            self._check_landscape = self.find_widget("checkLandscape")
+            self._check_landscape.set_active(preferences.get_bool("RecentPaperLandscape", False))
+
+            #
+            # font size
+            #
+            self._radio_font_user = self.find_widget("radioFontUser")
+            self._spin_font_size = self.find_widget("spinFontSize")
+            self._labelFontSize = self.find_widget("labelFontSize")
+
+            #
+            # font family
+            #
+            self._proxy_font_family = ComboBoxProxy(self.find_widget("comboFontFamily"), "RecentDefaultFontFamily")
+            for command, label in self._DEFAULT_FONT_FAMILIES:
+                self._proxy_font_family.add_option(command, label, False)
+            self._proxy_font_family.restore("\\rmdefault")
+
+
+            #
+            # input encodings
+            #
+            self._proxy_encoding = ComboBoxProxy(self.find_widget("comboEncoding"), "RecentInputEncoding")
+            input_encodings = environment.input_encodings
+            input_encodings.sort(key=lambda x: x.name.lower())
+            for e in input_encodings:
+                self._proxy_encoding.add_option(e.name, e.label)
+            self._proxy_encoding.restore("utf8")
+
+            #
+            # babel packages
+            #
+            self._proxy_babel = ComboBoxProxy(self.find_widget("comboBabel"), "RecentBabelPackage")
+            language_definitions = environment.language_definitions
+            language_definitions.sort(key=lambda x: x.name.lower())
+            for l in language_definitions:
+                self._proxy_babel.add_option(l.name, l.label)
+
+            try:
+                self._proxy_babel.restore(self._LOCALE_MAPPINGS[environment.language_code])
+            except Exception, e:
+                self._log.error("Failed to guess babel package: %s" % e)
+                self._proxy_babel.restore("english")
+
+            #
+            # connect signals
+            #
+            self.connect_signals({ "on_radioCustom_toggled" : self._on_custom_date_toggled,
+                                   "on_radioFontUser_toggled" : self._on_user_font_toggled })
+
+        return self.dialog
+
+    def _on_custom_date_toggled(self, toggle_button):
+        self._entry_date.set_sensitive(toggle_button.get_active())
+
+    def _on_user_font_toggled(self, toggle_button):
+        self._spin_font_size.set_sensitive(toggle_button.get_active())
+        self._labelFontSize.set_sensitive(toggle_button.get_active())
+
+    @property
+    def source(self):
+        """
+        Compose and return the source resulting from the dialog
+        """
+        # document class options
+        documentOptions = []
+
+        if self._radio_font_user.get_active():
+            documentOptions.append("%spt" % self._spin_font_size.get_value_as_int())
+
+#        paperSize = self._store_paper_size[self._combo_paper_size.get_active()][0]
+#        if len(paperSize) > 0:
+#            documentOptions.append(paperSize)
+        paperSize = self._proxy_paper_size.value
+        if paperSize != "":
+            documentOptions.append(paperSize)
+
+        if self._check_landscape.get_active():
+            documentOptions.append("landscape")
+
+        if len(documentOptions) > 0:
+            documentOptions = "[" + ",".join(documentOptions) + "]"
+        else:
+            documentOptions = ""
+
+
+#        documentClass = self._store_class[self._combo_class.get_active()][0]
+        documentClass = self._proxy_document_class.value
+
+        title = self._entry_title.get_text()
+        author = self._entry_author.get_text()
+#        babelPackage = self._store_babel[self._combo_babel.get_active()][0]
+#        inputEncoding = self._store_encoding[self._combo_encoding.get_active()][0]
+        babelPackage = self._proxy_babel.value
+        inputEncoding = self._proxy_encoding.value
+
+        if self._radio_date_custom.get_active():
+            date = self._entry_date.get_text()
+        else:
+            date = "\\today"
+
+        if self.find_widget("checkPDFMeta").get_active():
+            ifpdf = "\n\\usepackage{ifpdf}\n\\usepackage{hyperref}"
+
+            # pdfinfo is discouraged because it
+            #  - doesn't support special characters like umlauts
+            #  - is not supported by XeTeX
+            # see http://sourceforge.net/tracker/index.php?func=detail&aid=2809478&group_id=204144&atid=988431
+
+#            pdfinfo = """
 #\\ifpdf
-#	\\pdfinfo {
-#		/Author (%s)
-#		/Title (%s)
-#		/Subject (SUBJECT)
-#		/Keywords (KEYWORDS)
-#		/CreationDate (D:%s)
-#	}
+#    \\pdfinfo {
+#        /Author (%s)
+#        /Title (%s)
+#        /Subject (SUBJECT)
+#        /Keywords (KEYWORDS)
+#        /CreationDate (D:%s)
+#    }
 #\\fi""" % (author, title, time.strftime('%Y%m%d%H%M%S'))
 
-			pdfinfo = """
+            pdfinfo = """
 \\ifpdf
 \\hypersetup{
-	pdfauthor={%s},
-	pdftitle={%s},
+    pdfauthor={%s},
+    pdftitle={%s},
 }
 \\fi""" % (author, title)
-		else:
-			ifpdf = ""
-			pdfinfo = ""
-		
-		if self._proxy_font_family.value == "\\rmdefault":
-			default_font_family = ""	# \rmdefault is the default value of \familydefault
-		else:
-			default_font_family = "\n\\renewcommand{\\familydefault}{%s}" % self._proxy_font_family.value
-		
-		template_string = open(self._proxy_template.value).read()
-		template = string.Template(template_string)
-		s = template.safe_substitute({
-					"DocumentOptions" : documentOptions, 
-					"DocumentClass" : documentClass, 
-					"InputEncoding" : inputEncoding, 
-					"BabelPackage" : babelPackage, 
-					"AdditionalPackages" : default_font_family + ifpdf, 
-					"Title" : title, 
-					"Author" : author, 
-					"Date" : date, 
-					"AdditionalPreamble" : pdfinfo})
-		
-#		s = """\\documentclass%s{%s}
+        else:
+            ifpdf = ""
+            pdfinfo = ""
+
+        if self._proxy_font_family.value == "\\rmdefault":
+            default_font_family = ""    # \rmdefault is the default value of \familydefault
+        else:
+            default_font_family = "\n\\renewcommand{\\familydefault}{%s}" % self._proxy_font_family.value
+
+        template_string = open(self._proxy_template.value).read()
+        template = string.Template(template_string)
+        s = template.safe_substitute({
+                    "DocumentOptions" : documentOptions,
+                    "DocumentClass" : documentClass,
+                    "InputEncoding" : inputEncoding,
+                    "BabelPackage" : babelPackage,
+                    "AdditionalPackages" : default_font_family + ifpdf,
+                    "Title" : title,
+                    "Author" : author,
+                    "Date" : date,
+                    "AdditionalPreamble" : pdfinfo})
+
+#        s = """\\documentclass%s{%s}
 #\\usepackage[%s]{inputenc}
 #\\usepackage[%s]{babel}%s%s
 #\\title{%s}
 #\\author{%s}
 #\\date{%s}%s
 #\\begin{document}
-#	
+#
 #\\end{document}""" % (documentOptions, documentClass, inputEncoding, babelPackage, default_font_family, ifpdf, title, author, date, pdfinfo)
-		
-		return s
-	
-	@property
-	def file(self):
-		"""
-		Return the File object
-		"""
-		return File("%s/%s.tex" % (self._button_dir.get_filename(), self._entry_name.get_text()))
-	
-	def run(self):
-		"""
-		Runs the dialog
-		"""
-		dialog = self.get_dialog()
-		r = dialog.run()
-		dialog.hide()
-		return r
+
+        return s
+
+    @property
+    def file(self):
+        """
+        Return the File object
+        """
+        return File("%s/%s.tex" % (self._button_dir.get_filename(), self._entry_name.get_text()))
+
+    def run(self):
+        """
+        Runs the dialog
+        """
+        dialog = self.get_dialog()
+        r = dialog.run()
+        dialog.hide()
+        return r
 
 
 class UseBibliographyDialog(GladeInterface, PreviewRenderer):
-	"""
-	Dialog for inserting a reference to a bibliography
-	"""
-	filename = find_resource("ui/use_bibliography_dialog.ui")
-	
-	_log = logging.getLogger("UseBibliographyWizard")
-	
-	
-	# sample bibtex content used for rendering the preview
-	_BIBTEX = """@book{ dijkstra76,
-	title={{A Discipline of Programming}},
-	author={Edsger W. Dijkstra},
-	year=1976 }
+    """
+    Dialog for inserting a reference to a bibliography
+    """
+    filename = find_resource("ui/use_bibliography_dialog.ui")
+
+    _log = logging.getLogger("UseBibliographyWizard")
+
+
+    # sample bibtex content used for rendering the preview
+    _BIBTEX = """@book{ dijkstra76,
+    title={{A Discipline of Programming}},
+    author={Edsger W. Dijkstra},
+    year=1976 }
 @article{ dijkstra68,
-	title={{Go to statement considered harmful}},
-	author={Edsger W. Dijkstra},
-	year=1968 }"""
-	
-	dialog = None
-	
-	def run_dialog(self, edited_file):
-		"""
-		Run the dialog
-		
-		@param edited_file: the File edited the active Editor
-		@return: the source resulting from the dialog
-		"""
-		source = None
-		dialog = self._get_dialog()
-		
-		self._file_chooser_button.set_current_folder(edited_file.dirname)
-		
-		if dialog.run() == 1:		# TODO: use gtk constant
-			base_dir = edited_file.dirname
-
-			file = File(self._file_chooser_button.get_filename())
-			source = "\\bibliography{%s}\n\\bibliographystyle{%s}" % (file.relativize_shortname(base_dir), 
-																	self._storeStyle[self._comboStyle.get_active()][0])
-		
-		dialog.hide()
-		
-		return source
-	
-	def _get_dialog(self):
-		if not self.dialog:
-			
-			self.dialog = self.find_widget("dialogUseBibliography")
-			
-			# bib file
-			
-			self._file_chooser_button = self.find_widget("filechooserbutton")
-			
-			# styles
-			
-			self._storeStyle = Gtk.ListStore(str)
-			
-			styles = Environment().bibtex_styles
-			for style in styles:
-				self._storeStyle.append([style.name])
-			
-			self._comboStyle = self.find_widget("comboboxStyle")
-			self._comboStyle.set_model(self._storeStyle)
-			
-			try:
-				recent = styles.index(Preferences().get("RecentBibtexStyle", "plain"))
-			except ValueError:
-				recent = 0
-			self._comboStyle.set_active(recent)
-			
-			self._imagePreview = self.find_widget("previewimage")
-			self._imagePreview.show()
-
-			self.connect_signals({ "on_buttonRefresh_clicked" : self._on_refresh_clicked })
-			
-		return self.dialog
-	
-	def _on_refresh_clicked(self, widget):
-		"""
-		The button for refreshing the preview has been clicked
-		"""
-		index = self._comboStyle.get_active()
-		if index < 0:
-			self._log.error("No style selected")
-			return
-		
-		style = self._storeStyle[index][0]
-		
-		self._imagePreview.set_from_stock(Gtk.STOCK_EXECUTE, Gtk.IconSize.BUTTON)
-		
-		# create temporary bibtex file
-		self._tempFile = tempfile.NamedTemporaryFile(mode="w", suffix=".bib")
-		self._tempFile.write(self._BIBTEX)
-		self._tempFile.flush()
-		
-		filename = self._tempFile.name
-		self._filenameBase = os.path.splitext(os.path.basename(filename))[0]
-		
-		# build preview image
-		self.render("Book \\cite{dijkstra76} Article \\cite{dijkstra68} \\bibliography{%s}\\bibliographystyle{%s}" % (self._filenameBase, 
-																													style))
-	def _on_render_succeeded(self, pixbuf):
-		# PreviewRenderer._on_render_succeeded
-		self._imagePreview.set_from_pixbuf(pixbuf)
-		# remove the temp bib file
-		self._tempFile.close()
-	
-	def _on_render_failed(self):
-		# PreviewRenderer._on_render_failed
-		
-		# set a default icon as preview
-		self._imagePreview.set_from_stock(Gtk.STOCK_STOP, Gtk.IconSize.BUTTON)
-		# remove the temp bib file
-		self._tempFile.close()
+    title={{Go to statement considered harmful}},
+    author={Edsger W. Dijkstra},
+    year=1968 }"""
+
+    dialog = None
+
+    def run_dialog(self, edited_file):
+        """
+        Run the dialog
+
+        @param edited_file: the File edited the active Editor
+        @return: the source resulting from the dialog
+        """
+        source = None
+        dialog = self._get_dialog()
+
+        self._file_chooser_button.set_current_folder(edited_file.dirname)
+
+        if dialog.run() == 1:        # TODO: use gtk constant
+            base_dir = edited_file.dirname
+
+            file = File(self._file_chooser_button.get_filename())
+            source = "\\bibliography{%s}\n\\bibliographystyle{%s}" % (file.relativize_shortname(base_dir),
+                                                                    self._storeStyle[self._comboStyle.get_active()][0])
+
+        dialog.hide()
+
+        return source
+
+    def _get_dialog(self):
+        if not self.dialog:
+
+            self.dialog = self.find_widget("dialogUseBibliography")
+
+            # bib file
+
+            self._file_chooser_button = self.find_widget("filechooserbutton")
+
+            # styles
+
+            self._storeStyle = Gtk.ListStore(str)
+
+            styles = Environment().bibtex_styles
+            for style in styles:
+                self._storeStyle.append([style.name])
+
+            self._comboStyle = self.find_widget("comboboxStyle")
+            self._comboStyle.set_model(self._storeStyle)
+
+            try:
+                recent = styles.index(Preferences().get("RecentBibtexStyle", "plain"))
+            except ValueError:
+                recent = 0
+            self._comboStyle.set_active(recent)
+
+            self._imagePreview = self.find_widget("previewimage")
+            self._imagePreview.show()
+
+            self.connect_signals({ "on_buttonRefresh_clicked" : self._on_refresh_clicked })
+
+        return self.dialog
+
+    def _on_refresh_clicked(self, widget):
+        """
+        The button for refreshing the preview has been clicked
+        """
+        index = self._comboStyle.get_active()
+        if index < 0:
+            self._log.error("No style selected")
+            return
+
+        style = self._storeStyle[index][0]
+
+        self._imagePreview.set_from_stock(Gtk.STOCK_EXECUTE, Gtk.IconSize.BUTTON)
+
+        # create temporary bibtex file
+        self._tempFile = tempfile.NamedTemporaryFile(mode="w", suffix=".bib")
+        self._tempFile.write(self._BIBTEX)
+        self._tempFile.flush()
+
+        filename = self._tempFile.name
+        self._filenameBase = os.path.splitext(os.path.basename(filename))[0]
+
+        # build preview image
+        self.render("Book \\cite{dijkstra76} Article \\cite{dijkstra68} \\bibliography{%s}\\bibliographystyle{%s}" % (self._filenameBase,
+                                                                                                                    style))
+    def _on_render_succeeded(self, pixbuf):
+        # PreviewRenderer._on_render_succeeded
+        self._imagePreview.set_from_pixbuf(pixbuf)
+        # remove the temp bib file
+        self._tempFile.close()
+
+    def _on_render_failed(self):
+        # PreviewRenderer._on_render_failed
+
+        # set a default icon as preview
+        self._imagePreview.set_from_stock(Gtk.STOCK_STOP, Gtk.IconSize.BUTTON)
+        # remove the temp bib file
+        self._tempFile.close()
 
 
 class InsertGraphicsDialog(GladeInterface):
-	
-	_PREVIEW_WIDTH, _PREVIEW_HEIGHT = 128, 128
-	filename = find_resource("ui/insert_graphics_dialog.ui")
-	_dialog = None
-	
-	def run(self, edited_file):
-		"""
-		@param edited_file: the File currently edited
-		"""
-		dialog = self.__get_dialog()
-		
-		source = None
-		
-		if dialog.run() == 1:
-			#filename = self._fileChooser.get_filename()
-			
-			file = File(self._fileChooser.get_filename())
-			relative_filename = file.relativize(edited_file.dirname)
-			
-			# for eps and pdf the extension should be omitted 
-			# (see http://www.tex.ac.uk/cgi-bin/texfaq2html?label=graph-pspdf)
-			ext = os.path.splitext(relative_filename)[1]
-			if ext == ".pdf" or ext == ".eps":
-				relative_filename = os.path.splitext(relative_filename)[0]
-				
-			width = "%.2f" % round(self.find_widget("spinbuttonWidth").get_value() / 100.0, 2)
-			caption = self.find_widget("entryCaption").get_text()
-			label = self.find_widget("entryLabel").get_text()
-			floating = self.find_widget("checkbuttonFloat").get_active()
-			spread = self.find_widget("checkbuttonSpread").get_active()
-			relativeTo = self._comboRelative.get_active()
-			rotate = self.find_widget("spinRotate").get_value()
-			flip = self.find_widget("checkFlip").get_active()
-			
-			source = ""
-			packages = ["graphicx"]
-			
-			if relativeTo == 0:		# relative to image size
-				options = "scale=%s" % width
-			else:		# relative to text width
-				options = "width=%s\\textwidth" % width
-			
-			if rotate != 0:
-				options += ", angle=%s" % rotate
-			
-			includegraphics = "\\includegraphics[%s]{%s}" % (options, relative_filename)
-			
-			if flip:
-				includegraphics = "\\reflectbox{%s}" % includegraphics
-			
-			if floating:
-				if spread:
-					ast = "*"
-				else:
-					ast = ""
-				source += "\\begin{figure%s}[ht]\n\t\\centering\n\t%s" % (ast, includegraphics)
-				if len(caption):
-					source += "\n\t\\caption{%s}" % caption
-				if len(label) and label != "fig:":
-					source += "\n\t\\label{%s}" % label
-				source += "\n\\end{figure%s}" % ast
-			else:
-				source += "\\begin{center}\n\t%s" % includegraphics
-				if len(caption):
-					source += "\n\t\\captionof{figure}{%s}" % caption
-					packages.append("caption")
-				if len(label) and label != "fig:":
-					source += "\n\t\\label{%s}" % label
-				source += "\n\\end{center}"
-				
-#			viewAdapter.insertText(source)
-#			viewAdapter.ensurePackages(packages)
-
-			source = LaTeXSource(source, packages)
-		
-		dialog.hide()
-		
-		return source
-	
-	def __get_dialog(self):
-		if not self._dialog:
-			
-			# setup the dialog
-			
-			self._dialog = self.find_widget("dialogInsertGraphics")
-			
-			previewImage = Gtk.Image()
-			self._fileChooser = self.find_widget("FileChooser")
-			self._fileChooser.set_preview_widget(previewImage)
-			self._fileChooser.connect("update-preview", self.__update_preview, previewImage)
-			
-			#self._fileChooser.get_property("dialog").connect("response", self._fileChooserResponse)	# FIXME: not readable
-			#self._fileChooser.connect("file-activated", self._fileActivated)							# FIXME: doesn't work
-			
-			self._okayButton = self.find_widget("buttonOkay")
-			
-			self._comboRelative = self.find_widget("comboRelative")
-			self._comboRelative.set_active(0)
-			
-		return self._dialog
-	
-	def __update_preview(self, fileChooser, previewImage):
-		""" 
-		Update the FileChooser's preview image 
-		"""
-		filename = fileChooser.get_preview_filename()
-		
-		try:
-			pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, self._PREVIEW_WIDTH, self._PREVIEW_HEIGHT)
-			previewImage.set_from_pixbuf(pixbuf)
-			fileChooser.set_preview_widget_active(True)
-		except:
-			fileChooser.set_preview_widget_active(False)
+
+    _PREVIEW_WIDTH, _PREVIEW_HEIGHT = 128, 128
+    filename = find_resource("ui/insert_graphics_dialog.ui")
+    _dialog = None
+
+    def run(self, edited_file):
+        """
+        @param edited_file: the File currently edited
+        """
+        dialog = self.__get_dialog()
+
+        source = None
+
+        if dialog.run() == 1:
+            #filename = self._fileChooser.get_filename()
+
+            file = File(self._fileChooser.get_filename())
+            relative_filename = file.relativize(edited_file.dirname)
+
+            # for eps and pdf the extension should be omitted
+            # (see http://www.tex.ac.uk/cgi-bin/texfaq2html?label=graph-pspdf)
+            ext = os.path.splitext(relative_filename)[1]
+            if ext == ".pdf" or ext == ".eps":
+                relative_filename = os.path.splitext(relative_filename)[0]
+
+            width = "%.2f" % round(self.find_widget("spinbuttonWidth").get_value() / 100.0, 2)
+            caption = self.find_widget("entryCaption").get_text()
+            label = self.find_widget("entryLabel").get_text()
+            floating = self.find_widget("checkbuttonFloat").get_active()
+            spread = self.find_widget("checkbuttonSpread").get_active()
+            relativeTo = self._comboRelative.get_active()
+            rotate = self.find_widget("spinRotate").get_value()
+            flip = self.find_widget("checkFlip").get_active()
+
+            source = ""
+            packages = ["graphicx"]
+
+            if relativeTo == 0:        # relative to image size
+                options = "scale=%s" % width
+            else:        # relative to text width
+                options = "width=%s\\textwidth" % width
+
+            if rotate != 0:
+                options += ", angle=%s" % rotate
+
+            includegraphics = "\\includegraphics[%s]{%s}" % (options, relative_filename)
+
+            if flip:
+                includegraphics = "\\reflectbox{%s}" % includegraphics
+
+            if floating:
+                if spread:
+                    ast = "*"
+                else:
+                    ast = ""
+                source += "\\begin{figure%s}[ht]\n\t\\centering\n\t%s" % (ast, includegraphics)
+                if len(caption):
+                    source += "\n\t\\caption{%s}" % caption
+                if len(label) and label != "fig:":
+                    source += "\n\t\\label{%s}" % label
+                source += "\n\\end{figure%s}" % ast
+            else:
+                source += "\\begin{center}\n\t%s" % includegraphics
+                if len(caption):
+                    source += "\n\t\\captionof{figure}{%s}" % caption
+                    packages.append("caption")
+                if len(label) and label != "fig:":
+                    source += "\n\t\\label{%s}" % label
+                source += "\n\\end{center}"
+
+#            viewAdapter.insertText(source)
+#            viewAdapter.ensurePackages(packages)
+
+            source = LaTeXSource(source, packages)
+
+        dialog.hide()
+
+        return source
+
+    def __get_dialog(self):
+        if not self._dialog:
+
+            # setup the dialog
+
+            self._dialog = self.find_widget("dialogInsertGraphics")
+
+            previewImage = Gtk.Image()
+            self._fileChooser = self.find_widget("FileChooser")
+            self._fileChooser.set_preview_widget(previewImage)
+            self._fileChooser.connect("update-preview", self.__update_preview, previewImage)
+
+            #self._fileChooser.get_property("dialog").connect("response", self._fileChooserResponse)    # FIXME: not readable
+            #self._fileChooser.connect("file-activated", self._fileActivated)                            # FIXME: doesn't work
+
+            self._okayButton = self.find_widget("buttonOkay")
+
+            self._comboRelative = self.find_widget("comboRelative")
+            self._comboRelative.set_active(0)
+
+        return self._dialog
+
+    def __update_preview(self, fileChooser, previewImage):
+        """
+        Update the FileChooser's preview image
+        """
+        filename = fileChooser.get_preview_filename()
+
+        try:
+            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, self._PREVIEW_WIDTH, self._PREVIEW_HEIGHT)
+            previewImage.set_from_pixbuf(pixbuf)
+            fileChooser.set_preview_widget_active(True)
+        except:
+            fileChooser.set_preview_widget_active(False)
 
 
 class InsertTableDialog(GladeInterface):
-	"""
-	This is used to include tables and matrices
-	"""
-	
-	filename = find_resource("ui/insert_table_dialog.ui")
-	_dialog = None
-	
-	def run(self):
-		dialog = self.__get_dialog()
-		
-		source = None
-		
-		if dialog.run() == 1:
-			floating = self.find_widget("checkbuttonFloat").get_active()
-			rows = int(self.find_widget("spinbuttonRows").get_value())
-			cols = int(self.find_widget("spinbuttonColumns").get_value())
-			caption = self.find_widget("entryCaption").get_text()
-			label = self.find_widget("entryLabel").get_text()
-			
-			s = ""
-			
-			if self.find_widget("radiobuttonTable").get_active():
-				# table
-				
-				layout = "l" * cols
-				
-				if floating:
-					s += "\\begin{table}[ht]\n\t\\centering\n\t\\begin{tabular}{%s}%s\n\t\\end{tabular}" % (layout, 
-																						self.__build_table_body(rows, cols, "\t\t"))
-					
-					if len(caption):
-						s += "\n\t\\caption{%s}" % caption
-					
-					if len(label) and label != "tab:":
-						s += "\n\t\\label{%s}" % label
-					
-					s += "\n\\end{table}"
-				
-				else:
-					s += "\\begin{center}\n\t\\begin{tabular}{%s}%s\n\t\\end{tabular}" % (layout, 
-																						self.__build_table_body(rows, cols, "\t\t"))
-					
-					if len(caption):
-						s += "\n\t\\captionof{table}{%s}" % caption
-					
-					if len(label):
-						s += "\n\t\\label{%s}" % label
-					
-					s += "\n\\end{center}"
-				packages = []
-			else:
-				environ = self._storeDelims[self._comboDelims.get_active()][2]
-				s = "\\begin{%s}%s\n\\end{%s}" % (environ, self.__build_table_body(rows, cols, "\t\t"), environ)
-				packages = ["amsmath"]
-			
-			source = LaTeXSource(Template(s), packages)
-			
-		dialog.hide()
-		
-		return source
-	
-	def __get_dialog(self):
-		if not self._dialog:
-			self._dialog = self.find_widget("dialogInsertTable")
-			
-			# delimiters
-			self._storeDelims = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str)  	# icon, label, environment
-			
-			self._storeDelims.append([None, "None", "matrix"])
-			
-			delimiters = [("parantheses", "Parantheses", "pmatrix"), 
-						("brackets", "Brackets", "bmatrix"), 
-						("braces", "Braces", "Bmatrix"), 
-						("vbars", "Vertical Bars", "vmatrix"), 
-						("dvbars", "Double Vertical Bars", "Vmatrix")]
-			
-			for d in delimiters:
-				pixbuf = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/%s.png" % d[0]))
-				self._storeDelims.append([pixbuf, d[1], d[2]])
-			
-			self._comboDelims = self.find_widget("comboDelims")
-			self._comboDelims.set_model(self._storeDelims)
-			
-			cellPixbuf = Gtk.CellRendererPixbuf()
-			self._comboDelims.pack_start(cellPixbuf, False)
-			self._comboDelims.add_attribute(cellPixbuf, 'pixbuf', 0)
-			
-			cellText = Gtk.CellRendererText()
-			self._comboDelims.pack_start(cellText, False)
-			self._comboDelims.add_attribute(cellText, 'text', 1)
-			
-			self._comboDelims.set_active(0)
-			
-			self.connect_signals({ "on_radioMatrix_toggled" : self.__matrix_toggled })
-			
-		return self._dialog
-
-	def __matrix_toggled(self, toggleButton):
-		self._comboDelims.set_sensitive(toggleButton.get_active())
-
-	def __build_table_body(self, rows, cols, indent):
-		"""
-		This builds the body of a table template according to the number of rows and
-		columns.
-		"""
-		
-		# FIXME: create unique placeholders for each cell for multi-placeholder feature
-		
-		s = ""
-		for i in range(rows):
-			colList = []
-			for j in range(cols):
-				colList.append("${%s%s}" % (i + 1, j + 1))
-			s += "\n" + indent + " & ".join(colList) + " \\\\"
-		return s
+    """
+    This is used to include tables and matrices
+    """
+
+    filename = find_resource("ui/insert_table_dialog.ui")
+    _dialog = None
+
+    def run(self):
+        dialog = self.__get_dialog()
+
+        source = None
+
+        if dialog.run() == 1:
+            floating = self.find_widget("checkbuttonFloat").get_active()
+            rows = int(self.find_widget("spinbuttonRows").get_value())
+            cols = int(self.find_widget("spinbuttonColumns").get_value())
+            caption = self.find_widget("entryCaption").get_text()
+            label = self.find_widget("entryLabel").get_text()
+
+            s = ""
+
+            if self.find_widget("radiobuttonTable").get_active():
+                # table
+
+                layout = "l" * cols
+
+                if floating:
+                    s += "\\begin{table}[ht]\n\t\\centering\n\t\\begin{tabular}{%s}%s\n\t\\end{tabular}" % (layout,
+                                                                                        self.__build_table_body(rows, cols, "\t\t"))
+
+                    if len(caption):
+                        s += "\n\t\\caption{%s}" % caption
+
+                    if len(label) and label != "tab:":
+                        s += "\n\t\\label{%s}" % label
+
+                    s += "\n\\end{table}"
+
+                else:
+                    s += "\\begin{center}\n\t\\begin{tabular}{%s}%s\n\t\\end{tabular}" % (layout,
+                                                                                        self.__build_table_body(rows, cols, "\t\t"))
+
+                    if len(caption):
+                        s += "\n\t\\captionof{table}{%s}" % caption
+
+                    if len(label):
+                        s += "\n\t\\label{%s}" % label
+
+                    s += "\n\\end{center}"
+                packages = []
+            else:
+                environ = self._storeDelims[self._comboDelims.get_active()][2]
+                s = "\\begin{%s}%s\n\\end{%s}" % (environ, self.__build_table_body(rows, cols, "\t\t"), environ)
+                packages = ["amsmath"]
+
+            source = LaTeXSource(Template(s), packages)
+
+        dialog.hide()
+
+        return source
+
+    def __get_dialog(self):
+        if not self._dialog:
+            self._dialog = self.find_widget("dialogInsertTable")
+
+            # delimiters
+            self._storeDelims = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str)      # icon, label, environment
+
+            self._storeDelims.append([None, "None", "matrix"])
+
+            delimiters = [("parantheses", "Parantheses", "pmatrix"),
+                        ("brackets", "Brackets", "bmatrix"),
+                        ("braces", "Braces", "Bmatrix"),
+                        ("vbars", "Vertical Bars", "vmatrix"),
+                        ("dvbars", "Double Vertical Bars", "Vmatrix")]
+
+            for d in delimiters:
+                pixbuf = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/%s.png" % d[0]))
+                self._storeDelims.append([pixbuf, d[1], d[2]])
+
+            self._comboDelims = self.find_widget("comboDelims")
+            self._comboDelims.set_model(self._storeDelims)
+
+            cellPixbuf = Gtk.CellRendererPixbuf()
+            self._comboDelims.pack_start(cellPixbuf, False)
+            self._comboDelims.add_attribute(cellPixbuf, 'pixbuf', 0)
+
+            cellText = Gtk.CellRendererText()
+            self._comboDelims.pack_start(cellText, False)
+            self._comboDelims.add_attribute(cellText, 'text', 1)
+
+            self._comboDelims.set_active(0)
+
+            self.connect_signals({ "on_radioMatrix_toggled" : self.__matrix_toggled })
+
+        return self._dialog
+
+    def __matrix_toggled(self, toggleButton):
+        self._comboDelims.set_sensitive(toggleButton.get_active())
+
+    def __build_table_body(self, rows, cols, indent):
+        """
+        This builds the body of a table template according to the number of rows and
+        columns.
+        """
+
+        # FIXME: create unique placeholders for each cell for multi-placeholder feature
+
+        s = ""
+        for i in range(rows):
+            colList = []
+            for j in range(cols):
+                colList.append("${%s%s}" % (i + 1, j + 1))
+            s += "\n" + indent + " & ".join(colList) + " \\\\"
+        return s
 
 
 class InsertListingDialog(GladeInterface):
-	"""
-	"""
-	
-	filename = find_resource("ui/insert_listing_dialog.ui")
-	_dialog = None
-	
-	def run(self, edited_file):
-		"""
-		@param edited_file: the File currently edited
-		"""
-		dialog = self.__get_dialog()
-		
-		source = None
-		
-		if dialog.run() == 1:
-			
-			lstset = ""
-			options = ""
-			
-			language = self._storeLanguages[self._comboLanguage.get_active()][0]
-			try:
-				dialect = self._storeDialects[self._comboDialect.get_active()][0]
-			except IndexError:
-				dialect = ""
-			
-			if self._dialectsEnabled and len(dialect):
-				# we need the lstset command
-				lstset = "\\lstset{language=[%s]%s}\n" % (dialect, language)
-			else:
-				options = "[language=%s]" % language
-			
-			
-			if self._checkFile.get_active():
-				file = File(self._fileChooserButton.get_filename())
-				relative_filename = file.relativize(edited_file.dirname)
-				
-				source = "%s\\lstinputlisting%s{%s}" % (lstset, options, relative_filename)
-			
-			else:
-				source = "%s\\begin{lstlisting}%s\n\t$_\n\\end{lstlisting}" % (lstset, options)
-			
-			source = LaTeXSource(Template(source), ["listings"])
-			
-		dialog.hide()
-		
-		return source
-	
-	def __get_dialog(self):
-		if not self._dialog:
-			self._dialog = self.find_widget("dialogListing")
-			
-			self._fileChooserButton = self.find_widget("fileChooserButton")
-			
-			#
-			# languages
-			#
-			self._languages = []
-			parser = LanguagesParser()
-			parser.parse(self._languages, find_resource("listings.xml"))
-			
-			recentLanguage = Preferences().get("RecentListingLanguage", "Java")
-			
-			self._storeLanguages = Gtk.ListStore(str)
-			recentLanguageIndex = 0
-			i = 0
-			for l in self._languages:
-				self._storeLanguages.append([l.name])
-				if l.name == recentLanguage:
-					recentLanguageIndex = i
-				i += 1
-			
-			self._comboLanguage = self.find_widget("comboLanguage")
-			self._comboLanguage.set_model(self._storeLanguages)
-			cell = Gtk.CellRendererText()
-			self._comboLanguage.pack_start(cell, True)
-			self._comboLanguage.add_attribute(cell, "text", 0)
-			
-			#
-			# dialects
-			#
-			self._labelDialect = self.find_widget("labelDialect")
-			
-			self._storeDialects = Gtk.ListStore(str)
-			
-			self._comboDialect = self.find_widget("comboDialect")
-			self._comboDialect.set_model(self._storeDialects)
-			cell = Gtk.CellRendererText()
-			self._comboDialect.pack_start(cell, True)
-			self._comboDialect.add_attribute(cell, "text", 0)
-			
-			self._checkFile = self.find_widget("checkFile")
-			
-			self.connect_signals({ "on_checkFile_toggled" : self._fileToggled,
-								  "on_comboLanguage_changed" : self._languagesChanged })
-			
-			
-			self._comboLanguage.set_active(recentLanguageIndex)
-			
-			self._dialectsEnabled = False
-			
-		return self._dialog
-	
-	def _languagesChanged(self, comboBox):
-		language = self._languages[comboBox.get_active()]
-		
-		self._storeDialects.clear()
-		
-		if len(language.dialects):
-			i = 0
-			for dialect in language.dialects:
-				self._storeDialects.append([dialect.name])
-				if dialect.default:
-					self._comboDialect.set_active(i)
-				i += 1
-			
-			self._labelDialect.set_sensitive(True)
-			self._comboDialect.set_sensitive(True)
-			
-			self._dialectsEnabled = True
-		else:
-			self._labelDialect.set_sensitive(False)
-			self._comboDialect.set_sensitive(False)
-			
-			self._dialectsEnabled = False
-	
-	def _fileToggled(self, toggleButton):
-		self._fileChooserButton.set_sensitive(toggleButton.get_active())
-	
-	def _specificToggled(self, toggleButton):
-		self._comboLanguage.set_sensitive(toggleButton.get_active())
-		self._labelDialect.set_sensitive(toggleButton.get_active() and self._dialectsEnabled)
-		self._comboDialect.set_sensitive(toggleButton.get_active() and self._dialectsEnabled)
+    """
+    """
+
+    filename = find_resource("ui/insert_listing_dialog.ui")
+    _dialog = None
+
+    def run(self, edited_file):
+        """
+        @param edited_file: the File currently edited
+        """
+        dialog = self.__get_dialog()
+
+        source = None
+
+        if dialog.run() == 1:
+
+            lstset = ""
+            options = ""
+
+            language = self._storeLanguages[self._comboLanguage.get_active()][0]
+            try:
+                dialect = self._storeDialects[self._comboDialect.get_active()][0]
+            except IndexError:
+                dialect = ""
+
+            if self._dialectsEnabled and len(dialect):
+                # we need the lstset command
+                lstset = "\\lstset{language=[%s]%s}\n" % (dialect, language)
+            else:
+                options = "[language=%s]" % language
+
+
+            if self._checkFile.get_active():
+                file = File(self._fileChooserButton.get_filename())
+                relative_filename = file.relativize(edited_file.dirname)
+
+                source = "%s\\lstinputlisting%s{%s}" % (lstset, options, relative_filename)
+
+            else:
+                source = "%s\\begin{lstlisting}%s\n\t$_\n\\end{lstlisting}" % (lstset, options)
+
+            source = LaTeXSource(Template(source), ["listings"])
+
+        dialog.hide()
+
+        return source
+
+    def __get_dialog(self):
+        if not self._dialog:
+            self._dialog = self.find_widget("dialogListing")
+
+            self._fileChooserButton = self.find_widget("fileChooserButton")
+
+            #
+            # languages
+            #
+            self._languages = []
+            parser = LanguagesParser()
+            parser.parse(self._languages, find_resource("listings.xml"))
+
+            recentLanguage = Preferences().get("RecentListingLanguage", "Java")
+
+            self._storeLanguages = Gtk.ListStore(str)
+            recentLanguageIndex = 0
+            i = 0
+            for l in self._languages:
+                self._storeLanguages.append([l.name])
+                if l.name == recentLanguage:
+                    recentLanguageIndex = i
+                i += 1
+
+            self._comboLanguage = self.find_widget("comboLanguage")
+            self._comboLanguage.set_model(self._storeLanguages)
+            cell = Gtk.CellRendererText()
+            self._comboLanguage.pack_start(cell, True)
+            self._comboLanguage.add_attribute(cell, "text", 0)
+
+            #
+            # dialects
+            #
+            self._labelDialect = self.find_widget("labelDialect")
+
+            self._storeDialects = Gtk.ListStore(str)
+
+            self._comboDialect = self.find_widget("comboDialect")
+            self._comboDialect.set_model(self._storeDialects)
+            cell = Gtk.CellRendererText()
+            self._comboDialect.pack_start(cell, True)
+            self._comboDialect.add_attribute(cell, "text", 0)
+
+            self._checkFile = self.find_widget("checkFile")
+
+            self.connect_signals({ "on_checkFile_toggled" : self._fileToggled,
+                                  "on_comboLanguage_changed" : self._languagesChanged })
+
+
+            self._comboLanguage.set_active(recentLanguageIndex)
+
+            self._dialectsEnabled = False
+
+        return self._dialog
+
+    def _languagesChanged(self, comboBox):
+        language = self._languages[comboBox.get_active()]
+
+        self._storeDialects.clear()
+
+        if len(language.dialects):
+            i = 0
+            for dialect in language.dialects:
+                self._storeDialects.append([dialect.name])
+                if dialect.default:
+                    self._comboDialect.set_active(i)
+                i += 1
+
+            self._labelDialect.set_sensitive(True)
+            self._comboDialect.set_sensitive(True)
+
+            self._dialectsEnabled = True
+        else:
+            self._labelDialect.set_sensitive(False)
+            self._comboDialect.set_sensitive(False)
+
+            self._dialectsEnabled = False
+
+    def _fileToggled(self, toggleButton):
+        self._fileChooserButton.set_sensitive(toggleButton.get_active())
+
+    def _specificToggled(self, toggleButton):
+        self._comboLanguage.set_sensitive(toggleButton.get_active())
+        self._labelDialect.set_sensitive(toggleButton.get_active() and self._dialectsEnabled)
+        self._comboDialect.set_sensitive(toggleButton.get_active() and self._dialectsEnabled)
 
 
 class BuildImageDialog(GladeInterface):
-	"""
-	Render the document to an image
-	"""
-	
-	filename = find_resource("ui/build_image_dialog.ui")
-	_dialog = None
-	_generator = ImageToolGenerator()
-	
-	def run(self):
-		dialog = self._getDialog()
-		
-		if dialog.run() == 1:
-
-			if self.find_widget("radioPNG").get_active():
-				self._generator.format = ImageToolGenerator.FORMAT_PNG
-				self._generator.pngMode = self._storeMode[self._comboMode.get_active()][1]
-			elif self.find_widget("radioJPEG").get_active():
-				self._generator.format = ImageToolGenerator.FORMAT_JPEG
-			elif self.find_widget("radioGIF").get_active():
-				self._generator.format = ImageToolGenerator.FORMAT_GIF
-			
-			self._generator.open = True
-			self._generator.resolution = self._spinResolution.get_value_as_int()
-			self._generator.antialiasFactor = self._storeAntialias[self._comboAntialias.get_active()][1]
-			self._generator.render_box = self.find_widget("radioBox").get_active()
-			
-			generate = True
-		else:
-			generate = False
-			
-		dialog.hide()
-		
-		if generate:
-			return self._generator.generate()
-		else:
-			return None
-	
-	def _getDialog(self):
-		if not self._dialog:
-			self._dialog = self.find_widget("dialogRenderImage")
-			self.find_widget("table").set_col_spacing(0, 20)
-			
-			# PNG mode
-			
-			self._storeMode = Gtk.ListStore(str, int)	# label, mode constant
-			self._storeMode.append(["Monochrome", ImageToolGenerator.PNG_MODE_MONOCHROME])
-			self._storeMode.append(["Grayscale", ImageToolGenerator.PNG_MODE_GRAYSCALE])
-			self._storeMode.append(["RGB", ImageToolGenerator.PNG_MODE_RGB])
-			self._storeMode.append(["RGBA", ImageToolGenerator.PNG_MODE_RGBA])
-			
-			self._comboMode = self.find_widget("comboMode")
-			self._comboMode.set_model(self._storeMode)
-			cell = Gtk.CellRendererText()
-			self._comboMode.pack_start(cell, True)
-			self._comboMode.add_attribute(cell, "text", 0)
-			self._comboMode.set_active(3)
-			
-			# anti-alias
-			
-			self._storeAntialias = Gtk.ListStore(str, int)	# label, factor
-			self._storeAntialias.append(["Off", 0])
-			self._storeAntialias.append(["1x", 1])
-			self._storeAntialias.append(["2x", 2])
-			self._storeAntialias.append(["4x", 4])
-			self._storeAntialias.append(["8x", 8])
-			
-			self._comboAntialias = self.find_widget("comboAntialias")
-			self._comboAntialias.set_model(self._storeAntialias)
-			cell = Gtk.CellRendererText()
-			self._comboAntialias.pack_start(cell, True)
-			self._comboAntialias.add_attribute(cell, "text", 0)
-			self._comboAntialias.set_active(3)
-			
-			# resolution
-			
-			self._spinResolution = self.find_widget("spinResolution")
-			self._spinResolution.set_value(Environment().screen_dpi)
-			
-		return self._dialog
-	
+    """
+    Render the document to an image
+    """
+
+    filename = find_resource("ui/build_image_dialog.ui")
+    _dialog = None
+    _generator = ImageToolGenerator()
+
+    def run(self):
+        dialog = self._getDialog()
+
+        if dialog.run() == 1:
+
+            if self.find_widget("radioPNG").get_active():
+                self._generator.format = ImageToolGenerator.FORMAT_PNG
+                self._generator.pngMode = self._storeMode[self._comboMode.get_active()][1]
+            elif self.find_widget("radioJPEG").get_active():
+                self._generator.format = ImageToolGenerator.FORMAT_JPEG
+            elif self.find_widget("radioGIF").get_active():
+                self._generator.format = ImageToolGenerator.FORMAT_GIF
+
+            self._generator.open = True
+            self._generator.resolution = self._spinResolution.get_value_as_int()
+            self._generator.antialiasFactor = self._storeAntialias[self._comboAntialias.get_active()][1]
+            self._generator.render_box = self.find_widget("radioBox").get_active()
+
+            generate = True
+        else:
+            generate = False
+
+        dialog.hide()
+
+        if generate:
+            return self._generator.generate()
+        else:
+            return None
+
+    def _getDialog(self):
+        if not self._dialog:
+            self._dialog = self.find_widget("dialogRenderImage")
+            self.find_widget("table").set_col_spacing(0, 20)
+
+            # PNG mode
+
+            self._storeMode = Gtk.ListStore(str, int)    # label, mode constant
+            self._storeMode.append(["Monochrome", ImageToolGenerator.PNG_MODE_MONOCHROME])
+            self._storeMode.append(["Grayscale", ImageToolGenerator.PNG_MODE_GRAYSCALE])
+            self._storeMode.append(["RGB", ImageToolGenerator.PNG_MODE_RGB])
+            self._storeMode.append(["RGBA", ImageToolGenerator.PNG_MODE_RGBA])
+
+            self._comboMode = self.find_widget("comboMode")
+            self._comboMode.set_model(self._storeMode)
+            cell = Gtk.CellRendererText()
+            self._comboMode.pack_start(cell, True)
+            self._comboMode.add_attribute(cell, "text", 0)
+            self._comboMode.set_active(3)
+
+            # anti-alias
+
+            self._storeAntialias = Gtk.ListStore(str, int)    # label, factor
+            self._storeAntialias.append(["Off", 0])
+            self._storeAntialias.append(["1x", 1])
+            self._storeAntialias.append(["2x", 2])
+            self._storeAntialias.append(["4x", 4])
+            self._storeAntialias.append(["8x", 8])
+
+            self._comboAntialias = self.find_widget("comboAntialias")
+            self._comboAntialias.set_model(self._storeAntialias)
+            cell = Gtk.CellRendererText()
+            self._comboAntialias.pack_start(cell, True)
+            self._comboAntialias.add_attribute(cell, "text", 0)
+            self._comboAntialias.set_active(3)
+
+            # resolution
+
+            self._spinResolution = self.find_widget("spinResolution")
+            self._spinResolution.set_value(Environment().screen_dpi)
+
+        return self._dialog
+
 
 class SaveAsTemplateDialog(GladeInterface):
-	filename = find_resource("ui/save_as_template_dialog.ui")
-	_dialog = None
-	
-	def get_dialog(self):
-		self._folder = Preferences().TEMPLATE_DIR
-		
-		if self._dialog is None:
-			self._dialog = self.find_widget("dialogSaveAsTemplate")
-			self._entry_name = self.find_widget("entryName")
-			self._label = self.find_widget("labelLocation")
-			self._button_okay = self.find_widget("buttonOkay")
-			self._icon = self.find_widget("imageIcon")
-			
-			self.connect_signals({ "on_entryName_changed" : self._on_name_changed })
-			
-		return self._dialog
-	
-	def _escape_name(self, name):
-		return name.replace(" ", "_")
-	
-	def _on_name_changed(self, entry):
-		self._validate()
-	
-	def _validate(self):
-		name = self._entry_name.get_text()
-		valid = True
-		
-		if len(name) == 0:
-			self._label.set_markup("A name is required.")
-			self._icon.set_from_stock(Gtk.STOCK_DIALOG_ERROR, Gtk.IconSize.MENU)
-			self._icon.show()
-			
-			valid = False
-		else:			
-			filename = "%s/%s.template" % (self._folder, self._escape_name(name))
-			if File(filename).exists:
-				self._label.set_markup("The file <tt>%s</tt> already exists." % filename)
-				self._icon.set_from_stock(Gtk.STOCK_DIALOG_ERROR, Gtk.IconSize.MENU)
-				self._icon.show()
-				
-				valid = False
-			else:
-				self._label.set_markup("The file will be saved as <tt>%s</tt>." % filename)
-				self._icon.set_from_stock(Gtk.STOCK_DIALOG_INFO, Gtk.IconSize.MENU)
-				self._icon.show()
-		
-		self._button_okay.set_sensitive(valid)
-	
-	def run(self):
-		"""
-		@param template_location: the folder in which the template will be saved
-		"""
-		dialog = self.get_dialog()
-		
-		self._entry_name.set_text("")
-		self._validate()
-		
-		if dialog.run() == 1:
-			confirmed = True
-		else:
-			confirmed = False
-			
-		dialog.hide()
-		
-		if confirmed:
-			return File("%s/%s.template" % (self._folder, self._escape_name(self._entry_name.get_text())))
-		else:
-			return None
-	
+    filename = find_resource("ui/save_as_template_dialog.ui")
+    _dialog = None
+
+    def get_dialog(self):
+        self._folder = Preferences().TEMPLATE_DIR
+
+        if self._dialog is None:
+            self._dialog = self.find_widget("dialogSaveAsTemplate")
+            self._entry_name = self.find_widget("entryName")
+            self._label = self.find_widget("labelLocation")
+            self._button_okay = self.find_widget("buttonOkay")
+            self._icon = self.find_widget("imageIcon")
+
+            self.connect_signals({ "on_entryName_changed" : self._on_name_changed })
+
+        return self._dialog
+
+    def _escape_name(self, name):
+        return name.replace(" ", "_")
+
+    def _on_name_changed(self, entry):
+        self._validate()
+
+    def _validate(self):
+        name = self._entry_name.get_text()
+        valid = True
+
+        if len(name) == 0:
+            self._label.set_markup("A name is required.")
+            self._icon.set_from_stock(Gtk.STOCK_DIALOG_ERROR, Gtk.IconSize.MENU)
+            self._icon.show()
+
+            valid = False
+        else:
+            filename = "%s/%s.template" % (self._folder, self._escape_name(name))
+            if File(filename).exists:
+                self._label.set_markup("The file <tt>%s</tt> already exists." % filename)
+                self._icon.set_from_stock(Gtk.STOCK_DIALOG_ERROR, Gtk.IconSize.MENU)
+                self._icon.show()
+
+                valid = False
+            else:
+                self._label.set_markup("The file will be saved as <tt>%s</tt>." % filename)
+                self._icon.set_from_stock(Gtk.STOCK_DIALOG_INFO, Gtk.IconSize.MENU)
+                self._icon.show()
+
+        self._button_okay.set_sensitive(valid)
+
+    def run(self):
+        """
+        @param template_location: the folder in which the template will be saved
+        """
+        dialog = self.get_dialog()
+
+        self._entry_name.set_text("")
+        self._validate()
+
+        if dialog.run() == 1:
+            confirmed = True
+        else:
+            confirmed = False
+
+        dialog.hide()
+
+        if confirmed:
+            return File("%s/%s.template" % (self._folder, self._escape_name(self._entry_name.get_text())))
+        else:
+            return None
+
diff --git a/latex/latex/editor.py b/latex/latex/editor.py
index 7dd622b..ad124ac 100644
--- a/latex/latex/editor.py
+++ b/latex/latex/editor.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -48,374 +48,374 @@ from ..preferences import Preferences
 
 
 class LaTeXEditor(Editor, IIssueHandler):
-	
-	_log = getLogger("LaTeXEditor")
-	
-	#extensions = [".tex"]
-	extensions = Preferences().get("latex-extensions").split(",")
-	
-	dnd_extensions = [".png", ".pdf", ".bib", ".tex"]
-	
-	@property
-	def completion_handlers(self):
-		self.__latex_completion_handler = LaTeXCompletionHandler()
-		
-		return [ self.__latex_completion_handler ]
-	
-	def init(self, file, context):
-		"""
-		@param file: base.File
-		@param context: base.WindowContext
-		"""
-		self._log.debug("init(%s)" % file)
-		
-		self._file = file
-		self._context = context
-		
-		self._preferences = Preferences()
-		self._preferences.connect("preferences-changed", self._on_preferences_changed)
-
-		self.register_marker_type("latex-error", self._preferences.get("error-background-color"))
-		self.register_marker_type("latex-warning", self._preferences.get("warning-background-color"))
-		
-		self._issue_view = context.find_view(self, "IssueView")
-		self._outline_view = context.find_view(self, "LaTeXOutlineView")
-
-		self._parser = LaTeXParser()
-		self._outline_generator = LaTeXOutlineGenerator()
-		self._validator = LaTeXValidator()
-		self._document = None
-		
-		self._document_dirty = True
-		
-		# if the document is no master we display an info message on the packages to
-		# include - _ensured_packages holds the already mentioned packages to not
-		# annoy the user
-		self._ensured_packages = []
-		
-		#
-		# initially parse
-		#
-		self._change_reference = self.initial_timestamp
-		
-		self.__parse()
-		self.__update_neighbors()
-	
-	def _on_preferences_changed(self, prefs, key, new_value):
-		if key in ["outline-show-labels", "outline-show-tables", "outline-show-graphics"]:
-			# regenerate outline model
-			if self._document_is_master:
-				self._outline = self._outline_generator.generate(self._document, self)
-				self._outline_view.set_outline(self._outline)
-			else:
-				# FIXME: self._document contains the full model of child and master
-				# so we may not use it for regenerating the outline here
-				self.__parse()
-		elif key == "show-latex-toolbar":
-			show_toolbar = self._preferences.get_bool("show-latex-toolbar")
-			if show_toolbar:
-				self._window_context._window_decorator._toolbar.show()
-			else:
-				self._window_context._window_decorator._toolbar.hide()
-	
-	def drag_drop_received(self, files):
-		# see base.Editor.drag_drop_received
-		
-		# TODO: we need to insert the source at the drop location - so pass it here
-		
-		self._log.debug("drag_drop: %s" % files)
-		
-#		if len(files) == 1:
-#			file = files[0]
-#			self._log.debug("Got one file: %s, extension: %s" % (file.path, file.extension))
-#			if file.extension == ".png":
-#				self._log.debug("PNG image - including...")
-#				source = "\\includegraphics{%s}" % file.path
-#				self.insert(source)
-	
-	def insert(self, source):
-		# see base.Editor.insert
-		
-		if type(source) is LaTeXSource:
-			if source.packages and len(source.packages) > 0:
-				self.ensure_packages(source.packages)
-			
-			Editor.insert(self, source.source)
-		else:
-			Editor.insert(self, source)
-	
-	POSITION_PACKAGES, POSITION_BIBLIOGRAPHY = 1, 2
-	
-	def insert_at_position(self, source, position):
-		"""
-		Insert source at a certain position in the LaTeX document:
-		
-		 * POSITION_PACKAGES: after the last \usepackage statement
-		 * POSITION_BIBLIOGRAPHY: before \end{document}
-		
-		@param source: a LaTeXSource object
-		@param position: POSITION_PACKAGES | POSITION_BIBLIOGRAPHY
-		"""
-		
-		if position == self.POSITION_BIBLIOGRAPHY:
-			offset = self._document.end_of_document
-			Editor.insert_at_offset(self, offset, source, True)
-		elif position == self.POSITION_PACKAGES:
-			offset = self._document.end_of_packages
-			Editor.insert_at_offset(self, offset, source, False)
-		else:
-			raise NotImplementedError
-	
-	def ensure_packages(self, packages):
-		"""
-		Ensure that certain packages are included
-		
-		@param packages: a list of package names
-		"""
-		self.__parse()	# ensure up-to-date document model
-		
-		if not self._document_is_master:
-			self._log.debug("ensure_packages: document is not a master")
-			
-			# find the packages that haven't already been mentioned
-			info_packages = [p for p in packages if not p in self._ensured_packages]
-			
-			if len(info_packages) > 0:
-				# generate markup
-				li_tags = "\n".join([" â <tt>%s</tt>" % p for p in info_packages])
-				
-				from ..util import open_info
-				open_info("LaTeX Package Required", 
-						"Please make sure that the following packages are included in the master document per <tt>\\usepackage</tt>: \n\n%s" % li_tags)
-				
-				# extend the already mentioned packages
-				self._ensured_packages.extend(info_packages)
-			
-			return
-		
-		# find missing packages
-		present_package_names = [p.value for p in self._outline.packages]
-		package_names = [p for p in packages if not p in present_package_names]
-		
-		# insert the necessary \usepackage commands
-		if len(package_names) > 0:
-			source = "\n" + "\n".join(["\\usepackage{%s}" % n for n in package_names])
-			self.insert_at_position(source, self.POSITION_PACKAGES)
-	
-	def on_save(self):
-		"""
-		The file has been saved
-		
-		Update models
-		"""
-		
-#		from multiprocessing import Process
-#		
-#		p_parse = Process(target=self.__parse)
-#		p_parse.start()
-		
-		self.__parse()
-		
-	def __update_neighbors(self):
-		"""
-		Find all files in the working directory that are relevant for LaTeX, e.g.
-		other *.tex files or images.
-		"""
-		
-		# TODO: this is only needed to feed the LaTeXCompletionHandler. So maybe it should
-		# know the edited file and the Editor should call an update() method of the handler
-		# when the file is saved.
-		
-		tex_files = self._file.find_neighbors(".tex")
-		bib_files = self._file.find_neighbors(".bib")
-		
-		graphic_files = []
-		for extension in [".ps", ".pdf", ".png", ".jpg", ".eps"]:
-			graphic_files.extend(self._file.find_neighbors(extension))
-		
-		self.__latex_completion_handler.set_neighbors(tex_files, bib_files, graphic_files)
-	
-	# verbose
-	def __parse(self):
-		"""
-		Ensure that the document model is up-to-date
-		"""
-		if self.content_changed(self._change_reference):
-			# content has changed so document model may be dirty
-			self._change_reference = self.current_timestamp
-			
-			self._log.debug("Parsing document...")
-			
-			# reset highlight
-			self.remove_markers("latex-error")
-			self.remove_markers("latex-warning")
-			
-			# reset issues
-			self._issue_view.clear()
-			
-			if BENCHMARK: t = time.clock()
-			
-			# parse document
-			if self._document != None:
-				self._document.destroy()
-				del self._document
-			self._document = self._parser.parse(self.content, self._file, self)
-			
-			if BENCHMARK: self._log.info("LaTeXParser.parse: %f" % (time.clock() - t))
-			
-			# create a copy that won't be expanded (e.g. for spell check)
-			#self._local_document = deepcopy(self._document)
-			
-			self._log.debug("Parsed %s bytes of content" % len(self.content))
-			
-			# FIXME: the LaTeXChooseMasterAction enabled state has to be updated on tab change, too!
-			
-			if self._document.is_master:
-				
-				self._context.set_action_enabled("LaTeXChooseMasterAction", False)
-				self._document_is_master = True
-				
-				# expand child documents
-				expander = LaTeXReferenceExpander()
-				expander.expand(self._document, self._file, self, self.charset)
-				
-				# generate outline from the expanded model
-				self._outline = self._outline_generator.generate(self._document, self)
-				
-				# pass to view
-				self._outline_view.set_outline(self._outline)
-
-				# validate
-				self._validator.validate(self._document, self._outline, self)
-			else:
-				self._log.debug("Document is not a master")
-				
-				self._context.set_action_enabled("LaTeXChooseMasterAction", True)
-				self._document_is_master = False
-				
-				# the outline used by the outline view has to be created only from the child model
-				# otherwise we see the outline of the master and get wrong offsets
-				self._outline = self._outline_generator.generate(self._document, self)
-				self._outline_view.set_outline(self._outline)
-				
-				# find master
-				master_file = self.__master_file
-				
-				if master_file is None:
-					return
-				
-				# parse master
-				master_content = open(master_file.path).read()
-				self._document = self._parser.parse(master_content, master_file, self)
-				
-				# expand its child documents
-				expander = LaTeXReferenceExpander()
-				expander.expand(self._document, master_file, self, self.charset)
-				
-				# create another outline of the expanded master model to make elements
-				# from the master available (labels, colors, BibTeX files etc.)
-				self._outline = self._outline_generator.generate(self._document, self)
-
-				# validate
-				self._validator.validate(self._document, self._outline, self)
-			
-			# pass outline to completion
-			self.__latex_completion_handler.set_outline(self._outline)
-			
-			# pass neighbor files to completion
-			self.__update_neighbors()
-			
-			self._log.debug("Parsing finished")
-			
-			#print self._document.xml
-	
-	@property
-	# returns(File)
-	def __master_file(self):
-		"""
-		Find the LaTeX master of this child
-		
-		@return: base.File
-		"""
-		# TODO: cache result
-		
-		property_file = PropertyFile(self._file)
-		try:
-			#return File(property_file["MasterFilename"])
-			
-			path = property_file["MasterFilename"]
-			# the property file may contain absolute and relative paths
-			# because we switched in 0.2rc2
-			if File.is_absolute(path):
-				self._log.debug("Path is absolute")
-				return File(path)
-			else:
-				self._log.debug("Path is relative")
-				return File.create_from_relative_path(path, self._file.dirname)
-		except KeyError:		# master filename not found
-			# ask user
-			master_filename = ChooseMasterDialog().run(self._file.dirname)
-			if master_filename:
-				# relativize the master filename
-				master_filename = File(master_filename).relativize(self._file.dirname, True)
-				
-				property_file["MasterFilename"] = master_filename
-				property_file.save()
-				return File(master_filename)
-			else:
-				# no master file chosen
-				return None
-	
-	def issue(self, issue):
-		# see IIssueHandler.issue
-		
-		local = (issue.file == self._file)
-		
-		self._issue_view.append_issue(issue, local)
-		
-		if issue.file == self._file:
-			if issue.severity == Issue.SEVERITY_ERROR:
-				self.create_marker("latex-error", issue.start, issue.end)
-			elif issue.severity == Issue.SEVERITY_WARNING:
-				self.create_marker("latex-warning", issue.start, issue.end)
-	
-	def on_cursor_moved(self, offset):
-		"""
-		The cursor has moved
-		"""
-		if self._preferences.get_bool("outline-connect-to-editor"):
-			self._outline_view.select_path_by_offset(offset)
-		
-	@property
-	def file(self):
-		# overrides Editor.file
-		
-		# we may not call self._document.is_master because _document is always
-		# replaced by the master model
-		if self._document_is_master:
-			return self._file
-		else:
-			return self.__master_file
-	
-	@property
-	def edited_file(self):
-		"""
-		Always returns the really edited file instead of the master
-		
-		This is called by the outline view to identify foreign nodes
-		"""
-		return self._file
-
-	def destroy(self):
-		# unreference the window context
-		del self._context
-		
-		# destroy the cached document
-		self._document.destroy()
-		del self._document
-
-		Editor.destroy(self)
-		
-	def __del__(self):
-		self._log.debug("Properly destroyed %s" % self)
-	
+
+    _log = getLogger("LaTeXEditor")
+
+    #extensions = [".tex"]
+    extensions = Preferences().get("latex-extensions").split(",")
+
+    dnd_extensions = [".png", ".pdf", ".bib", ".tex"]
+
+    @property
+    def completion_handlers(self):
+        self.__latex_completion_handler = LaTeXCompletionHandler()
+
+        return [ self.__latex_completion_handler ]
+
+    def init(self, file, context):
+        """
+        @param file: base.File
+        @param context: base.WindowContext
+        """
+        self._log.debug("init(%s)" % file)
+
+        self._file = file
+        self._context = context
+
+        self._preferences = Preferences()
+        self._preferences.connect("preferences-changed", self._on_preferences_changed)
+
+        self.register_marker_type("latex-error", self._preferences.get("error-background-color"))
+        self.register_marker_type("latex-warning", self._preferences.get("warning-background-color"))
+
+        self._issue_view = context.find_view(self, "IssueView")
+        self._outline_view = context.find_view(self, "LaTeXOutlineView")
+
+        self._parser = LaTeXParser()
+        self._outline_generator = LaTeXOutlineGenerator()
+        self._validator = LaTeXValidator()
+        self._document = None
+
+        self._document_dirty = True
+
+        # if the document is no master we display an info message on the packages to
+        # include - _ensured_packages holds the already mentioned packages to not
+        # annoy the user
+        self._ensured_packages = []
+
+        #
+        # initially parse
+        #
+        self._change_reference = self.initial_timestamp
+
+        self.__parse()
+        self.__update_neighbors()
+
+    def _on_preferences_changed(self, prefs, key, new_value):
+        if key in ["outline-show-labels", "outline-show-tables", "outline-show-graphics"]:
+            # regenerate outline model
+            if self._document_is_master:
+                self._outline = self._outline_generator.generate(self._document, self)
+                self._outline_view.set_outline(self._outline)
+            else:
+                # FIXME: self._document contains the full model of child and master
+                # so we may not use it for regenerating the outline here
+                self.__parse()
+        elif key == "show-latex-toolbar":
+            show_toolbar = self._preferences.get_bool("show-latex-toolbar")
+            if show_toolbar:
+                self._window_context._window_decorator._toolbar.show()
+            else:
+                self._window_context._window_decorator._toolbar.hide()
+
+    def drag_drop_received(self, files):
+        # see base.Editor.drag_drop_received
+
+        # TODO: we need to insert the source at the drop location - so pass it here
+
+        self._log.debug("drag_drop: %s" % files)
+
+#        if len(files) == 1:
+#            file = files[0]
+#            self._log.debug("Got one file: %s, extension: %s" % (file.path, file.extension))
+#            if file.extension == ".png":
+#                self._log.debug("PNG image - including...")
+#                source = "\\includegraphics{%s}" % file.path
+#                self.insert(source)
+
+    def insert(self, source):
+        # see base.Editor.insert
+
+        if type(source) is LaTeXSource:
+            if source.packages and len(source.packages) > 0:
+                self.ensure_packages(source.packages)
+
+            Editor.insert(self, source.source)
+        else:
+            Editor.insert(self, source)
+
+    POSITION_PACKAGES, POSITION_BIBLIOGRAPHY = 1, 2
+
+    def insert_at_position(self, source, position):
+        """
+        Insert source at a certain position in the LaTeX document:
+
+         * POSITION_PACKAGES: after the last \usepackage statement
+         * POSITION_BIBLIOGRAPHY: before \end{document}
+
+        @param source: a LaTeXSource object
+        @param position: POSITION_PACKAGES | POSITION_BIBLIOGRAPHY
+        """
+
+        if position == self.POSITION_BIBLIOGRAPHY:
+            offset = self._document.end_of_document
+            Editor.insert_at_offset(self, offset, source, True)
+        elif position == self.POSITION_PACKAGES:
+            offset = self._document.end_of_packages
+            Editor.insert_at_offset(self, offset, source, False)
+        else:
+            raise NotImplementedError
+
+    def ensure_packages(self, packages):
+        """
+        Ensure that certain packages are included
+
+        @param packages: a list of package names
+        """
+        self.__parse()    # ensure up-to-date document model
+
+        if not self._document_is_master:
+            self._log.debug("ensure_packages: document is not a master")
+
+            # find the packages that haven't already been mentioned
+            info_packages = [p for p in packages if not p in self._ensured_packages]
+
+            if len(info_packages) > 0:
+                # generate markup
+                li_tags = "\n".join([" â <tt>%s</tt>" % p for p in info_packages])
+
+                from ..util import open_info
+                open_info("LaTeX Package Required",
+                        "Please make sure that the following packages are included in the master document per <tt>\\usepackage</tt>: \n\n%s" % li_tags)
+
+                # extend the already mentioned packages
+                self._ensured_packages.extend(info_packages)
+
+            return
+
+        # find missing packages
+        present_package_names = [p.value for p in self._outline.packages]
+        package_names = [p for p in packages if not p in present_package_names]
+
+        # insert the necessary \usepackage commands
+        if len(package_names) > 0:
+            source = "\n" + "\n".join(["\\usepackage{%s}" % n for n in package_names])
+            self.insert_at_position(source, self.POSITION_PACKAGES)
+
+    def on_save(self):
+        """
+        The file has been saved
+
+        Update models
+        """
+
+#        from multiprocessing import Process
+#
+#        p_parse = Process(target=self.__parse)
+#        p_parse.start()
+
+        self.__parse()
+
+    def __update_neighbors(self):
+        """
+        Find all files in the working directory that are relevant for LaTeX, e.g.
+        other *.tex files or images.
+        """
+
+        # TODO: this is only needed to feed the LaTeXCompletionHandler. So maybe it should
+        # know the edited file and the Editor should call an update() method of the handler
+        # when the file is saved.
+
+        tex_files = self._file.find_neighbors(".tex")
+        bib_files = self._file.find_neighbors(".bib")
+
+        graphic_files = []
+        for extension in [".ps", ".pdf", ".png", ".jpg", ".eps"]:
+            graphic_files.extend(self._file.find_neighbors(extension))
+
+        self.__latex_completion_handler.set_neighbors(tex_files, bib_files, graphic_files)
+
+    # verbose
+    def __parse(self):
+        """
+        Ensure that the document model is up-to-date
+        """
+        if self.content_changed(self._change_reference):
+            # content has changed so document model may be dirty
+            self._change_reference = self.current_timestamp
+
+            self._log.debug("Parsing document...")
+
+            # reset highlight
+            self.remove_markers("latex-error")
+            self.remove_markers("latex-warning")
+
+            # reset issues
+            self._issue_view.clear()
+
+            if BENCHMARK: t = time.clock()
+
+            # parse document
+            if self._document != None:
+                self._document.destroy()
+                del self._document
+            self._document = self._parser.parse(self.content, self._file, self)
+
+            if BENCHMARK: self._log.info("LaTeXParser.parse: %f" % (time.clock() - t))
+
+            # create a copy that won't be expanded (e.g. for spell check)
+            #self._local_document = deepcopy(self._document)
+
+            self._log.debug("Parsed %s bytes of content" % len(self.content))
+
+            # FIXME: the LaTeXChooseMasterAction enabled state has to be updated on tab change, too!
+
+            if self._document.is_master:
+
+                self._context.set_action_enabled("LaTeXChooseMasterAction", False)
+                self._document_is_master = True
+
+                # expand child documents
+                expander = LaTeXReferenceExpander()
+                expander.expand(self._document, self._file, self, self.charset)
+
+                # generate outline from the expanded model
+                self._outline = self._outline_generator.generate(self._document, self)
+
+                # pass to view
+                self._outline_view.set_outline(self._outline)
+
+                # validate
+                self._validator.validate(self._document, self._outline, self)
+            else:
+                self._log.debug("Document is not a master")
+
+                self._context.set_action_enabled("LaTeXChooseMasterAction", True)
+                self._document_is_master = False
+
+                # the outline used by the outline view has to be created only from the child model
+                # otherwise we see the outline of the master and get wrong offsets
+                self._outline = self._outline_generator.generate(self._document, self)
+                self._outline_view.set_outline(self._outline)
+
+                # find master
+                master_file = self.__master_file
+
+                if master_file is None:
+                    return
+
+                # parse master
+                master_content = open(master_file.path).read()
+                self._document = self._parser.parse(master_content, master_file, self)
+
+                # expand its child documents
+                expander = LaTeXReferenceExpander()
+                expander.expand(self._document, master_file, self, self.charset)
+
+                # create another outline of the expanded master model to make elements
+                # from the master available (labels, colors, BibTeX files etc.)
+                self._outline = self._outline_generator.generate(self._document, self)
+
+                # validate
+                self._validator.validate(self._document, self._outline, self)
+
+            # pass outline to completion
+            self.__latex_completion_handler.set_outline(self._outline)
+
+            # pass neighbor files to completion
+            self.__update_neighbors()
+
+            self._log.debug("Parsing finished")
+
+            #print self._document.xml
+
+    @property
+    # returns(File)
+    def __master_file(self):
+        """
+        Find the LaTeX master of this child
+
+        @return: base.File
+        """
+        # TODO: cache result
+
+        property_file = PropertyFile(self._file)
+        try:
+            #return File(property_file["MasterFilename"])
+
+            path = property_file["MasterFilename"]
+            # the property file may contain absolute and relative paths
+            # because we switched in 0.2rc2
+            if File.is_absolute(path):
+                self._log.debug("Path is absolute")
+                return File(path)
+            else:
+                self._log.debug("Path is relative")
+                return File.create_from_relative_path(path, self._file.dirname)
+        except KeyError:        # master filename not found
+            # ask user
+            master_filename = ChooseMasterDialog().run(self._file.dirname)
+            if master_filename:
+                # relativize the master filename
+                master_filename = File(master_filename).relativize(self._file.dirname, True)
+
+                property_file["MasterFilename"] = master_filename
+                property_file.save()
+                return File(master_filename)
+            else:
+                # no master file chosen
+                return None
+
+    def issue(self, issue):
+        # see IIssueHandler.issue
+
+        local = (issue.file == self._file)
+
+        self._issue_view.append_issue(issue, local)
+
+        if issue.file == self._file:
+            if issue.severity == Issue.SEVERITY_ERROR:
+                self.create_marker("latex-error", issue.start, issue.end)
+            elif issue.severity == Issue.SEVERITY_WARNING:
+                self.create_marker("latex-warning", issue.start, issue.end)
+
+    def on_cursor_moved(self, offset):
+        """
+        The cursor has moved
+        """
+        if self._preferences.get_bool("outline-connect-to-editor"):
+            self._outline_view.select_path_by_offset(offset)
+
+    @property
+    def file(self):
+        # overrides Editor.file
+
+        # we may not call self._document.is_master because _document is always
+        # replaced by the master model
+        if self._document_is_master:
+            return self._file
+        else:
+            return self.__master_file
+
+    @property
+    def edited_file(self):
+        """
+        Always returns the really edited file instead of the master
+
+        This is called by the outline view to identify foreign nodes
+        """
+        return self._file
+
+    def destroy(self):
+        # unreference the window context
+        del self._context
+
+        # destroy the cached document
+        self._document.destroy()
+        del self._document
+
+        Editor.destroy(self)
+
+    def __del__(self):
+        self._log.debug("Properly destroyed %s" % self)
+
diff --git a/latex/latex/environment.py b/latex/latex/environment.py
index 4742464..7b987a8 100644
--- a/latex/latex/environment.py
+++ b/latex/latex/environment.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -32,149 +32,149 @@ from logging import getLogger
 
 
 class CnfFile(dict):
-	"""
-	This parses a .cnf file and provides its contents as a dictionary
-	"""
-	def __init__(self, filename):
-		"""
-		@raise IOError: if file is not found
-		"""
-		for line in open(filename).readlines():
-			if not line.startswith("%"):
-				try:
-					key, value = line.split("=")
-					self[key.strip()] = value.strip()
-				except:
-					pass
+    """
+    This parses a .cnf file and provides its contents as a dictionary
+    """
+    def __init__(self, filename):
+        """
+        @raise IOError: if file is not found
+        """
+        for line in open(filename).readlines():
+            if not line.startswith("%"):
+                try:
+                    key, value = line.split("=")
+                    self[key.strip()] = value.strip()
+                except:
+                    pass
 
 
 _INPUT_ENCODINGS = {
-	"utf8"	   : "UTF-8 (Unicode)",
-	"ascii"	   : "US-ASCII",
-	"next"     : "ASCII (NeXT)",
-	"ansinew"  : "ASCII (Windows)",
-	"applemac" : "ASCII (Apple)",
-	"macce"    : "MacCE (Apple Central European)",
-	"latin1"   : "Latin-1 (Western European)",
-	"latin2"   : "Latin-2 (Danubian European)",
-	"latin3"   : "Latin-3 (South European)",
-	"latin4"   : "Latin-4 (North European)",
-	"latin5"   : "Latin-5 (Turkish)",
-	"latin6"   : "Latin-6 (Nordic)",
-	"latin7"   : "Latin-7 (Baltic)",
-	"latin8"   : "Latin-8 (Celtic)",
-	"latin9"   : "Latin-9 (extended Latin-1)",
-	"latin10"  : "Latin-10 (South-Eastern European)",
-	"cp1250"   : "CP1250 (Windows Central European)",
-	"cp1252"   : "CP1252 (Windows Western European)",
-	"cp1257"   : "CP1257 (Windows Baltic)",
-	"cp437"    : "CP437 (DOS US with Î)",
-	"cp437de"  : "CP437 (DOS US with Ã)",
-	"cp850"    : "CP850 (DOS Latin-1)",
-	"cp852"    : "CP852 (DOS Central European)",
-	"cp858"    : "CP858 (DOS Western European)",
-	"cp865"    : "CP865 (DOS Nordic)",
-	"decmulti" : "DEC Multinational Character Set"
+    "utf8"       : "UTF-8 (Unicode)",
+    "ascii"       : "US-ASCII",
+    "next"     : "ASCII (NeXT)",
+    "ansinew"  : "ASCII (Windows)",
+    "applemac" : "ASCII (Apple)",
+    "macce"    : "MacCE (Apple Central European)",
+    "latin1"   : "Latin-1 (Western European)",
+    "latin2"   : "Latin-2 (Danubian European)",
+    "latin3"   : "Latin-3 (South European)",
+    "latin4"   : "Latin-4 (North European)",
+    "latin5"   : "Latin-5 (Turkish)",
+    "latin6"   : "Latin-6 (Nordic)",
+    "latin7"   : "Latin-7 (Baltic)",
+    "latin8"   : "Latin-8 (Celtic)",
+    "latin9"   : "Latin-9 (extended Latin-1)",
+    "latin10"  : "Latin-10 (South-Eastern European)",
+    "cp1250"   : "CP1250 (Windows Central European)",
+    "cp1252"   : "CP1252 (Windows Western European)",
+    "cp1257"   : "CP1257 (Windows Baltic)",
+    "cp437"    : "CP437 (DOS US with Î)",
+    "cp437de"  : "CP437 (DOS US with Ã)",
+    "cp850"    : "CP850 (DOS Latin-1)",
+    "cp852"    : "CP852 (DOS Central European)",
+    "cp858"    : "CP858 (DOS Western European)",
+    "cp865"    : "CP865 (DOS Nordic)",
+    "decmulti" : "DEC Multinational Character Set"
 }
 
 _BABEL_PACKAGES = {
-	"albanian"	 : "Albanian",
-	"afrikaans"	 : "Afrikaans", #dutch dialect
-	"austrian"	 : "Austrian",
-	"naustrian"	 : "Austrian (new spelling)",
-	"bahasai"  	 : "Bahasa Indonesia",
-	"bahasam"	 : "Bahasa Malaysia",
-	"basque" 	 : "Basque",
-	"breton"	 : "Breton",
-	"bulgarian"	 : "Bulgarian",
-	"catalan"	 : "Catalan",
-	"croatian"	 : "Croatian",
-	"czech"		 : "Czech",
-	"danish"	 : "Danish",
-	"dutch"		 : "Dutch",
-	"australian"	 : "English (AU)",
-	"canadian"	 : "English (CA)",
-	"newzealand"	 : "English (NZ)",
-	"UKenglish"	 : "English (UK)",
-	"english"	 : "English (US)",
-	"esperanto"	 : "Esperanto",
-	"estonian"	 : "Estonian",
-	"finnish"	 : "Finnish",
-	"frenchb"	 : "French",
-	"acadian"	 : "French (Acadian)",
-	"canadien"	 : "French (CA)",
-	"galician"	 : "Galician",
-	"germanb"	 : "German",
-	"ngermanb"	 : "German (new spelling)",
-	"greek"		 : "Greek",
-	"polutonikogreek" : "Greek (polytonic)",
-#	"athnum"	 : "Greek (Athens numbering)",
-	"hebrew"	 : "Hebrew",
-	"magyar"	 : "Hungarian",
-	"icelandic"	 : "Icelandic",
-	"interlingua"	 : "Interlingua",
-	"irish"		 : "Irish Gaelic",
-	"italian"	 : "Italian",
-	"latin"		 : "Latin",
-	"lsorbian"	 : "Lower Sorbian",
-	"norsk"		 : "Norwegian BokmÃl",
-	"nynorsk"	 : "Norwegian Nynorsk",
-	"polish"	 : "Polish",
-	"portuges"	 : "Portuguese (PT)",
-	"brazilian"	 : "Portuguese (BR)",
-	"romanian"	 : "Romanian",
-	"russianb"	 : "Russian",
-	"samin"		 : "North Sami",
-	"scottish"	 : "Scottish Gaelic",
-	"serbian"	 : "Serbian",
-	"slovak"	 : "Slovak",
-	"slovene"	 : "Slovene",
-	"spanish"	 : "Spanish",
-	"swedish"	 : "Swedish",
-	"turkish"	 : "Turkish",
-	"ukraineb"	 : "Ukraine",
-	"usorbian"	 : "Upper Sorbian",
-	"welsh"		 : "Welsh"
+    "albanian"     : "Albanian",
+    "afrikaans"     : "Afrikaans", #dutch dialect
+    "austrian"     : "Austrian",
+    "naustrian"     : "Austrian (new spelling)",
+    "bahasai"       : "Bahasa Indonesia",
+    "bahasam"     : "Bahasa Malaysia",
+    "basque"      : "Basque",
+    "breton"     : "Breton",
+    "bulgarian"     : "Bulgarian",
+    "catalan"     : "Catalan",
+    "croatian"     : "Croatian",
+    "czech"         : "Czech",
+    "danish"     : "Danish",
+    "dutch"         : "Dutch",
+    "australian"     : "English (AU)",
+    "canadian"     : "English (CA)",
+    "newzealand"     : "English (NZ)",
+    "UKenglish"     : "English (UK)",
+    "english"     : "English (US)",
+    "esperanto"     : "Esperanto",
+    "estonian"     : "Estonian",
+    "finnish"     : "Finnish",
+    "frenchb"     : "French",
+    "acadian"     : "French (Acadian)",
+    "canadien"     : "French (CA)",
+    "galician"     : "Galician",
+    "germanb"     : "German",
+    "ngermanb"     : "German (new spelling)",
+    "greek"         : "Greek",
+    "polutonikogreek" : "Greek (polytonic)",
+#    "athnum"     : "Greek (Athens numbering)",
+    "hebrew"     : "Hebrew",
+    "magyar"     : "Hungarian",
+    "icelandic"     : "Icelandic",
+    "interlingua"     : "Interlingua",
+    "irish"         : "Irish Gaelic",
+    "italian"     : "Italian",
+    "latin"         : "Latin",
+    "lsorbian"     : "Lower Sorbian",
+    "norsk"         : "Norwegian BokmÃl",
+    "nynorsk"     : "Norwegian Nynorsk",
+    "polish"     : "Polish",
+    "portuges"     : "Portuguese (PT)",
+    "brazilian"     : "Portuguese (BR)",
+    "romanian"     : "Romanian",
+    "russianb"     : "Russian",
+    "samin"         : "North Sami",
+    "scottish"     : "Scottish Gaelic",
+    "serbian"     : "Serbian",
+    "slovak"     : "Slovak",
+    "slovene"     : "Slovene",
+    "spanish"     : "Spanish",
+    "swedish"     : "Swedish",
+    "turkish"     : "Turkish",
+    "ukraineb"     : "Ukraine",
+    "usorbian"     : "Upper Sorbian",
+    "welsh"         : "Welsh"
 }
 
 _DOCUMENT_CLASSES = {
-	"abstbook"	: _("Book of abstracts"),
-	"article" 	: _("Article"),
-	"amsart"	: _("Article (AMS)"),
-	"amsbook"	: _("Book (AMS)"),
-	"amsdtx"	: _("AMS Documentation"),
-	"amsproc"	: _("Proceedings (AMS)"),
-	"report" 	: _("Report"),
-	"beamer" 	: _("Beamer slides"),
-	"beletter"	: _("Belgian letter"),
-	"book" 		: _("Book"),
-	"flashcard"	: _("Flashcard"),
-	"iagproc"	: _("Proceedings (IAG)"),
-	"letter" 	: _("Letter"),
-	"ltnews"	: _("LaTeX News"),
-	"ltxdoc"	: _("LaTeX Documentation"),
-	"ltxguide"	: _("LaTeX Guide"),
-	"proc"		: _("Proceedings"),
-	"scrartcl" 	: _("Article (KOMA-Script)"),
-	"scrreport"	: _("Report (KOMA-Script)"),
-	"scrbook" 	: _("Book (KOMA-Script)"),
-	"scrlettr" 	: _("Letter (KOMA-Script)"),
-	"scrlttr2" 	: _("Letter 2 (KOMA-Script)"),
-	"scrreprt"	: _("Report (KOMA-Script)"),
-	"slides"	: _("Slides")
+    "abstbook"    : _("Book of abstracts"),
+    "article"     : _("Article"),
+    "amsart"    : _("Article (AMS)"),
+    "amsbook"    : _("Book (AMS)"),
+    "amsdtx"    : _("AMS Documentation"),
+    "amsproc"    : _("Proceedings (AMS)"),
+    "report"     : _("Report"),
+    "beamer"     : _("Beamer slides"),
+    "beletter"    : _("Belgian letter"),
+    "book"         : _("Book"),
+    "flashcard"    : _("Flashcard"),
+    "iagproc"    : _("Proceedings (IAG)"),
+    "letter"     : _("Letter"),
+    "ltnews"    : _("LaTeX News"),
+    "ltxdoc"    : _("LaTeX Documentation"),
+    "ltxguide"    : _("LaTeX Guide"),
+    "proc"        : _("Proceedings"),
+    "scrartcl"     : _("Article (KOMA-Script)"),
+    "scrreport"    : _("Report (KOMA-Script)"),
+    "scrbook"     : _("Book (KOMA-Script)"),
+    "scrlettr"     : _("Letter (KOMA-Script)"),
+    "scrlttr2"     : _("Letter 2 (KOMA-Script)"),
+    "scrreprt"    : _("Report (KOMA-Script)"),
+    "slides"    : _("Slides")
 }
 
 
 class TeXResource(object):
-	def __init__(self, file, name, label):
-		"""
-		@param file: a File object
-		@param name: the identifier of this resource (e.g. 'ams' for 'ams.bib')
-		@param label: a descriptive label
-		"""
-		self.file = file
-		self.name = name
-		self.label = label
+    def __init__(self, file, name, label):
+        """
+        @param file: a File object
+        @param name: the identifier of this resource (e.g. 'ams' for 'ams.bib')
+        @param label: a descriptive label
+        """
+        self.file = file
+        self.name = name
+        self.label = label
 
 
 from os.path import expanduser
@@ -183,192 +183,192 @@ from ..base import File
 
 
 class Environment(object):
-	
-	_CONFIG_FILENAME = "/etc/texmf/texmf.cnf"
-	_DEFAULT_TEXMF_DIR = "/usr/share/texmf-texlive"
-	_DEFAULT_TEXMF_DIR_HOME = "~/texmf"
-	
-	"""
-	This encapsulates the user's LaTeX distribution and provides methods
-	for searching it
-	"""
-	
-	_log = getLogger("Environment")
-	
-	def __new__(cls):
-		if not '_instance' in cls.__dict__:
-			cls._instance = object.__new__(cls)
-		return cls._instance
-	
-	def __init__(self):
-		if not '_ready' in dir(self):
-			self._bibtex_styles = None
-			self._classes = None
-			self._language_definitions = None
-			self._input_encodings = None
-			self._screen_dpi = None
-			self._kpsewhich_checked = False
-			self._kpsewhich_installed = None
-			self._file_exists_cache = {}
-			
-			self._search_paths = []
-			default_search_paths = [self._DEFAULT_TEXMF_DIR, expanduser(self._DEFAULT_TEXMF_DIR_HOME)]
-			
-			try:
-				cnf_file = CnfFile(self._CONFIG_FILENAME)
-
-				path_found = False
-				
-				for key in ["TEXMFMAIN", "TEXMFDIST", "TEXMFHOME"]:
-					try:
-						self._search_paths.append(cnf_file[key])
-						path_found = True
-					except KeyError:
-						# config key not found
-						self._log.error("Key %s not found in %s" % (key, self._CONFIG_FILENAME))
-				
-				if not path_found:
-					self._log.error("No search paths found in %s, using default search paths %s" % (self._CONFIG_FILENAME, default_search_paths))
-					self._search_paths = default_search_paths
-				
-			except IOError:
-				# file _CONFIG_FILENAME not found - use default path
-				self._log.error("%s not found, using default search paths %s" % (self._CONFIG_FILENAME, default_search_paths))
-				self._search_paths = default_search_paths
-			
-			self._ready = True
-	
-	@property
-	def kpsewhich_installed(self):
-		"""
-		Return whether kpsewhich is installed
-		"""
-		if not self._kpsewhich_checked:
-			self._kpsewhich_installed = bool(system("kpsewhich --version $2>/dev/null") == 0)
-			self._kpsewhich_checked = True
-		return self._kpsewhich_installed
-	
-	def file_exists(self, filename):
-		"""
-		Uses kpsewhich to check if a TeX related file (.bst, .sty etc.) exists. The result
-		is cached to minimize 'kpsewhich' calls. 
-		"""
-		if not self.kpsewhich_installed:
-			return True
-		
-		try:
-			return self._file_exists_cache[filename]
-		except KeyError:
-			found = popen("kpsewhich %s" % filename).read().splitlines()
-			exists = bool(len(found))
-			self._file_exists_cache[filename] = exists
-			return exists
-	
-	@property
-	def bibtex_styles(self):
-		"""
-		Return the available .bst files
-		"""
-		if not self._bibtex_styles:
-			self._bibtex_styles = self._find_resources("", ".bst", {})
-		return self._bibtex_styles
-	
-	@property
-	def document_classes(self):
-		"""
-		Return the available document classes
-		"""
-		if not self._classes:
-			self._classes = self._find_resources("", ".cls", _DOCUMENT_CLASSES)
-		return self._classes
-	
-	@property
-	def language_definitions(self):
-		if not self._language_definitions:
-			self._language_definitions = self._find_resources("/tex/generic/babel/", ".ldf", _BABEL_PACKAGES)
-		return self._language_definitions
-	
-	@property
-	def input_encodings(self):
-		"""
-		Return a list of all available input encodings
-		"""
-		if not self._input_encodings:
-			self._input_encodings = self._find_resources("/tex/latex/base/", ".def", _INPUT_ENCODINGS)
-		return self._input_encodings
-	
-	def _find_resources(self, relative, extension, labels):
-		"""
-		Find TeX resources
-		
-		@param relative: a path relative to TEXMF... search path, e.g. '/tex/latex/base/'
-		@param extension: the file extension of the resources, e.g. '.bst'
-		@param labels: the dictionary to be searched for labels  
-		"""
-		resources = []
-		files = []
-		
-		for search_path in self._search_paths:
-			files += [File(f) for f in popen("find %s%s -name '*%s'" % (search_path, relative, extension)).readlines()]
-			
-		if len(files) > 0:
-			for file in files:
-				name = file.shortbasename
-				try:
-					label = labels[name]
-				except KeyError:
-					label = ""
-				resources.append(TeXResource(file, name, label))
-		else:
-			# no files found
-			self._log.error("No %s-files found in %s%s" % (extension, search_path, relative))
-			
-		for name, label in labels.iteritems():
-			found = False
-			for resource in resources:
-				if resource.name == name:
-					found = True
-			if not found:
-				resources.append(TeXResource(None, name, label))
-					
-		return resources
-	
-	@property
-	def screen_dpi(self):
-		if not self._screen_dpi:
-			screen = Gdk.Screen.get_default()
-			dpi_x = screen.width() / screen.width_mm() * 25.4
-			dpi_y = screen.height() / screen.height_mm() * 25.4
-			
-			self._screen_dpi = (dpi_x + dpi_y) / 2.0
-			
-		return self._screen_dpi
-	
-	@property
-	def username(self):
-		"""
-		Return user name derived from pwd entry
-		"""
-		record = getpwnam(getuser()) # get pwd entry
-		
-		self._log.debug("Found user pw entry: " + str(record))
-		
-		if len(record[4]):
-			return record[4].split(",")[0]
-		else:
-			return record[0].title()
-	
-	@property
-	def date_format(self):
-		"""
-		Return localized date format for use in strftime()
-		"""
-		return nl_langinfo(D_FMT)
-	
-	@property
-	def language_code(self):
-		"""
-		Return language code like 'de'
-		"""
-		return getdefaultlocale()[0]
+
+    _CONFIG_FILENAME = "/etc/texmf/texmf.cnf"
+    _DEFAULT_TEXMF_DIR = "/usr/share/texmf-texlive"
+    _DEFAULT_TEXMF_DIR_HOME = "~/texmf"
+
+    """
+    This encapsulates the user's LaTeX distribution and provides methods
+    for searching it
+    """
+
+    _log = getLogger("Environment")
+
+    def __new__(cls):
+        if not '_instance' in cls.__dict__:
+            cls._instance = object.__new__(cls)
+        return cls._instance
+
+    def __init__(self):
+        if not '_ready' in dir(self):
+            self._bibtex_styles = None
+            self._classes = None
+            self._language_definitions = None
+            self._input_encodings = None
+            self._screen_dpi = None
+            self._kpsewhich_checked = False
+            self._kpsewhich_installed = None
+            self._file_exists_cache = {}
+
+            self._search_paths = []
+            default_search_paths = [self._DEFAULT_TEXMF_DIR, expanduser(self._DEFAULT_TEXMF_DIR_HOME)]
+
+            try:
+                cnf_file = CnfFile(self._CONFIG_FILENAME)
+
+                path_found = False
+
+                for key in ["TEXMFMAIN", "TEXMFDIST", "TEXMFHOME"]:
+                    try:
+                        self._search_paths.append(cnf_file[key])
+                        path_found = True
+                    except KeyError:
+                        # config key not found
+                        self._log.error("Key %s not found in %s" % (key, self._CONFIG_FILENAME))
+
+                if not path_found:
+                    self._log.error("No search paths found in %s, using default search paths %s" % (self._CONFIG_FILENAME, default_search_paths))
+                    self._search_paths = default_search_paths
+
+            except IOError:
+                # file _CONFIG_FILENAME not found - use default path
+                self._log.error("%s not found, using default search paths %s" % (self._CONFIG_FILENAME, default_search_paths))
+                self._search_paths = default_search_paths
+
+            self._ready = True
+
+    @property
+    def kpsewhich_installed(self):
+        """
+        Return whether kpsewhich is installed
+        """
+        if not self._kpsewhich_checked:
+            self._kpsewhich_installed = bool(system("kpsewhich --version $2>/dev/null") == 0)
+            self._kpsewhich_checked = True
+        return self._kpsewhich_installed
+
+    def file_exists(self, filename):
+        """
+        Uses kpsewhich to check if a TeX related file (.bst, .sty etc.) exists. The result
+        is cached to minimize 'kpsewhich' calls.
+        """
+        if not self.kpsewhich_installed:
+            return True
+
+        try:
+            return self._file_exists_cache[filename]
+        except KeyError:
+            found = popen("kpsewhich %s" % filename).read().splitlines()
+            exists = bool(len(found))
+            self._file_exists_cache[filename] = exists
+            return exists
+
+    @property
+    def bibtex_styles(self):
+        """
+        Return the available .bst files
+        """
+        if not self._bibtex_styles:
+            self._bibtex_styles = self._find_resources("", ".bst", {})
+        return self._bibtex_styles
+
+    @property
+    def document_classes(self):
+        """
+        Return the available document classes
+        """
+        if not self._classes:
+            self._classes = self._find_resources("", ".cls", _DOCUMENT_CLASSES)
+        return self._classes
+
+    @property
+    def language_definitions(self):
+        if not self._language_definitions:
+            self._language_definitions = self._find_resources("/tex/generic/babel/", ".ldf", _BABEL_PACKAGES)
+        return self._language_definitions
+
+    @property
+    def input_encodings(self):
+        """
+        Return a list of all available input encodings
+        """
+        if not self._input_encodings:
+            self._input_encodings = self._find_resources("/tex/latex/base/", ".def", _INPUT_ENCODINGS)
+        return self._input_encodings
+
+    def _find_resources(self, relative, extension, labels):
+        """
+        Find TeX resources
+
+        @param relative: a path relative to TEXMF... search path, e.g. '/tex/latex/base/'
+        @param extension: the file extension of the resources, e.g. '.bst'
+        @param labels: the dictionary to be searched for labels
+        """
+        resources = []
+        files = []
+
+        for search_path in self._search_paths:
+            files += [File(f) for f in popen("find %s%s -name '*%s'" % (search_path, relative, extension)).readlines()]
+
+        if len(files) > 0:
+            for file in files:
+                name = file.shortbasename
+                try:
+                    label = labels[name]
+                except KeyError:
+                    label = ""
+                resources.append(TeXResource(file, name, label))
+        else:
+            # no files found
+            self._log.error("No %s-files found in %s%s" % (extension, search_path, relative))
+
+        for name, label in labels.iteritems():
+            found = False
+            for resource in resources:
+                if resource.name == name:
+                    found = True
+            if not found:
+                resources.append(TeXResource(None, name, label))
+
+        return resources
+
+    @property
+    def screen_dpi(self):
+        if not self._screen_dpi:
+            screen = Gdk.Screen.get_default()
+            dpi_x = screen.width() / screen.width_mm() * 25.4
+            dpi_y = screen.height() / screen.height_mm() * 25.4
+
+            self._screen_dpi = (dpi_x + dpi_y) / 2.0
+
+        return self._screen_dpi
+
+    @property
+    def username(self):
+        """
+        Return user name derived from pwd entry
+        """
+        record = getpwnam(getuser()) # get pwd entry
+
+        self._log.debug("Found user pw entry: " + str(record))
+
+        if len(record[4]):
+            return record[4].split(",")[0]
+        else:
+            return record[0].title()
+
+    @property
+    def date_format(self):
+        """
+        Return localized date format for use in strftime()
+        """
+        return nl_langinfo(D_FMT)
+
+    @property
+    def language_code(self):
+        """
+        Return language code like 'de'
+        """
+        return getdefaultlocale()[0]
 
diff --git a/latex/latex/expander.py b/latex/latex/expander.py
index 975aa36..c34abd0 100644
--- a/latex/latex/expander.py
+++ b/latex/latex/expander.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -30,69 +30,69 @@ from parser import Node
 
 
 class LaTeXReferenceExpander(object):
-	"""
-	This expands '\include' and '\input' commands by parsing the referenced child
-	documents. The resulting trees may be attached to the parent tree.
-	"""
-	
-	# TODO: embed this into parser so that we don't need to walk the document again
-	
-	_log = getLogger("ReferenceExpander")
-	
-	def __init__(self):
-		self._document_cache = LaTeXDocumentCache()
-	
-	def expand(self, documentNode, master_file, issue_handler, charset):
-		"""
-		@param documentNode: the master model
-		@param master_file: the File object of the master
-		@param issue_handler: an IIssueHandler object
-		@param charset: a string naming the character set used by Gedit
-		"""
-		self._master_file = master_file
-		self._issue_handler = issue_handler
-		self._charset = charset
-		self._expand(documentNode)
-		
-	def _expand(self, parentNode):
-		"""
-		Recursively walk all nodes in the document model and check for \input or \include
-		commands. Extract the filename from the command and parse the referenced file.
-		Attach its model as a DOCUMENT node to the master model and hold its filename as
-		the value of that DOCUMENT node, so that the Validator may differ between issue
-		sources.
-		"""
-		for node in parentNode:
-			if node.type == Node.COMMAND and (node.value == "input" or node.value == "include"):
-				try:
-					# build child filename (absolute/relative, with .tex/without .tex)
-					target = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-
-					file = None
-					if File.is_absolute(target):
-						# absolute path
-						# look for 'x' and then for 'x.tex'
-						file = File("%s.tex" % target)
-						if not file.exists:
-							file = File(target)
-					else:
-						# path relative to the master file's directory
-						# TODO: include TeX search path!
-						# look for 'x' and then for 'x.tex'
-						file = File.create_from_relative_path("%s.tex" % target, self._master_file.dirname)
-						if not file.exists:
-							file = File.create_from_relative_path(target, self._master_file.dirname)
-					
-					self._log.debug("Expanding %s" % file)
-					
-					# lookup/parse child document model
-					try:
-						fragment = self._document_cache.get_document(file, self._charset, self._issue_handler)
-
-						node.append(fragment)
-					except IOError:
-						self._log.error("Referenced file not found: %s" % file.uri)
-				except IndexError:
-					self._log.error("Malformed reference command at %s" % node.start)
-			
-			self._expand(node)
+    """
+    This expands '\include' and '\input' commands by parsing the referenced child
+    documents. The resulting trees may be attached to the parent tree.
+    """
+
+    # TODO: embed this into parser so that we don't need to walk the document again
+
+    _log = getLogger("ReferenceExpander")
+
+    def __init__(self):
+        self._document_cache = LaTeXDocumentCache()
+
+    def expand(self, documentNode, master_file, issue_handler, charset):
+        """
+        @param documentNode: the master model
+        @param master_file: the File object of the master
+        @param issue_handler: an IIssueHandler object
+        @param charset: a string naming the character set used by Gedit
+        """
+        self._master_file = master_file
+        self._issue_handler = issue_handler
+        self._charset = charset
+        self._expand(documentNode)
+
+    def _expand(self, parentNode):
+        """
+        Recursively walk all nodes in the document model and check for \input or \include
+        commands. Extract the filename from the command and parse the referenced file.
+        Attach its model as a DOCUMENT node to the master model and hold its filename as
+        the value of that DOCUMENT node, so that the Validator may differ between issue
+        sources.
+        """
+        for node in parentNode:
+            if node.type == Node.COMMAND and (node.value == "input" or node.value == "include"):
+                try:
+                    # build child filename (absolute/relative, with .tex/without .tex)
+                    target = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+
+                    file = None
+                    if File.is_absolute(target):
+                        # absolute path
+                        # look for 'x' and then for 'x.tex'
+                        file = File("%s.tex" % target)
+                        if not file.exists:
+                            file = File(target)
+                    else:
+                        # path relative to the master file's directory
+                        # TODO: include TeX search path!
+                        # look for 'x' and then for 'x.tex'
+                        file = File.create_from_relative_path("%s.tex" % target, self._master_file.dirname)
+                        if not file.exists:
+                            file = File.create_from_relative_path(target, self._master_file.dirname)
+
+                    self._log.debug("Expanding %s" % file)
+
+                    # lookup/parse child document model
+                    try:
+                        fragment = self._document_cache.get_document(file, self._charset, self._issue_handler)
+
+                        node.append(fragment)
+                    except IOError:
+                        self._log.error("Referenced file not found: %s" % file.uri)
+                except IndexError:
+                    self._log.error("Malformed reference command at %s" % node.start)
+
+            self._expand(node)
diff --git a/latex/latex/inversesearch.py b/latex/latex/inversesearch.py
index f80aaa8..10ed416 100644
--- a/latex/latex/inversesearch.py
+++ b/latex/latex/inversesearch.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -42,56 +42,55 @@ BUS_NAME = 'org.gedit.LaTeXPlugin'
 OBJECT_PATH = '/org/gedit/LaTeXPlugin/InverseSearchService'
 
 try:
-	import dbus
-	import dbus.service
-	import dbus.glib	# attach D-Bus connections to main loop
-	
-	class InverseSearchService(dbus.service.Object):
-		"""
-		A D-Bus object listening for commands from xdvi. This is a delegate object
-		for GeditWindowDecorator.
-		
-		@deprecated: 
-		"""
-		
-		def __init__(self, context):
-			"""
-			Construct the service object
-			
-			@param context: a base.WindowContext instance
-			"""
-			bus_name = dbus.service.BusName(BUS_NAME, bus=dbus.SessionBus())
-			dbus.service.Object.__init__(self, bus_name, OBJECT_PATH)
-			
-			self._context = context
-			
-			_log.debug("Created service object %s" % OBJECT_PATH)
-
-		@dbus.service.method('org.gedit.LaTeXPlugin.InverseSearchService')
-		def inverse_search(self, filename, line):
-			"""
-			A service call has been received
-			
-			@param filename: the filename
-			@param line: a line number counting from 1 (!)
-			"""
-			_log.debug("Received inverse DVI search call: %s %s" % (filename, line))
-			
-			file = File(filename)
-			
-			try:
-				self._context.activate_editor(file)
-				editor = self._context.active_editor
-				
-				assert type(editor) is LaTeXEditor
-				
-				editor.select_lines(line - 1)
-				
-			except KeyError:
-				_log.error("Could not activate tab for file %s" % filename)
-			
+    import dbus
+    import dbus.service
+    import dbus.glib    # attach D-Bus connections to main loop
+
+    class InverseSearchService(dbus.service.Object):
+        """
+        A D-Bus object listening for commands from xdvi. This is a delegate object
+        for GeditWindowDecorator.
+
+        @deprecated:
+        """
+
+        def __init__(self, context):
+            """
+            Construct the service object
+
+            @param context: a base.WindowContext instance
+            """
+            bus_name = dbus.service.BusName(BUS_NAME, bus=dbus.SessionBus())
+            dbus.service.Object.__init__(self, bus_name, OBJECT_PATH)
+
+            self._context = context
+
+            _log.debug("Created service object %s" % OBJECT_PATH)
+
+        @dbus.service.method('org.gedit.LaTeXPlugin.InverseSearchService')
+        def inverse_search(self, filename, line):
+            """
+            A service call has been received
+
+            @param filename: the filename
+            @param line: a line number counting from 1 (!)
+            """
+            _log.debug("Received inverse DVI search call: %s %s" % (filename, line))
+
+            file = File(filename)
+
+            try:
+                self._context.activate_editor(file)
+                editor = self._context.active_editor
+
+                assert type(editor) is LaTeXEditor
+
+                editor.select_lines(line - 1)
+
+            except KeyError:
+                _log.error("Could not activate tab for file %s" % filename)
+
 except ImportError:
-	# TODO: popup a message
-	_log.error("Failed to import D-Bus bindings")
-			
-			
\ No newline at end of file
+    # TODO: popup a message
+    _log.error("Failed to import D-Bus bindings")
+
diff --git a/latex/latex/lexer.py b/latex/latex/lexer.py
index 66c9570..87a76ad 100644
--- a/latex/latex/lexer.py
+++ b/latex/latex/lexer.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -23,299 +23,299 @@ latex.lexer
 """
 
 class StringListener(object):
-	"""
-	Recognizes a string in a stream of characters
-	"""
-	def __init__(self, string, any_position=True):
-		"""
-		@param string: the character sequence to be recognized
-		@param any_position: if True the sequence may occur at any position in
-				the stream, if False it must occur at the start
-		"""
-		self._string = string
-		self._last = len(string)
-		self._pos = 0
-		self._any_position = any_position
-		
-		self._active = True
-		
-	def put(self, char):
-		"""
-		Returns True if the string is recognized
-		"""
-		if not self._active:
-			return False
-		
-		if char == self._string[self._pos]:
-			self._pos += 1
-		
-			if self._pos == self._last:
-				return True
-		else:
-			if self._any_position:
-				self._pos = 0
-			else:
-				self._active = False
-			
-		return False
+    """
+    Recognizes a string in a stream of characters
+    """
+    def __init__(self, string, any_position=True):
+        """
+        @param string: the character sequence to be recognized
+        @param any_position: if True the sequence may occur at any position in
+                the stream, if False it must occur at the start
+        """
+        self._string = string
+        self._last = len(string)
+        self._pos = 0
+        self._any_position = any_position
+
+        self._active = True
+
+    def put(self, char):
+        """
+        Returns True if the string is recognized
+        """
+        if not self._active:
+            return False
+
+        if char == self._string[self._pos]:
+            self._pos += 1
+
+            if self._pos == self._last:
+                return True
+        else:
+            if self._any_position:
+                self._pos = 0
+            else:
+                self._active = False
+
+        return False
 
 
 from ..util import StringReader
 
 
 class Token(object):
-	"""
-	A Token returned by the Lexer
-	"""
-	
-	COMMAND, TEXT, COMMENT, VERBATIM, BEGIN_CURLY, END_CURLY, BEGIN_SQUARE, END_SQUARE = range(8)
-	
-	def __init__(self, type, offset=None, value=None):
-		self.type = type
-		self.offset = offset
-		self.value = value
-	
-	@property
-	def xml(self):
-		if self.type == self.COMMAND:
-			return "<t:command>%s</t:command>" % self.value
-		elif self.type == self.TEXT:
-			return "<t:text>%s</t:text>" % self.value
-		elif self.type == self.VERBATIM:
-			return "<t:verbatim>%s</t:verbatim>" % self.value
-		elif self.type == self.COMMENT:
-			return "<t:comment>%s</t:comment>" % self.value
-		else:
-			return "<t:terminal />"
+    """
+    A Token returned by the Lexer
+    """
+
+    COMMAND, TEXT, COMMENT, VERBATIM, BEGIN_CURLY, END_CURLY, BEGIN_SQUARE, END_SQUARE = range(8)
+
+    def __init__(self, type, offset=None, value=None):
+        self.type = type
+        self.offset = offset
+        self.value = value
+
+    @property
+    def xml(self):
+        if self.type == self.COMMAND:
+            return "<t:command>%s</t:command>" % self.value
+        elif self.type == self.TEXT:
+            return "<t:text>%s</t:text>" % self.value
+        elif self.type == self.VERBATIM:
+            return "<t:verbatim>%s</t:verbatim>" % self.value
+        elif self.type == self.COMMENT:
+            return "<t:comment>%s</t:comment>" % self.value
+        else:
+            return "<t:terminal />"
 
 
 class Lexer(object):
-	"""
-	LaTeX lexer
-	"""
-	
-	# TODO: redesign and optimize this from a DFA
-	
-	# states of the lexer
-	_DEFAULT, _BACKSLASH, _COMMAND, _TEXT, _COMMENT, _PRE_VERB, _VERB, _VERBATIM = range(8)
-	
-	_SPECIAL = set(["&", "$", "{", "}", "[", "]", "%", "#", "_", "\\"])
-	
-	_TERMINALS = set(["{", "}", "[", "]"])
-	_TERMINALS_MAP = {"{" : Token.BEGIN_CURLY, "}" : Token.END_CURLY, 
-					  "[" : Token.BEGIN_SQUARE, "]" : Token.END_SQUARE}
-	
-	_VERBATIM_ENVIRONS = set(["verbatim", "verbatim*", "lstlisting", "lstlisting*"])
-	
-	
-	# additional states for recognizing "\begin{verbatim}"
-	_VERBATIM_DEFAULT, _VERBATIM_BEGIN, _VERBATIM_BEGIN_CURLY, _VERBATIM_BEGIN_CURLY_ENVIRON = range(4)
-	
-	
-	def __init__(self, string, skipWs=True, skipComment=False):
-		self._reader = StringReader(string)
-		
-		self._skipWs = skipWs
-		self._skipComment = skipComment
-		
-		self._state = self._DEFAULT
-		self._verbatimState = self._VERBATIM_DEFAULT
-		
-		self._eof = False
-		self._tokenStack = []	# used to return a sequence of tokens after a verbatim ended
-		
-	def __iter__(self):
-		return self
-	
-	def next(self):
-		if self._eof:
-			raise StopIteration
-		
-		# first empty the token stack
-		if len(self._tokenStack):
-			return self._tokenStack.pop()
-		
-		while True:
-			try:
-				char = self._reader.read()
-				
-				if self._state == self._DEFAULT:
-					if char == "\\":
-						self._state = self._BACKSLASH
-						self._verbatimState = self._VERBATIM_DEFAULT
-						self._startOffset = self._reader.offset - 1
-					
-					elif char == "%":
-						self._state = self._COMMENT
-						self._verbatimState = self._VERBATIM_DEFAULT
-						self._startOffset = self._reader.offset - 1
-						if not self._skipComment:
-							self._text = []
-					
-					elif char in self._TERMINALS:
-						if self._verbatimState == self._VERBATIM_BEGIN and char == "{":
-							self._verbatimState = self._VERBATIM_BEGIN_CURLY
-							
-						elif self._verbatimState == self._VERBATIM_BEGIN_CURLY_ENVIRON and char == "}":
-							# we have "\begin{verbatim}"
-							self._verbatimState = self._VERBATIM_DEFAULT
-							self._state = self._VERBATIM
-							self._text = []
-							self._startOffset = self._reader.offset
-							self._verbatimSequenceListener = StringListener("\\end{%s}" % self._verbatimEnviron)
-							
-						else:
-							self._verbatimState = self._VERBATIM_DEFAULT
-							
-						return Token(self._TERMINALS_MAP[char], self._reader.offset - 1)
-					
-					else:
-						self._state = self._TEXT
-						self._startOffset = self._reader.offset - 1
-						self._text = [char]
-					
-				elif self._state == self._BACKSLASH:
-					if char in self._SPECIAL or char.isspace():
-						# this is a one-character-command, also whitespace is allowed
-						self._state = self._DEFAULT
-						return Token(Token.COMMAND, self._startOffset, char)
-					
-					else:
-						self._state = self._COMMAND
-						
-						self._verbListener = StringListener("verb", any_position=False)
-						self._verbListener.put(char)
-						
-						self._text = [char]
-				
-				elif self._state == self._COMMENT:
-					if char == "\n":
-						self._state = self._DEFAULT
-						if not self._skipComment:
-							return Token(Token.COMMENT, self._startOffset, "".join(self._text))
-					
-					else:
-						if not self._skipComment:
-							self._text.append(char)
-				
-				elif self._state == self._COMMAND:
-					if char in self._SPECIAL or char.isspace():
-
-						name = "".join(self._text)
-						
-						# this is mostly false because \verb is mostly followed by something like |
-						if name == "verb":
-							self._state = self._VERB
-							self._verbDelimiter = char
-							self._startOffset = self._reader.offset - 1
-							self._text = [char]
-						
-						elif name == "url": 	# we handle "\url" just like "\verb"
-							self._state = self._VERB
-							self._verbDelimiter = "}"
-							self._startOffset = self._reader.offset - 1
-							self._text = []
-						
-						else:
-							self._state = self._DEFAULT
-							self._reader.unread(char)
-							
-							if name == "begin":
-								self._verbatimState = self._VERBATIM_BEGIN
-							
-							return Token(Token.COMMAND, self._startOffset, name)
-					
-					else:
-						if self._verbListener.put(char):
-							# we have "\verb"
-							self._state = self._PRE_VERB
-						else:
-							self._text.append(char)
-				
-				elif self._state == self._PRE_VERB:
-					self._state = self._VERB
-					self._verbDelimiter = char
-					self._startOffset = self._reader.offset - 1
-					self._text = []
-				
-				elif self._state == self._TEXT:
-					if char in self._SPECIAL:
-						self._state = self._DEFAULT
-						self._reader.unread(char)
-						
-						text = "".join(self._text)
-						
-						if self._skipWs and text.isspace():
-							continue
-						else:
-							
-							if self._verbatimState == self._VERBATIM_BEGIN_CURLY:
-								# we have "\begin{" until now, handle verbatim environment
-								
-								if text in self._VERBATIM_ENVIRONS:
-									self._verbatimEnviron = text
-									self._verbatimState = self._VERBATIM_BEGIN_CURLY_ENVIRON
-								
-								else:
-									self._verbatimState = self._VERBATIM_DEFAULT
-							
-							return Token(Token.TEXT, self._startOffset, text)
-					
-					else:
-						self._text.append(char)
-				
-				elif self._state == self._VERB:
-					if char == self._verbDelimiter:		# FIXME: \overbrace
-						self._state = self._DEFAULT
-						
-						return Token(Token.VERBATIM, self._startOffset, "".join(self._text) + char)
-					
-					else:
-						self._text.append(char)
-				
-				elif self._state == self._VERBATIM:
-					if self._verbatimSequenceListener.put(char):
-						self._state = self._DEFAULT
-						
-						# TODO: calculate offsets
-						self._tokenStack = [ Token(Token.END_CURLY, 0),
-											 Token(Token.TEXT, 0, self._verbatimEnviron),
-											 Token(Token.BEGIN_CURLY, 0),
-											 Token(Token.COMMAND, 0, "end") ]
-						
-						text = "".join(self._text)
-						text = text[5:]		# cut off "\end{"
-						return Token(Token.VERBATIM, self._startOffset, text)
-					else:
-						self._text.append(char)
-				
-				elif self._state == self._VERB:
-					# this char is the verb delimiter
-					# TODO: implement verbatim detection
-					pass
-						
-			except StopIteration:
-				self._eof = True
-				
-				# evaluate final state
-				if self._state == self._BACKSLASH:
-					return Token(Token.COMMAND, self._startOffset, "")
-				
-				elif self._state == self._COMMAND:
-					return Token(Token.COMMAND, self._startOffset, "".join(self._text))
-				
-				elif self._state == self._TEXT:
-					text = "".join(self._text)
-					if not (self._skipWs and text.isspace()):
-						return Token(Token.TEXT, self._startOffset, text)
-				
-				elif self._state == self._VERB:
-					
-					# TODO: the document is malformed in this case, so the lexer should be
-					# able to produce issues, too
-					#
-					# TODO: return a VERBATIM token
-					
-					return Token(Token.TEXT, self._startOffset, "".join(self._text)) 
-				
-				raise StopIteration
\ No newline at end of file
+    """
+    LaTeX lexer
+    """
+
+    # TODO: redesign and optimize this from a DFA
+
+    # states of the lexer
+    _DEFAULT, _BACKSLASH, _COMMAND, _TEXT, _COMMENT, _PRE_VERB, _VERB, _VERBATIM = range(8)
+
+    _SPECIAL = set(["&", "$", "{", "}", "[", "]", "%", "#", "_", "\\"])
+
+    _TERMINALS = set(["{", "}", "[", "]"])
+    _TERMINALS_MAP = {"{" : Token.BEGIN_CURLY, "}" : Token.END_CURLY,
+                      "[" : Token.BEGIN_SQUARE, "]" : Token.END_SQUARE}
+
+    _VERBATIM_ENVIRONS = set(["verbatim", "verbatim*", "lstlisting", "lstlisting*"])
+
+
+    # additional states for recognizing "\begin{verbatim}"
+    _VERBATIM_DEFAULT, _VERBATIM_BEGIN, _VERBATIM_BEGIN_CURLY, _VERBATIM_BEGIN_CURLY_ENVIRON = range(4)
+
+
+    def __init__(self, string, skipWs=True, skipComment=False):
+        self._reader = StringReader(string)
+
+        self._skipWs = skipWs
+        self._skipComment = skipComment
+
+        self._state = self._DEFAULT
+        self._verbatimState = self._VERBATIM_DEFAULT
+
+        self._eof = False
+        self._tokenStack = []    # used to return a sequence of tokens after a verbatim ended
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        if self._eof:
+            raise StopIteration
+
+        # first empty the token stack
+        if len(self._tokenStack):
+            return self._tokenStack.pop()
+
+        while True:
+            try:
+                char = self._reader.read()
+
+                if self._state == self._DEFAULT:
+                    if char == "\\":
+                        self._state = self._BACKSLASH
+                        self._verbatimState = self._VERBATIM_DEFAULT
+                        self._startOffset = self._reader.offset - 1
+
+                    elif char == "%":
+                        self._state = self._COMMENT
+                        self._verbatimState = self._VERBATIM_DEFAULT
+                        self._startOffset = self._reader.offset - 1
+                        if not self._skipComment:
+                            self._text = []
+
+                    elif char in self._TERMINALS:
+                        if self._verbatimState == self._VERBATIM_BEGIN and char == "{":
+                            self._verbatimState = self._VERBATIM_BEGIN_CURLY
+
+                        elif self._verbatimState == self._VERBATIM_BEGIN_CURLY_ENVIRON and char == "}":
+                            # we have "\begin{verbatim}"
+                            self._verbatimState = self._VERBATIM_DEFAULT
+                            self._state = self._VERBATIM
+                            self._text = []
+                            self._startOffset = self._reader.offset
+                            self._verbatimSequenceListener = StringListener("\\end{%s}" % self._verbatimEnviron)
+
+                        else:
+                            self._verbatimState = self._VERBATIM_DEFAULT
+
+                        return Token(self._TERMINALS_MAP[char], self._reader.offset - 1)
+
+                    else:
+                        self._state = self._TEXT
+                        self._startOffset = self._reader.offset - 1
+                        self._text = [char]
+
+                elif self._state == self._BACKSLASH:
+                    if char in self._SPECIAL or char.isspace():
+                        # this is a one-character-command, also whitespace is allowed
+                        self._state = self._DEFAULT
+                        return Token(Token.COMMAND, self._startOffset, char)
+
+                    else:
+                        self._state = self._COMMAND
+
+                        self._verbListener = StringListener("verb", any_position=False)
+                        self._verbListener.put(char)
+
+                        self._text = [char]
+
+                elif self._state == self._COMMENT:
+                    if char == "\n":
+                        self._state = self._DEFAULT
+                        if not self._skipComment:
+                            return Token(Token.COMMENT, self._startOffset, "".join(self._text))
+
+                    else:
+                        if not self._skipComment:
+                            self._text.append(char)
+
+                elif self._state == self._COMMAND:
+                    if char in self._SPECIAL or char.isspace():
+
+                        name = "".join(self._text)
+
+                        # this is mostly false because \verb is mostly followed by something like |
+                        if name == "verb":
+                            self._state = self._VERB
+                            self._verbDelimiter = char
+                            self._startOffset = self._reader.offset - 1
+                            self._text = [char]
+
+                        elif name == "url":     # we handle "\url" just like "\verb"
+                            self._state = self._VERB
+                            self._verbDelimiter = "}"
+                            self._startOffset = self._reader.offset - 1
+                            self._text = []
+
+                        else:
+                            self._state = self._DEFAULT
+                            self._reader.unread(char)
+
+                            if name == "begin":
+                                self._verbatimState = self._VERBATIM_BEGIN
+
+                            return Token(Token.COMMAND, self._startOffset, name)
+
+                    else:
+                        if self._verbListener.put(char):
+                            # we have "\verb"
+                            self._state = self._PRE_VERB
+                        else:
+                            self._text.append(char)
+
+                elif self._state == self._PRE_VERB:
+                    self._state = self._VERB
+                    self._verbDelimiter = char
+                    self._startOffset = self._reader.offset - 1
+                    self._text = []
+
+                elif self._state == self._TEXT:
+                    if char in self._SPECIAL:
+                        self._state = self._DEFAULT
+                        self._reader.unread(char)
+
+                        text = "".join(self._text)
+
+                        if self._skipWs and text.isspace():
+                            continue
+                        else:
+
+                            if self._verbatimState == self._VERBATIM_BEGIN_CURLY:
+                                # we have "\begin{" until now, handle verbatim environment
+
+                                if text in self._VERBATIM_ENVIRONS:
+                                    self._verbatimEnviron = text
+                                    self._verbatimState = self._VERBATIM_BEGIN_CURLY_ENVIRON
+
+                                else:
+                                    self._verbatimState = self._VERBATIM_DEFAULT
+
+                            return Token(Token.TEXT, self._startOffset, text)
+
+                    else:
+                        self._text.append(char)
+
+                elif self._state == self._VERB:
+                    if char == self._verbDelimiter:        # FIXME: \overbrace
+                        self._state = self._DEFAULT
+
+                        return Token(Token.VERBATIM, self._startOffset, "".join(self._text) + char)
+
+                    else:
+                        self._text.append(char)
+
+                elif self._state == self._VERBATIM:
+                    if self._verbatimSequenceListener.put(char):
+                        self._state = self._DEFAULT
+
+                        # TODO: calculate offsets
+                        self._tokenStack = [ Token(Token.END_CURLY, 0),
+                                             Token(Token.TEXT, 0, self._verbatimEnviron),
+                                             Token(Token.BEGIN_CURLY, 0),
+                                             Token(Token.COMMAND, 0, "end") ]
+
+                        text = "".join(self._text)
+                        text = text[5:]        # cut off "\end{"
+                        return Token(Token.VERBATIM, self._startOffset, text)
+                    else:
+                        self._text.append(char)
+
+                elif self._state == self._VERB:
+                    # this char is the verb delimiter
+                    # TODO: implement verbatim detection
+                    pass
+
+            except StopIteration:
+                self._eof = True
+
+                # evaluate final state
+                if self._state == self._BACKSLASH:
+                    return Token(Token.COMMAND, self._startOffset, "")
+
+                elif self._state == self._COMMAND:
+                    return Token(Token.COMMAND, self._startOffset, "".join(self._text))
+
+                elif self._state == self._TEXT:
+                    text = "".join(self._text)
+                    if not (self._skipWs and text.isspace()):
+                        return Token(Token.TEXT, self._startOffset, text)
+
+                elif self._state == self._VERB:
+
+                    # TODO: the document is malformed in this case, so the lexer should be
+                    # able to produce issues, too
+                    #
+                    # TODO: return a VERBATIM token
+
+                    return Token(Token.TEXT, self._startOffset, "".join(self._text))
+
+                raise StopIteration
\ No newline at end of file
diff --git a/latex/latex/listing.py b/latex/latex/listing.py
index 1746fb5..a9627b8 100644
--- a/latex/latex/listing.py
+++ b/latex/latex/listing.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -26,49 +26,48 @@ from xml.sax import ContentHandler, parse
 
 
 class Language(object):
-	def __init__(self, name):
-		self.name = name
-		self.dialects = []
-		
+    def __init__(self, name):
+        self.name = name
+        self.dialects = []
+
 
 class Dialect(object):
-	def __init__(self, name, default):
-		self.name = name
-		self.default = default
-	
+    def __init__(self, name, default):
+        self.name = name
+        self.default = default
+
 
 class LanguagesParser(ContentHandler):
-	"""
-	This parses the listing.xml file containing the available languages for listings
-	"""
-	def __init__(self):
-		self._languages = None
-		self._language = None
-	
-	def startElement(self, name, attributes):
-		if name == "language":
-			l = Language(attributes["name"])
-			self._languages.append(l)
-			self._language = l
-		elif name == "dialect":
-			default = False
-			try:
-				if attributes["default"] == "true":
-					default = True
-			except KeyError:
-				pass
-			
-			self._language.dialects.append(Dialect(attributes["name"], default))
-		elif name == "no-dialect":
-			self._language.dialects.append(Dialect(None, True))
-			
-	def parse(self, languages, filename):
-		"""
-		Parse XML
-		
-		"languages" must be a list
-		"""
-		self._languages = languages
-		parse(filename, self)
-		
-		
\ No newline at end of file
+    """
+    This parses the listing.xml file containing the available languages for listings
+    """
+    def __init__(self):
+        self._languages = None
+        self._language = None
+
+    def startElement(self, name, attributes):
+        if name == "language":
+            l = Language(attributes["name"])
+            self._languages.append(l)
+            self._language = l
+        elif name == "dialect":
+            default = False
+            try:
+                if attributes["default"] == "true":
+                    default = True
+            except KeyError:
+                pass
+
+            self._language.dialects.append(Dialect(attributes["name"], default))
+        elif name == "no-dialect":
+            self._language.dialects.append(Dialect(None, True))
+
+    def parse(self, languages, filename):
+        """
+        Parse XML
+
+        "languages" must be a list
+        """
+        self._languages = languages
+        parse(filename, self)
+
diff --git a/latex/latex/model.py b/latex/latex/model.py
index 885d6ff..9d1f28d 100644
--- a/latex/latex/model.py
+++ b/latex/latex/model.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -28,214 +28,214 @@ from logging import getLogger
 
 
 class Element(object):
-	"""
-	"""
-	TYPE_COMMAND, TYPE_MANDATORY_ARGUMENT, TYPE_OPTIONAL_ARGUMENT, TYPE_CHOICE, TYPE_PLACEHOLDER = 1, 2, 3, 4, 5
-	
-	def __init__(self, package, type):
-		"""
-		@param package: the name of the LaTeX package providing this Element 
-		@param type: one of TYPE_*
-		"""
-		self.package = package
-		self.type = type
-		self._children = []
-	
-	@property
-	def children(self):
-		return self._children
-	
-	def append_child(self, child):
-		self._children.append(child)
-	
+    """
+    """
+    TYPE_COMMAND, TYPE_MANDATORY_ARGUMENT, TYPE_OPTIONAL_ARGUMENT, TYPE_CHOICE, TYPE_PLACEHOLDER = 1, 2, 3, 4, 5
+
+    def __init__(self, package, type):
+        """
+        @param package: the name of the LaTeX package providing this Element
+        @param type: one of TYPE_*
+        """
+        self.package = package
+        self.type = type
+        self._children = []
+
+    @property
+    def children(self):
+        return self._children
+
+    def append_child(self, child):
+        self._children.append(child)
+
 
 class Command(Element):
-	def __init__(self, package, name):
-		Element.__init__(self, package, Element.TYPE_COMMAND)
-		self.name = name
-	
-	@property
-	def first_mandatory_argument(self):
-		for node in self.children:
-			if node.type == Element.TYPE_MANDATORY_ARGUMENT:
-				return node
-		raise IndexError
-	
-	@property
-	def first_optional_argument(self):
-		for node in self.children:
-			if node.type == Element.TYPE_OPTIONAL_ARGUMENT:
-				return node
-		raise IndexError
+    def __init__(self, package, name):
+        Element.__init__(self, package, Element.TYPE_COMMAND)
+        self.name = name
+
+    @property
+    def first_mandatory_argument(self):
+        for node in self.children:
+            if node.type == Element.TYPE_MANDATORY_ARGUMENT:
+                return node
+        raise IndexError
+
+    @property
+    def first_optional_argument(self):
+        for node in self.children:
+            if node.type == Element.TYPE_OPTIONAL_ARGUMENT:
+                return node
+        raise IndexError
 
 
 class Argument(Element):
-	def get_children(self):
-		"""
-		Override the children property of Element to be able to evaluate Placeholders
-		allowed as children of Arguments
-		"""
-		children = []
-		
-		for node in self._children:
-			if node.type == Element.TYPE_PLACEHOLDER:
-				children.extend(node.children)
-			else:
-				children.append(node)
-		return children
-	
-	def set_children(self, children):
-		self._children = children
-		
-	children = property(get_children, set_children)
+    def get_children(self):
+        """
+        Override the children property of Element to be able to evaluate Placeholders
+        allowed as children of Arguments
+        """
+        children = []
+
+        for node in self._children:
+            if node.type == Element.TYPE_PLACEHOLDER:
+                children.extend(node.children)
+            else:
+                children.append(node)
+        return children
+
+    def set_children(self, children):
+        self._children = children
+
+    children = property(get_children, set_children)
 
 
 class MandatoryArgument(Argument):
-	def __init__(self, package, label):
-		Argument.__init__(self, package, Element.TYPE_MANDATORY_ARGUMENT)
-		self.label = label
+    def __init__(self, package, label):
+        Argument.__init__(self, package, Element.TYPE_MANDATORY_ARGUMENT)
+        self.label = label
 
 
 class OptionalArgument(Argument):
-	def __init__(self, package, label):
-		Argument.__init__(self, package, Element.TYPE_OPTIONAL_ARGUMENT)
-		self.label = label
-		
-		
+    def __init__(self, package, label):
+        Argument.__init__(self, package, Element.TYPE_OPTIONAL_ARGUMENT)
+        self.label = label
+
+
 class Choice(Element):
-	def __init__(self, package, value, details=None):
-		Element.__init__(self, package, Element.TYPE_CHOICE)
-		self.value = value
-		self.details = details
+    def __init__(self, package, value, details=None):
+        Element.__init__(self, package, Element.TYPE_CHOICE)
+        self.value = value
+        self.details = details
 
 
 class Placeholder(Element):
-	def __init__(self, name):
-		Element.__init__(self, None, Element.TYPE_PLACEHOLDER)
-		self.name = name
-	
-	def get_children(self):
-		return self._children
-	
-	def set_children(self, children):
-		self._children = children
-	
-	children = property(get_children, set_children)
+    def __init__(self, name):
+        Element.__init__(self, None, Element.TYPE_PLACEHOLDER)
+        self.name = name
+
+    def get_children(self):
+        return self._children
+
+    def set_children(self, children):
+        self._children = children
+
+    children = property(get_children, set_children)
 
 
 class LanguageModel(object):
-	"""
-	"""
-	
-	__log = getLogger("LanguageModel")
-	
-	def __init__(self):
-		self.commands = {}			# maps command names to Command elements
-		
-		self.__placeholders = {}
-		self.__newcommands = []
-		
-		self.__log.debug("init")
-	
-	def find_command(self, prefix):
-		"""
-		Find a command by a prefix. A prefix like 'be' would return the command '\begin'
-		"""
-		return [command for name, command in self.commands.iteritems() if name.startswith(prefix)]
-	
-	def register_placeholder(self, placeholder):
-		"""
-		Register a placeholder under its name. There may be multiple 
-		placeholder nodes for one name.
-		"""
-		try:
-			nodes = self.__placeholders[placeholder.name]
-			nodes.append(placeholder)
-		except KeyError:
-			self.__placeholders[placeholder.name] = [placeholder]
-	
-	def fill_placeholder(self, name, child_elements):
-		"""
-		Attach child elements to a placeholder
-		"""
-		try:
-			#self.__log.debug("fill_placeholder: name=%s, child_elements=%s" % (name, child_elements))
-			
-			for placeholder in self.__placeholders[name]:
-				placeholder.children = child_elements
-		except KeyError:
-			self.__log.error("fill_placeholder: placeholder '%s' not registered" % name)
-	
-	def set_newcommands(self, newcommands):
-		
-		# TODO: use sets
-		
-		self.__log.debug("set_newcommands: " + ",".join([c.name for c in newcommands]))
-		
-		for name in self.__newcommands:
-			self.commands.__delitem__(name)
-		
-		for command in newcommands:
-			self.commands[command.name] = command
+    """
+    """
+
+    __log = getLogger("LanguageModel")
+
+    def __init__(self):
+        self.commands = {}            # maps command names to Command elements
+
+        self.__placeholders = {}
+        self.__newcommands = []
+
+        self.__log.debug("init")
+
+    def find_command(self, prefix):
+        """
+        Find a command by a prefix. A prefix like 'be' would return the command '\begin'
+        """
+        return [command for name, command in self.commands.iteritems() if name.startswith(prefix)]
+
+    def register_placeholder(self, placeholder):
+        """
+        Register a placeholder under its name. There may be multiple
+        placeholder nodes for one name.
+        """
+        try:
+            nodes = self.__placeholders[placeholder.name]
+            nodes.append(placeholder)
+        except KeyError:
+            self.__placeholders[placeholder.name] = [placeholder]
+
+    def fill_placeholder(self, name, child_elements):
+        """
+        Attach child elements to a placeholder
+        """
+        try:
+            #self.__log.debug("fill_placeholder: name=%s, child_elements=%s" % (name, child_elements))
+
+            for placeholder in self.__placeholders[name]:
+                placeholder.children = child_elements
+        except KeyError:
+            self.__log.error("fill_placeholder: placeholder '%s' not registered" % name)
+
+    def set_newcommands(self, newcommands):
+
+        # TODO: use sets
+
+        self.__log.debug("set_newcommands: " + ",".join([c.name for c in newcommands]))
+
+        for name in self.__newcommands:
+            self.commands.__delitem__(name)
+
+        for command in newcommands:
+            self.commands[command.name] = command
 
 
 from xml import sax
 
 
 class LanguageModelParser(sax.ContentHandler):
-	"""
-	SAX parser for the language model in latex.xml
-	"""
-	
-	# TODO: this should be a simple state machine
-	
-	__log = getLogger("LanguageModelParser")
-	
-	def parse(self, filename, language_model):
-		self.__language_model = language_model
-		
-		self.__command = None
-		self.__argument = None
-		
-		self.__log.debug("Parsing %s" % filename)
-		
-		sax.parse(filename, self)
-		
-	def startElement(self, name, attrs):
-		try:
-			package = attrs["package"]
-		except KeyError:
-			package = None
-		
-		if name == "command":
-			name = attrs["name"]
-			self.__command = Command(package, name)
-			self.__language_model.commands[name] = self.__command
-			
-		elif name == "argument":
-			try:
-				label = attrs["label"]
-			except KeyError:
-				label = ""
-				
-			try:
-				if attrs["type"] == "optional":
-					self.__argument = OptionalArgument(package, label)
-				else:
-					self.__argument = MandatoryArgument(package, label)
-			except KeyError:
-				self.__argument = MandatoryArgument(package, label)
-				
-			self.__command.children.append(self.__argument)
-			
-		elif name == "choice":
-			choice = Choice(package, attrs["name"])
-			self.__argument.append_child(choice)
-		
-		elif name == "placeholder":
-			placeholder = Placeholder(attrs["key"])
-			self.__argument.append_child(placeholder)
-			self.__language_model.register_placeholder(placeholder)
+    """
+    SAX parser for the language model in latex.xml
+    """
+
+    # TODO: this should be a simple state machine
+
+    __log = getLogger("LanguageModelParser")
+
+    def parse(self, filename, language_model):
+        self.__language_model = language_model
+
+        self.__command = None
+        self.__argument = None
+
+        self.__log.debug("Parsing %s" % filename)
+
+        sax.parse(filename, self)
+
+    def startElement(self, name, attrs):
+        try:
+            package = attrs["package"]
+        except KeyError:
+            package = None
+
+        if name == "command":
+            name = attrs["name"]
+            self.__command = Command(package, name)
+            self.__language_model.commands[name] = self.__command
+
+        elif name == "argument":
+            try:
+                label = attrs["label"]
+            except KeyError:
+                label = ""
+
+            try:
+                if attrs["type"] == "optional":
+                    self.__argument = OptionalArgument(package, label)
+                else:
+                    self.__argument = MandatoryArgument(package, label)
+            except KeyError:
+                self.__argument = MandatoryArgument(package, label)
+
+            self.__command.children.append(self.__argument)
+
+        elif name == "choice":
+            choice = Choice(package, attrs["name"])
+            self.__argument.append_child(choice)
+
+        elif name == "placeholder":
+            placeholder = Placeholder(attrs["key"])
+            self.__argument.append_child(placeholder)
+            self.__language_model.register_placeholder(placeholder)
 
 
 from copy import deepcopy
@@ -246,62 +246,62 @@ from ..base import File
 
 
 class LanguageModelFactory(object):
-	"""
-	This singleton creates LanguageModel instances. 
-	
-	If a serialized ('pickled') LanguageModel object exists, then a copy 
-	of this object is returned. Otherwise the XML file must be parsed.
-	"""
-	
-	__log = getLogger("LanguageModelFactory")
-	
-	def __new__(cls):
-		if not '_instance' in cls.__dict__:
-			cls._instance = object.__new__(cls)
-		return cls._instance
-	
-	def __init__(self):
-		if not '_ready' in dir(self):
-			
-			pickled_object = self.__find_pickled_object()
-			
-			if pickled_object:
-				self.__language_model = pickled_object
-			else:
-				pkl_filename = find_resource("latex.pkl", MODE_READWRITE)
-				xml_filename = find_resource("latex.xml")
-				
-				self.__language_model = LanguageModel()
-				parser = LanguageModelParser()
-				parser.parse(xml_filename, self.__language_model)
-				
-				pickle.dump(self.__language_model, open(pkl_filename, 'w'))
-			
-			self._ready = True
-	
-	def __find_pickled_object(self):
-		pkl_file = File(find_resource("latex.pkl", MODE_READWRITE))
-		xml_file = File(find_resource("latex.xml"))
-		
-		if pkl_file.exists:
-			if xml_file.mtime > pkl_file.mtime:
-				self.__log.debug("Pickled object and XML file have different modification times")
-			else:
-				try:
-					self.__log.debug("Pickled object found: %s" % pkl_file.path)
-					return pickle.load(open(pkl_file.path))
-				except:
-					return None
-		else:
-			self.__log.debug("No pickled object found")
-		return None
-	
-	def create_language_model(self):
-		"""
-		Return a new LanguageModel
-		"""
-		return deepcopy(self.__language_model)
-		
-		
-		
-		
+    """
+    This singleton creates LanguageModel instances.
+
+    If a serialized ('pickled') LanguageModel object exists, then a copy
+    of this object is returned. Otherwise the XML file must be parsed.
+    """
+
+    __log = getLogger("LanguageModelFactory")
+
+    def __new__(cls):
+        if not '_instance' in cls.__dict__:
+            cls._instance = object.__new__(cls)
+        return cls._instance
+
+    def __init__(self):
+        if not '_ready' in dir(self):
+
+            pickled_object = self.__find_pickled_object()
+
+            if pickled_object:
+                self.__language_model = pickled_object
+            else:
+                pkl_filename = find_resource("latex.pkl", MODE_READWRITE)
+                xml_filename = find_resource("latex.xml")
+
+                self.__language_model = LanguageModel()
+                parser = LanguageModelParser()
+                parser.parse(xml_filename, self.__language_model)
+
+                pickle.dump(self.__language_model, open(pkl_filename, 'w'))
+
+            self._ready = True
+
+    def __find_pickled_object(self):
+        pkl_file = File(find_resource("latex.pkl", MODE_READWRITE))
+        xml_file = File(find_resource("latex.xml"))
+
+        if pkl_file.exists:
+            if xml_file.mtime > pkl_file.mtime:
+                self.__log.debug("Pickled object and XML file have different modification times")
+            else:
+                try:
+                    self.__log.debug("Pickled object found: %s" % pkl_file.path)
+                    return pickle.load(open(pkl_file.path))
+                except:
+                    return None
+        else:
+            self.__log.debug("No pickled object found")
+        return None
+
+    def create_language_model(self):
+        """
+        Return a new LanguageModel
+        """
+        return deepcopy(self.__language_model)
+
+
+
+
diff --git a/latex/latex/outline.py b/latex/latex/outline.py
index d407885..9a2b0b0 100644
--- a/latex/latex/outline.py
+++ b/latex/latex/outline.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -33,40 +33,40 @@ from ..issues import Issue
 
 
 class OutlineNode(list):
-	
-	ROOT, STRUCTURE, LABEL, NEWCOMMAND, REFERENCE, GRAPHICS, PACKAGE, TABLE, NEWENVIRONMENT = range(9)
-	
-	def __init__(self, type, start=None, end=None, value=None, level=None, foreign=False, numOfArgs=None, file=None):
-		"""
-		numOfArgs		only used for NEWCOMMAND type
-		"""
-		self.type = type
-		self.start = start
-		self.end = end
-		self.value = value
-		self.level = level
-		self.foreign = foreign
-		self.numOfArgs = numOfArgs
-		self.file = file
-	
-	@property
-	def xml(self):
-		if self.type == self.ROOT:
-			return "<root>%s</root>" % "".join([child.xml for child in self])
-		elif self.type == self.STRUCTURE:
-			return "<structure level=\"%s\" headline=\"%s\">%s</structure>" % (self.level, self.value, 
-																				"".join([child.xml for child in self]))
-			
+
+    ROOT, STRUCTURE, LABEL, NEWCOMMAND, REFERENCE, GRAPHICS, PACKAGE, TABLE, NEWENVIRONMENT = range(9)
+
+    def __init__(self, type, start=None, end=None, value=None, level=None, foreign=False, numOfArgs=None, file=None):
+        """
+        numOfArgs        only used for NEWCOMMAND type
+        """
+        self.type = type
+        self.start = start
+        self.end = end
+        self.value = value
+        self.level = level
+        self.foreign = foreign
+        self.numOfArgs = numOfArgs
+        self.file = file
+
+    @property
+    def xml(self):
+        if self.type == self.ROOT:
+            return "<root>%s</root>" % "".join([child.xml for child in self])
+        elif self.type == self.STRUCTURE:
+            return "<structure level=\"%s\" headline=\"%s\">%s</structure>" % (self.level, self.value,
+                                                                                "".join([child.xml for child in self]))
+
 
 class Outline(object):
-	def __init__(self):
-		self.rootNode = OutlineNode(OutlineNode.ROOT, level=0)
-		self.labels = []			# OutlineNode objects
-		self.bibliographies = []	# File objects
-		self.colors = []
-		self.packages = []			# OutlineNode objects
-		self.newcommands = []		# OutlineNode objects
-		self.newenvironments = []	# OutlineNode objects
+    def __init__(self):
+        self.rootNode = OutlineNode(OutlineNode.ROOT, level=0)
+        self.labels = []            # OutlineNode objects
+        self.bibliographies = []    # File objects
+        self.colors = []
+        self.packages = []            # OutlineNode objects
+        self.newcommands = []        # OutlineNode objects
+        self.newenvironments = []    # OutlineNode objects
 
 
 from ..base import File
@@ -74,190 +74,189 @@ from ..preferences import Preferences
 
 
 class LaTeXOutlineGenerator(object):
-	
-	_log = getLogger("LaTeXOutlineGenerator")
-	
-	# TODO: foreign flag is not necessary
-	
-	_STRUCTURE_LEVELS = { "part" : 1, "part*" : 1, 
-						  "chapter" : 2, "chapter*" : 2, 
-						  "section" : 3, "section*" : 3,
-						  "subsection" : 4, "subsection*" : 4,
-						  "subsubsection" : 5, "subsubsection*" : 5,
-						  "paragraph" : 6, 
-						  "subparagraph" : 7 }
-	
-#	def __init__(self):
-#		# TODO: read config
-#		self.cfgLabelsInTree = True
-#		self.cfgTablesInTree = True
-#		self.cfgGraphicsInTree = True
-	
-	def generate(self, documentNode, issue_handler):
-		"""
-		Generates an outline model from a document model and returns a list
-		of list of issues if some occured.
-		"""
-		
-		# setup
-		self.cfgLabelsInTree = Preferences().get_bool("outline-show-labels")
-		self.cfgTablesInTree = Preferences().get_bool("outline-show-tables")
-		self.cfgGraphicsInTree = Preferences().get_bool("outline-show-graphics")
-		
-		self._outline = Outline()
-		self._stack = [self._outline.rootNode]
-		
-		self._labelCache = {}
-		
-#		self._file = documentNode.value		# this is updated when a DOCUMENT occurs
-		
-		self._walk(documentNode, issue_handler)
-		
-		return self._outline
-	
-	def _walk(self, parentNode, issue_handler, foreign=False):
-		"""
-		Recursively walk a node in the document model
-		
-		foreign		if True this node is a child of a reference node, so it's coming 
-					from an expanded reference
-		"""
-		
-		childForeign = foreign
-		
-		for node in parentNode:
-#			if node.type == Node.DOCUMENT:
-#				self._file = node.value
-			if node.type == Node.COMMAND:
-				if node.value in self._STRUCTURE_LEVELS.keys():
-					try:
-						headline = node.firstOfType(Node.MANDATORY_ARGUMENT).innerMarkup
-						level = self._STRUCTURE_LEVELS[node.value]
-						outlineNode = OutlineNode(OutlineNode.STRUCTURE, node.start, node.lastEnd, headline, level, foreign, file=node.file)
-						
-						while self._stack[-1].level >= level:
-							self._stack.pop()
-						
-						self._stack[-1].append(outlineNode)
-						self._stack.append(outlineNode)
-					except IndexError:
-						issue_handler.issue(Issue("Malformed structure command", node.start, node.end, node.file, Issue.SEVERITY_ERROR))
-				
-				elif node.value == "label":
-					try:
-						value = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-	
-						if value in self._labelCache.keys():
-							start, end = self._labelCache[value]
-							issue_handler.issue(Issue("Label <b>%s</b> has already been defined" % value, start, end, node.file, Issue.SEVERITY_ERROR))
-						else:
-							self._labelCache[value] = (node.start, node.lastEnd)
-						
-							labelNode = OutlineNode(OutlineNode.LABEL, node.start, node.lastEnd, value, foreign=foreign, file=node.file)
-	
-							self._outline.labels.append(labelNode)
-							if self.cfgLabelsInTree:
-								self._stack[-1].append(labelNode)
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-				
-#				elif node.value == "begin":
-#					environment = str(node.filter(Node.MANDATORY_ARGUMENT)[0][0])
-#					if environment == "lstlisting":
-#						# look for label in listing environment
-#						try:
-#							# TODO: Node should have a method like toDict() or something
-#							optionNode = node.filter(Node.OPTIONAL_ARGUMENT)[0]
-#							option = "".join([str(child) for child in optionNode])
-#							for pair in option.split(","):
-#								key, value = pair.split("=")
-#								if key.strip() == "label":
-#									labelNode = OutlineNode(OutlineNode.LABEL, node.start, node.end, value.strip())
-#									outline.labels.append(labelNode)
-#									if self.cfgLabelsInTree:
-#										stack[-1].append(labelNode)
-#						except IndexError:
-#							pass
-				
-				elif node.value == "usepackage":
-					try:
-						package = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						packageNode = OutlineNode(OutlineNode.PACKAGE, node.start, node.lastEnd, package, file=node.file)
-						self._outline.packages.append(packageNode)
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.end, node.file, Issue.SEVERITY_ERROR))
-				
-				elif self.cfgTablesInTree and node.value == "begin":
-					try:
-						environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						if environ == "tabular":
-							tableNode = OutlineNode(OutlineNode.TABLE, node.start, node.lastEnd, "", foreign=foreign, file=node.file)
-							self._stack[-1].append(tableNode)
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-				
-				elif self.cfgGraphicsInTree and node.value == "includegraphics":
-					try:
-						target = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						graphicsNode = OutlineNode(OutlineNode.GRAPHICS, node.start, node.lastEnd, target, foreign=foreign, file=node.file)
-						self._stack[-1].append(graphicsNode)
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-				
-				elif node.value == "bibliography":
-					try:
-						value = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						for bib in value.split(","):
-							self._outline.bibliographies.append(File("%s/%s.bib" % (node.file.dirname, bib)))
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-						
-				elif node.value == "definecolor" or node.value == "xdefinecolor":
-					try:
-						name = str(node.firstOfType(Node.MANDATORY_ARGUMENT)[0])
-						self._outline.colors.append(name)
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-						
-				elif node.value == "newcommand":
-					try:
-						name = str(node.firstOfType(Node.MANDATORY_ARGUMENT)[0])[1:]	# remove "\"
-						try:
-							nArgs = int(node.filter(Node.OPTIONAL_ARGUMENT)[0].innerText)
-						except IndexError:
-							nArgs = 0
-						except Exception:
-							issue_handler.issue(Issue("Malformed newcommand", node.start, node.end, node.file, Issue.SEVERITY_ERROR))
-							nArgs = 0
-						ncNode = OutlineNode(OutlineNode.NEWCOMMAND, node.start, node.lastEnd, name, numOfArgs=nArgs, file=node.file)
-						self._outline.newcommands.append(ncNode)
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-					
-					# don't walk through \newcommand
-					continue
-				
-				elif node.value in ["newenvironment", "newtheorem"]:
-					try:
-						name = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						#try:
-						#	n_args = int(node.firstOfType(Node.OPTIONAL_ARGUMENT).innerText)
-						#except IndexError:
-						#	n_args = 0
-						ne_node = OutlineNode(OutlineNode.NEWENVIRONMENT, node.start, node.lastEnd, name, numOfArgs=0, file=node.file)
-						self._outline.newenvironments.append(ne_node)
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-				
-				elif node.value == "include" or node.value == "input":
-					childForeign = True
-			
-			self._walk(node, issue_handler, childForeign)
-
-	#~ def __del__(self):
-		#~ print "Properly destroyed %s" % self
-
-
-	
-	
-	
\ No newline at end of file
+
+    _log = getLogger("LaTeXOutlineGenerator")
+
+    # TODO: foreign flag is not necessary
+
+    _STRUCTURE_LEVELS = { "part" : 1, "part*" : 1,
+                          "chapter" : 2, "chapter*" : 2,
+                          "section" : 3, "section*" : 3,
+                          "subsection" : 4, "subsection*" : 4,
+                          "subsubsection" : 5, "subsubsection*" : 5,
+                          "paragraph" : 6,
+                          "subparagraph" : 7 }
+
+#    def __init__(self):
+#        # TODO: read config
+#        self.cfgLabelsInTree = True
+#        self.cfgTablesInTree = True
+#        self.cfgGraphicsInTree = True
+
+    def generate(self, documentNode, issue_handler):
+        """
+        Generates an outline model from a document model and returns a list
+        of list of issues if some occured.
+        """
+
+        # setup
+        self.cfgLabelsInTree = Preferences().get_bool("outline-show-labels")
+        self.cfgTablesInTree = Preferences().get_bool("outline-show-tables")
+        self.cfgGraphicsInTree = Preferences().get_bool("outline-show-graphics")
+
+        self._outline = Outline()
+        self._stack = [self._outline.rootNode]
+
+        self._labelCache = {}
+
+#        self._file = documentNode.value        # this is updated when a DOCUMENT occurs
+
+        self._walk(documentNode, issue_handler)
+
+        return self._outline
+
+    def _walk(self, parentNode, issue_handler, foreign=False):
+        """
+        Recursively walk a node in the document model
+
+        foreign        if True this node is a child of a reference node, so it's coming
+                    from an expanded reference
+        """
+
+        childForeign = foreign
+
+        for node in parentNode:
+#            if node.type == Node.DOCUMENT:
+#                self._file = node.value
+            if node.type == Node.COMMAND:
+                if node.value in self._STRUCTURE_LEVELS.keys():
+                    try:
+                        headline = node.firstOfType(Node.MANDATORY_ARGUMENT).innerMarkup
+                        level = self._STRUCTURE_LEVELS[node.value]
+                        outlineNode = OutlineNode(OutlineNode.STRUCTURE, node.start, node.lastEnd, headline, level, foreign, file=node.file)
+
+                        while self._stack[-1].level >= level:
+                            self._stack.pop()
+
+                        self._stack[-1].append(outlineNode)
+                        self._stack.append(outlineNode)
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed structure command", node.start, node.end, node.file, Issue.SEVERITY_ERROR))
+
+                elif node.value == "label":
+                    try:
+                        value = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+
+                        if value in self._labelCache.keys():
+                            start, end = self._labelCache[value]
+                            issue_handler.issue(Issue("Label <b>%s</b> has already been defined" % value, start, end, node.file, Issue.SEVERITY_ERROR))
+                        else:
+                            self._labelCache[value] = (node.start, node.lastEnd)
+
+                            labelNode = OutlineNode(OutlineNode.LABEL, node.start, node.lastEnd, value, foreign=foreign, file=node.file)
+
+                            self._outline.labels.append(labelNode)
+                            if self.cfgLabelsInTree:
+                                self._stack[-1].append(labelNode)
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+#                elif node.value == "begin":
+#                    environment = str(node.filter(Node.MANDATORY_ARGUMENT)[0][0])
+#                    if environment == "lstlisting":
+#                        # look for label in listing environment
+#                        try:
+#                            # TODO: Node should have a method like toDict() or something
+#                            optionNode = node.filter(Node.OPTIONAL_ARGUMENT)[0]
+#                            option = "".join([str(child) for child in optionNode])
+#                            for pair in option.split(","):
+#                                key, value = pair.split("=")
+#                                if key.strip() == "label":
+#                                    labelNode = OutlineNode(OutlineNode.LABEL, node.start, node.end, value.strip())
+#                                    outline.labels.append(labelNode)
+#                                    if self.cfgLabelsInTree:
+#                                        stack[-1].append(labelNode)
+#                        except IndexError:
+#                            pass
+
+                elif node.value == "usepackage":
+                    try:
+                        package = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        packageNode = OutlineNode(OutlineNode.PACKAGE, node.start, node.lastEnd, package, file=node.file)
+                        self._outline.packages.append(packageNode)
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.end, node.file, Issue.SEVERITY_ERROR))
+
+                elif self.cfgTablesInTree and node.value == "begin":
+                    try:
+                        environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        if environ == "tabular":
+                            tableNode = OutlineNode(OutlineNode.TABLE, node.start, node.lastEnd, "", foreign=foreign, file=node.file)
+                            self._stack[-1].append(tableNode)
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif self.cfgGraphicsInTree and node.value == "includegraphics":
+                    try:
+                        target = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        graphicsNode = OutlineNode(OutlineNode.GRAPHICS, node.start, node.lastEnd, target, foreign=foreign, file=node.file)
+                        self._stack[-1].append(graphicsNode)
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif node.value == "bibliography":
+                    try:
+                        value = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        for bib in value.split(","):
+                            self._outline.bibliographies.append(File("%s/%s.bib" % (node.file.dirname, bib)))
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif node.value == "definecolor" or node.value == "xdefinecolor":
+                    try:
+                        name = str(node.firstOfType(Node.MANDATORY_ARGUMENT)[0])
+                        self._outline.colors.append(name)
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif node.value == "newcommand":
+                    try:
+                        name = str(node.firstOfType(Node.MANDATORY_ARGUMENT)[0])[1:]    # remove "\"
+                        try:
+                            nArgs = int(node.filter(Node.OPTIONAL_ARGUMENT)[0].innerText)
+                        except IndexError:
+                            nArgs = 0
+                        except Exception:
+                            issue_handler.issue(Issue("Malformed newcommand", node.start, node.end, node.file, Issue.SEVERITY_ERROR))
+                            nArgs = 0
+                        ncNode = OutlineNode(OutlineNode.NEWCOMMAND, node.start, node.lastEnd, name, numOfArgs=nArgs, file=node.file)
+                        self._outline.newcommands.append(ncNode)
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                    # don't walk through \newcommand
+                    continue
+
+                elif node.value in ["newenvironment", "newtheorem"]:
+                    try:
+                        name = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        #try:
+                        #    n_args = int(node.firstOfType(Node.OPTIONAL_ARGUMENT).innerText)
+                        #except IndexError:
+                        #    n_args = 0
+                        ne_node = OutlineNode(OutlineNode.NEWENVIRONMENT, node.start, node.lastEnd, name, numOfArgs=0, file=node.file)
+                        self._outline.newenvironments.append(ne_node)
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif node.value == "include" or node.value == "input":
+                    childForeign = True
+
+            self._walk(node, issue_handler, childForeign)
+
+    #~ def __del__(self):
+        #~ print "Properly destroyed %s" % self
+
+
+
+
diff --git a/latex/latex/parser.py b/latex/latex/parser.py
index b93e7c7..fe04374 100644
--- a/latex/latex/parser.py
+++ b/latex/latex/parser.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -34,678 +34,677 @@ from ..issues import Issue
 
 
 class Node(list):
-	"""
-	This is the base class of the LaTeX object model
-	"""
-	
-	DOCUMENT, COMMAND, MANDATORY_ARGUMENT, OPTIONAL_ARGUMENT, TEXT, EMBRACED = range(6)
-	
-	def __init__(self, type, value=None):
-		self.type = type
-		self.value = value
-		self.parent = None
-		
-		# this indicates if an argument is closed or not
-		# (only used by the PrefixParser)
-		self.closed = False
-	
-	def firstOfType(self, type):
-		"""
-		Return the first child node of a given type
-		"""
-		for node in self:
-			if node.type == type:
-				return node
-		raise IndexError
-	
-	def filter(self, type):
-		"""
-		Return all child nodes of this node having a certain type
-		"""
-		return [node for node in self if node.type == type]
-	
-	@property
-	def xml(self):
-		"""
-		Return an XML representation of this node (for debugging)
-		"""
-		if self.type == self.COMMAND:
-			content = "".join([node.xml for node in self])
-			if len(content):
-				return "<command name=\"%s\">%s</command>" % (escape(self.value), content)
-			else:
-				return "<command name=\"%s\" />" % escape(self.value)
-		elif self.type == self.MANDATORY_ARGUMENT:
-			return "<mandatory>%s</mandatory>" % "".join([node.xml for node in self])
-		elif self.type == self.OPTIONAL_ARGUMENT:
-			return "<optional>%s</optional>" % "".join([node.xml for node in self])
-		elif self.type == self.TEXT:
-			return escape(self.value)
-		elif self.type == self.DOCUMENT:
-			return "<document>%s</document>" % "".join([node.xml for node in self])
-		elif self.type == self.EMBRACED:
-			return "<embraced>%s</embraced>" % "".join([node.xml for node in self])
-	
-	@property
-	def xmlPrefix(self):
-		"""
-		Return an XML representation of this node (for debugging)
-		
-		This is for the prefix mode, so we print if the arguments are closed or not
-		"""
-		if self.type == self.COMMAND:
-			content = "".join([node.xmlPrefix for node in self])
-			if len(content):
-				return "<command name=\"%s\">%s</command>" % (self.value, content)
-			else:
-				return "<command name=\"%s\" />" % self.value
-		elif self.type == self.MANDATORY_ARGUMENT:
-			return "<mandatory closed=%s>%s</mandatory>" % (self.closed, "".join([node.xmlPrefix for node in self]))
-		elif self.type == self.OPTIONAL_ARGUMENT:
-			return "<optional closed=%s>%s</optional>" % (self.closed, "".join([node.xmlPrefix for node in self]))
-		elif self.type == self.TEXT:
-			return escape(self.value)
-		elif self.type == self.DOCUMENT:
-			return "<document>%s</document>" % "".join([node.xmlPrefix for node in self])
-		elif self.type == self.EMBRACED:
-			return "<embraced>%s</embraced>" % "".join([node.xmlPrefix for node in self])
-	
-	def __str__(self):
-		"""
-		Return the original LaTeX representation of this node
-		"""
-		if self.type == self.COMMAND:
-			return "\\%s%s" % (self.value, "".join([str(node) for node in self]))
-		elif self.type == self.MANDATORY_ARGUMENT or self.type == self.EMBRACED:
-			return "{%s}" % "".join([str(node) for node in self])
-		elif self.type == self.OPTIONAL_ARGUMENT:
-			return "[%s]" % "".join([str(node) for node in self])
-		elif self.type == self.TEXT:
-			return self.value
-		elif self.type == self.DOCUMENT:
-			return "".join([str(node) for node in self])
-	
-	@property
-	def innerText(self):
-		"""
-		Return the concatenated values of all TEXT child nodes
-		"""
-		return "".join([child.value for child in self if child.type == Node.TEXT])
-	
-	@property
-	def markup(self):
-		"""
-		Return the concatenated markup values of this node and all child nodes
-		"""
-		if self.type == self.COMMAND:
-			return "<span color=\"grey\">\\%s</span>%s" % (escape(self.value), "".join([node.markup for node in self]))
-		elif self.type == self.MANDATORY_ARGUMENT or self.type == self.EMBRACED:
-			return "<span color=\"grey\">{</span>%s<span color=\"grey\">}</span>" % "".join([node.markup for node in self])
-		elif self.type == self.OPTIONAL_ARGUMENT:
-			return "<span color=\"grey\">[</span>%s<span color=\"grey\">]</span>" % "".join([node.markup for node in self])
-		elif self.type == self.TEXT:
-			return escape(self.value)
-		elif self.type == self.DOCUMENT:
-			return "".join([node.markup for node in self])
-	
-	@property
-	def innerMarkup(self):
-		"""
-		Return the concatenated markup values of only the child nodes
-		"""
-		return "".join([node.markup for node in self])
-	
-	def append(self, node):
-		"""
-		Append a child node and store a back-reference
-		"""
-		node.parent = self
-		list.append(self, node)
-	
-	def find(self, value):
-		"""
-		Find child node with given value (recursive, so grand-children are found, too)
-		"""
-		# TODO
-		
-	def destroy(self):
-		# This is important, python cannot automatically free this 
-		# object because of cyclic references (it is doubly linked)
-		self.parent = None
-		for child in self:
-			child.destroy()
-		del self[:]
-		
-	#~ def __del__(self):
-		#~ print "Properly destroyed %s" % self
-		
-		
+    """
+    This is the base class of the LaTeX object model
+    """
+
+    DOCUMENT, COMMAND, MANDATORY_ARGUMENT, OPTIONAL_ARGUMENT, TEXT, EMBRACED = range(6)
+
+    def __init__(self, type, value=None):
+        self.type = type
+        self.value = value
+        self.parent = None
+
+        # this indicates if an argument is closed or not
+        # (only used by the PrefixParser)
+        self.closed = False
+
+    def firstOfType(self, type):
+        """
+        Return the first child node of a given type
+        """
+        for node in self:
+            if node.type == type:
+                return node
+        raise IndexError
+
+    def filter(self, type):
+        """
+        Return all child nodes of this node having a certain type
+        """
+        return [node for node in self if node.type == type]
+
+    @property
+    def xml(self):
+        """
+        Return an XML representation of this node (for debugging)
+        """
+        if self.type == self.COMMAND:
+            content = "".join([node.xml for node in self])
+            if len(content):
+                return "<command name=\"%s\">%s</command>" % (escape(self.value), content)
+            else:
+                return "<command name=\"%s\" />" % escape(self.value)
+        elif self.type == self.MANDATORY_ARGUMENT:
+            return "<mandatory>%s</mandatory>" % "".join([node.xml for node in self])
+        elif self.type == self.OPTIONAL_ARGUMENT:
+            return "<optional>%s</optional>" % "".join([node.xml for node in self])
+        elif self.type == self.TEXT:
+            return escape(self.value)
+        elif self.type == self.DOCUMENT:
+            return "<document>%s</document>" % "".join([node.xml for node in self])
+        elif self.type == self.EMBRACED:
+            return "<embraced>%s</embraced>" % "".join([node.xml for node in self])
+
+    @property
+    def xmlPrefix(self):
+        """
+        Return an XML representation of this node (for debugging)
+
+        This is for the prefix mode, so we print if the arguments are closed or not
+        """
+        if self.type == self.COMMAND:
+            content = "".join([node.xmlPrefix for node in self])
+            if len(content):
+                return "<command name=\"%s\">%s</command>" % (self.value, content)
+            else:
+                return "<command name=\"%s\" />" % self.value
+        elif self.type == self.MANDATORY_ARGUMENT:
+            return "<mandatory closed=%s>%s</mandatory>" % (self.closed, "".join([node.xmlPrefix for node in self]))
+        elif self.type == self.OPTIONAL_ARGUMENT:
+            return "<optional closed=%s>%s</optional>" % (self.closed, "".join([node.xmlPrefix for node in self]))
+        elif self.type == self.TEXT:
+            return escape(self.value)
+        elif self.type == self.DOCUMENT:
+            return "<document>%s</document>" % "".join([node.xmlPrefix for node in self])
+        elif self.type == self.EMBRACED:
+            return "<embraced>%s</embraced>" % "".join([node.xmlPrefix for node in self])
+
+    def __str__(self):
+        """
+        Return the original LaTeX representation of this node
+        """
+        if self.type == self.COMMAND:
+            return "\\%s%s" % (self.value, "".join([str(node) for node in self]))
+        elif self.type == self.MANDATORY_ARGUMENT or self.type == self.EMBRACED:
+            return "{%s}" % "".join([str(node) for node in self])
+        elif self.type == self.OPTIONAL_ARGUMENT:
+            return "[%s]" % "".join([str(node) for node in self])
+        elif self.type == self.TEXT:
+            return self.value
+        elif self.type == self.DOCUMENT:
+            return "".join([str(node) for node in self])
+
+    @property
+    def innerText(self):
+        """
+        Return the concatenated values of all TEXT child nodes
+        """
+        return "".join([child.value for child in self if child.type == Node.TEXT])
+
+    @property
+    def markup(self):
+        """
+        Return the concatenated markup values of this node and all child nodes
+        """
+        if self.type == self.COMMAND:
+            return "<span color=\"grey\">\\%s</span>%s" % (escape(self.value), "".join([node.markup for node in self]))
+        elif self.type == self.MANDATORY_ARGUMENT or self.type == self.EMBRACED:
+            return "<span color=\"grey\">{</span>%s<span color=\"grey\">}</span>" % "".join([node.markup for node in self])
+        elif self.type == self.OPTIONAL_ARGUMENT:
+            return "<span color=\"grey\">[</span>%s<span color=\"grey\">]</span>" % "".join([node.markup for node in self])
+        elif self.type == self.TEXT:
+            return escape(self.value)
+        elif self.type == self.DOCUMENT:
+            return "".join([node.markup for node in self])
+
+    @property
+    def innerMarkup(self):
+        """
+        Return the concatenated markup values of only the child nodes
+        """
+        return "".join([node.markup for node in self])
+
+    def append(self, node):
+        """
+        Append a child node and store a back-reference
+        """
+        node.parent = self
+        list.append(self, node)
+
+    def find(self, value):
+        """
+        Find child node with given value (recursive, so grand-children are found, too)
+        """
+        # TODO
+
+    def destroy(self):
+        # This is important, python cannot automatically free this
+        # object because of cyclic references (it is doubly linked)
+        self.parent = None
+        for child in self:
+            child.destroy()
+        del self[:]
+
+    #~ def __del__(self):
+        #~ print "Properly destroyed %s" % self
+
+
 
 class Document(Node):
-	"""
-	An extended Node with special methods for a LaTeX document
-	"""
-	
-	# FIXME: doesn't extend LocalizedNode which leads to
-	# https://sourceforge.net/tracker/index.php?func=detail&aid=2899795&group_id=204144&atid=988428
-	
-	# TODO: implement a generic interface for searching the document model
-	
-	def __init__(self, file):
-		Node.__init__(self, Node.DOCUMENT, file)
-		
-		self._is_master_called = False
-		self._is_master = False
-		
-		self._end_of_document = None
-		self._end_of_packages = None
-	
-	def _do_is_master(self):
-		# TODO: this should be recursive
-		
-		for node in self:
-			if node.type == Node.COMMAND and node.value == "begin":
-				if node.firstOfType(Node.MANDATORY_ARGUMENT).innerText == "document":
-					return True
-		return False
-	
-	@property
-	def is_master(self):
-		"""
-		@return: True if this document is a master document
-		"""
-		
-		# TODO: determine this while parsing
-		
-		if not self._is_master_called:
-			self._is_master = self._do_is_master()
-			self._is_master_called = True
-		return self._is_master
-	
-	def _find_end_of_document(self):
-		# TODO: this should be recursive
-		
-		for node in self:
-			if node.type == Node.COMMAND and node.value == "end":
-				if node.firstOfType(Node.MANDATORY_ARGUMENT).innerText == "document":
-					return node.start
-		return 0
-	
-	@property
-	def end_of_document(self):
-		"""
-		Return the offset right before \end{document}
-		
-		used by LaTeXEditor.insert_at_position
-		"""
-		if self._end_of_document is None:
-			self._end_of_document = self._find_end_of_document()
-		return self._end_of_document
-	
-	def _find_end_of_packages(self):
-		"""
-		@raise IndexError: if not found
-		"""
-		# TODO: this should be recursive
-		
-		offset = None
-		for node in self:
-			if node.type == Node.COMMAND and node.value == "usepackage":
-				offset = node.lastEnd
-		if offset is None:
-			raise IndexError
-		return offset
-	
-	def _find_begin_of_preamble(self):
-		# TODO: this should be recursive
-		
-		offset = 0
-		for node in self:
-			if node.type == Node.COMMAND and node.value == "documentclass":
-				offset = node.lastEnd
-		return offset
-	
-	@property
-	def end_of_packages(self):
-		"""
-		Return the offset right after the last \usepackage
-		
-		used by LaTeXEditor.insert_at_position
-		"""
-		if self._end_of_packages is None:
-			try:
-				self._end_of_packages = self._find_end_of_packages()
-			except IndexError:
-				self._end_of_packages = self._find_begin_of_preamble()
-			
-		return self._end_of_packages
-		
+    """
+    An extended Node with special methods for a LaTeX document
+    """
+
+    # FIXME: doesn't extend LocalizedNode which leads to
+    # https://sourceforge.net/tracker/index.php?func=detail&aid=2899795&group_id=204144&atid=988428
+
+    # TODO: implement a generic interface for searching the document model
+
+    def __init__(self, file):
+        Node.__init__(self, Node.DOCUMENT, file)
+
+        self._is_master_called = False
+        self._is_master = False
+
+        self._end_of_document = None
+        self._end_of_packages = None
+
+    def _do_is_master(self):
+        # TODO: this should be recursive
+
+        for node in self:
+            if node.type == Node.COMMAND and node.value == "begin":
+                if node.firstOfType(Node.MANDATORY_ARGUMENT).innerText == "document":
+                    return True
+        return False
+
+    @property
+    def is_master(self):
+        """
+        @return: True if this document is a master document
+        """
+
+        # TODO: determine this while parsing
+
+        if not self._is_master_called:
+            self._is_master = self._do_is_master()
+            self._is_master_called = True
+        return self._is_master
+
+    def _find_end_of_document(self):
+        # TODO: this should be recursive
+
+        for node in self:
+            if node.type == Node.COMMAND and node.value == "end":
+                if node.firstOfType(Node.MANDATORY_ARGUMENT).innerText == "document":
+                    return node.start
+        return 0
+
+    @property
+    def end_of_document(self):
+        """
+        Return the offset right before \end{document}
+
+        used by LaTeXEditor.insert_at_position
+        """
+        if self._end_of_document is None:
+            self._end_of_document = self._find_end_of_document()
+        return self._end_of_document
+
+    def _find_end_of_packages(self):
+        """
+        @raise IndexError: if not found
+        """
+        # TODO: this should be recursive
+
+        offset = None
+        for node in self:
+            if node.type == Node.COMMAND and node.value == "usepackage":
+                offset = node.lastEnd
+        if offset is None:
+            raise IndexError
+        return offset
+
+    def _find_begin_of_preamble(self):
+        # TODO: this should be recursive
+
+        offset = 0
+        for node in self:
+            if node.type == Node.COMMAND and node.value == "documentclass":
+                offset = node.lastEnd
+        return offset
+
+    @property
+    def end_of_packages(self):
+        """
+        Return the offset right after the last \usepackage
+
+        used by LaTeXEditor.insert_at_position
+        """
+        if self._end_of_packages is None:
+            try:
+                self._end_of_packages = self._find_end_of_packages()
+            except IndexError:
+                self._end_of_packages = self._find_begin_of_preamble()
+
+        return self._end_of_packages
+
 
 class LocalizedNode(Node):
-	"""
-	This Node type holds the start and end offsets of the substring it belongs to
-	in the source
-	"""
-	def __init__(self, type, start, end, value=None, file=None):
-		Node.__init__(self, type, value)
-		self.start = start
-		self.end = end
-		self.file = file
-	
-	@property
-	def lastEnd(self):
-		"""
-		Return the end of the last child node or of this node if
-		it doesn't have children.
-		"""
-		try:
-			return self[-1].end
-		except IndexError:
-			return self.end
-		except AttributeError:
-			# AttributeError: 'Document' object has no attribute 'end'
-			# FIXME: why can this happen?
-			return self.end
-				
+    """
+    This Node type holds the start and end offsets of the substring it belongs to
+    in the source
+    """
+    def __init__(self, type, start, end, value=None, file=None):
+        Node.__init__(self, type, value)
+        self.start = start
+        self.end = end
+        self.file = file
+
+    @property
+    def lastEnd(self):
+        """
+        Return the end of the last child node or of this node if
+        it doesn't have children.
+        """
+        try:
+            return self[-1].end
+        except IndexError:
+            return self.end
+        except AttributeError:
+            # AttributeError: 'Document' object has no attribute 'end'
+            # FIXME: why can this happen?
+            return self.end
+
 
 class FatalParseException(Exception):
-	"""
-	This raised of the Parser faces a fatal error and cannot continue
-	"""
+    """
+    This raised of the Parser faces a fatal error and cannot continue
+    """
 
 
 from lexer import Lexer, Token
 
 
 class LaTeXParser(object):
-	"""
-	A tree parser building an object model of nodes
-	"""
-	
-	# TODO: remove second parse method
-	
-	@verbose
-	def _parse(self, string, documentNode, file, issue_handler):
-		"""
-		@deprecated: use parse_string() and issues() instead
-		"""
-		
-		self._stack = [documentNode]
-		
-		callables = {
-				Token.COMMAND : self.command, 
-				Token.TEXT : self.text, 
-				Token.BEGIN_CURLY : self.beginCurly,
-				Token.END_CURLY : self.endCurly, 
-				Token.BEGIN_SQUARE : self.beginSquare, 
-				Token.END_SQUARE : self.endSquare,
-				Token.COMMENT : self.comment, 
-				Token.VERBATIM : self.verbatim }
-		
-		try:
-			for token in Lexer(string):
-				callables[token.type].__call__(token.value, token.offset, file, issue_handler)
-		except FatalParseException:
-			return
-		
-		# check stack remainder
-		for node in self._stack:
-			if node.type == Node.MANDATORY_ARGUMENT or node.type == Node.EMBRACED:
-				issue_handler.issue(Issue("Unclosed {", node.start, node.start + 1, file, Issue.SEVERITY_ERROR))
-			elif node.type == Node.OPTIONAL_ARGUMENT:
-				issue_handler.issue(Issue("Unclosed [", node.start, node.start + 1, file, Issue.SEVERITY_ERROR))
-	
-	def parse(self, string, file, issue_handler):
-		"""
-		@param string: LaTeX source
-		@param from_filename: filename from where the source is read (this is used to tag
-				parts of the model)
-				
-		@rtype: Document
-		"""
-		document_node = Document(file)
-		self._parse(string, document_node, file, issue_handler)
-		
-		return document_node
-	
-	# TODO: rename methods from "command()" to "_on_command()"
-	
-	def command(self, value, offset, file, issue_handler):
-		top = self._stack[-1]
-		
-		if top.type == Node.DOCUMENT \
-				or top.type == Node.MANDATORY_ARGUMENT \
-				or top.type == Node.OPTIONAL_ARGUMENT \
-				or top.type == Node.EMBRACED:
-			node = LocalizedNode(Node.COMMAND, offset, offset + len(value) + 1, value, file)
-			top.append(node)
-			self._stack.append(node)
-			
-		elif top.type == Node.COMMAND \
-				or top.type == Node.TEXT:
-			try:
-				self._stack.pop()
-				self.command(value, offset, file, issue_handler)
-			except IndexError:
-				issue_handler.issue(Issue("Undefined Parse Error", offset, offset + 1, file, Issue.SEVERITY_ERROR))
-	
-	def text(self, value, offset, file, issue_handler):
-		top = self._stack[-1]
-
-		if top.type == Node.DOCUMENT \
-				or top.type == Node.MANDATORY_ARGUMENT \
-				or top.type == Node.OPTIONAL_ARGUMENT \
-				or top.type == Node.EMBRACED:
-			node = LocalizedNode(Node.TEXT, offset, offset + len(value), value, file)
-			top.append(node)
-			self._stack.append(node)
-			
-		elif top.type == Node.COMMAND:
-			self._stack.pop()
-			self.text(value, offset, file, issue_handler)
-			
-		elif top.type == Node.TEXT:
-			try:
-				self._stack.pop()
-				self.text(value, offset, file, issue_handler)
-			except IndexError:
-				issue_handler.issue(Issue("Undefined Parse Error", offset, offset + 1, file, Issue.SEVERITY_ERROR))
-		else:
-			# TODO: possible?
-			issue_handler.issue(Issue("Unexpected TEXT token with %s on stack" % top.type, offset, offset + 1, file, Issue.SEVERITY_ERROR))
-	
-	def beginCurly(self, value, offset, file, issue_handler):
-		top = self._stack[-1]
-		
-		if top.type == Node.COMMAND:
-			node = LocalizedNode(Node.MANDATORY_ARGUMENT, offset, offset + 1, file=file)
-			top.append(node)
-			self._stack.append(node)
-		
-		elif top.type == Node.DOCUMENT \
-				or top.type == Node.MANDATORY_ARGUMENT \
-				or top.type == Node.OPTIONAL_ARGUMENT \
-				or top.type == Node.EMBRACED:
-			node = LocalizedNode(Node.EMBRACED, offset, offset + 1, file=file)
-			top.append(node)
-			self._stack.append(node)
-		
-		elif top.type == Node.TEXT:
-			try:
-				self._stack.pop()
-				self.beginCurly(value, offset, file, issue_handler)
-			except IndexError:
-				issue_handler.issue(Issue("Undefined Parse Error", offset, offset + 1, file, Issue.SEVERITY_ERROR))
-		else:
-			# TODO: possible?
-			issue_handler.issue(Issue("Unexpected BEGIN_CURLY token with %s on stack" % top.type, offset, offset + 1, file, Issue.SEVERITY_ERROR))
-	
-	def endCurly(self, value, offset, file, issue_handler):
-		try:
-			# pop from stack until MANDATORY_ARGUMENT or EMBRACED
-			while True:
-				top = self._stack[-1]
-				if top.type == Node.MANDATORY_ARGUMENT or top.type == Node.EMBRACED:
-					node = self._stack.pop()
-					break
-				self._stack.pop()
-				
-			# set end offset of MANDATORY_ARGUMENT or EMBRACED
-			node.end = offset + 1
-		except IndexError:
-			issue_handler.issue(Issue("Encountered <b>}</b> without <b>{</b>", offset, offset + 1, file, Issue.SEVERITY_ERROR))
-			# we cannot continue after that
-			raise FatalParseException
-	
-	def beginSquare(self, value, offset, file, issue_handler):
-		top = self._stack[-1]
-		if top.type == Node.COMMAND:
-			node = LocalizedNode(Node.OPTIONAL_ARGUMENT, offset, offset + 1, file=file)
-			top.append(node)
-			self._stack.append(node)
-		
-		elif top.type == Node.TEXT:
-			top.value += "["
-		
-		elif top.type == Node.MANDATORY_ARGUMENT:
-			node = LocalizedNode(Node.TEXT, offset, offset + 1, "[", file)
-			top.append(node)
-			self._stack.append(node)
-		
-		else:
-			issue_handler.issue(Issue("Unexpected BEGIN_SQUARE token with %s on stack" % top.type, offset, offset + 1, file, Issue.SEVERITY_ERROR))
-	
-	def endSquare(self, value, offset, file, issue_handler):
-		try:
-			node = [node for node in self._stack if node.type == Node.OPTIONAL_ARGUMENT][-1]
-			
-			# open optional argument found 
-			# this square closes it, so pop stack until there
-			
-			while self._stack[-1].type != Node.OPTIONAL_ARGUMENT:
-				self._stack.pop()
-			node = self._stack.pop()
-			node.end = offset + 1
-			
-		except IndexError:
-			# no open optional argument, so this "]" is TEXT
-			
-			top = self._stack[-1]
-			if top.type == Node.TEXT:
-				top.value += "]"
-				
-			elif top.type == Node.COMMAND:
-				try:
-					self._stack.pop()
-					self.endSquare(value, offset, file, issue_handler)
-				except IndexError:
-					issue_handler.issue(Issue("Undefined Parse Error", offset, offset + 1, file, Issue.SEVERITY_ERROR))
-			
-			elif top.type == Node.MANDATORY_ARGUMENT or top.type == Node.DOCUMENT or top.type == Node.OPTIONAL_ARGUMENT:
-				node = LocalizedNode(Node.TEXT, offset, offset + 1, "]", file)
-				top.append(node)
-				self._stack.append(node)
-			
-			else:
-				issue_handler.issue(Issue("Unexpected END_SQUARE token with %s on stack and no optional argument" % top.type, offset, offset + 1, file, Issue.SEVERITY_ERROR))
-	
-	_PATTERN = compile("(TODO|FIXME)\w?\:?(?P<text>.*)")
-	
-	def comment(self, value, offset, file, issue_handler):
-		"""
-		Extract TODOs and FIXMEs
-		"""
-		match = self._PATTERN.search(value)
-		if match:
-			text = match.group("text").strip()
-			# TODO: escape
-			issue_handler.issue(Issue(text, offset + match.start(), offset + match.end() + 1, file, Issue.SEVERITY_TASK))
-		
-	def verbatim(self, value, offset, file, issue_handler):
-		pass
-		
-	#~ def __del__(self):
-		#~ print "Properly destroyed %s" % self
+    """
+    A tree parser building an object model of nodes
+    """
+
+    # TODO: remove second parse method
+
+    @verbose
+    def _parse(self, string, documentNode, file, issue_handler):
+        """
+        @deprecated: use parse_string() and issues() instead
+        """
+
+        self._stack = [documentNode]
+
+        callables = {
+                Token.COMMAND : self.command,
+                Token.TEXT : self.text,
+                Token.BEGIN_CURLY : self.beginCurly,
+                Token.END_CURLY : self.endCurly,
+                Token.BEGIN_SQUARE : self.beginSquare,
+                Token.END_SQUARE : self.endSquare,
+                Token.COMMENT : self.comment,
+                Token.VERBATIM : self.verbatim }
+
+        try:
+            for token in Lexer(string):
+                callables[token.type].__call__(token.value, token.offset, file, issue_handler)
+        except FatalParseException:
+            return
+
+        # check stack remainder
+        for node in self._stack:
+            if node.type == Node.MANDATORY_ARGUMENT or node.type == Node.EMBRACED:
+                issue_handler.issue(Issue("Unclosed {", node.start, node.start + 1, file, Issue.SEVERITY_ERROR))
+            elif node.type == Node.OPTIONAL_ARGUMENT:
+                issue_handler.issue(Issue("Unclosed [", node.start, node.start + 1, file, Issue.SEVERITY_ERROR))
+
+    def parse(self, string, file, issue_handler):
+        """
+        @param string: LaTeX source
+        @param from_filename: filename from where the source is read (this is used to tag
+                parts of the model)
+
+        @rtype: Document
+        """
+        document_node = Document(file)
+        self._parse(string, document_node, file, issue_handler)
+
+        return document_node
+
+    # TODO: rename methods from "command()" to "_on_command()"
+
+    def command(self, value, offset, file, issue_handler):
+        top = self._stack[-1]
+
+        if top.type == Node.DOCUMENT \
+                or top.type == Node.MANDATORY_ARGUMENT \
+                or top.type == Node.OPTIONAL_ARGUMENT \
+                or top.type == Node.EMBRACED:
+            node = LocalizedNode(Node.COMMAND, offset, offset + len(value) + 1, value, file)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.COMMAND \
+                or top.type == Node.TEXT:
+            try:
+                self._stack.pop()
+                self.command(value, offset, file, issue_handler)
+            except IndexError:
+                issue_handler.issue(Issue("Undefined Parse Error", offset, offset + 1, file, Issue.SEVERITY_ERROR))
+
+    def text(self, value, offset, file, issue_handler):
+        top = self._stack[-1]
+
+        if top.type == Node.DOCUMENT \
+                or top.type == Node.MANDATORY_ARGUMENT \
+                or top.type == Node.OPTIONAL_ARGUMENT \
+                or top.type == Node.EMBRACED:
+            node = LocalizedNode(Node.TEXT, offset, offset + len(value), value, file)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.COMMAND:
+            self._stack.pop()
+            self.text(value, offset, file, issue_handler)
+
+        elif top.type == Node.TEXT:
+            try:
+                self._stack.pop()
+                self.text(value, offset, file, issue_handler)
+            except IndexError:
+                issue_handler.issue(Issue("Undefined Parse Error", offset, offset + 1, file, Issue.SEVERITY_ERROR))
+        else:
+            # TODO: possible?
+            issue_handler.issue(Issue("Unexpected TEXT token with %s on stack" % top.type, offset, offset + 1, file, Issue.SEVERITY_ERROR))
+
+    def beginCurly(self, value, offset, file, issue_handler):
+        top = self._stack[-1]
+
+        if top.type == Node.COMMAND:
+            node = LocalizedNode(Node.MANDATORY_ARGUMENT, offset, offset + 1, file=file)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.DOCUMENT \
+                or top.type == Node.MANDATORY_ARGUMENT \
+                or top.type == Node.OPTIONAL_ARGUMENT \
+                or top.type == Node.EMBRACED:
+            node = LocalizedNode(Node.EMBRACED, offset, offset + 1, file=file)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.TEXT:
+            try:
+                self._stack.pop()
+                self.beginCurly(value, offset, file, issue_handler)
+            except IndexError:
+                issue_handler.issue(Issue("Undefined Parse Error", offset, offset + 1, file, Issue.SEVERITY_ERROR))
+        else:
+            # TODO: possible?
+            issue_handler.issue(Issue("Unexpected BEGIN_CURLY token with %s on stack" % top.type, offset, offset + 1, file, Issue.SEVERITY_ERROR))
+
+    def endCurly(self, value, offset, file, issue_handler):
+        try:
+            # pop from stack until MANDATORY_ARGUMENT or EMBRACED
+            while True:
+                top = self._stack[-1]
+                if top.type == Node.MANDATORY_ARGUMENT or top.type == Node.EMBRACED:
+                    node = self._stack.pop()
+                    break
+                self._stack.pop()
+
+            # set end offset of MANDATORY_ARGUMENT or EMBRACED
+            node.end = offset + 1
+        except IndexError:
+            issue_handler.issue(Issue("Encountered <b>}</b> without <b>{</b>", offset, offset + 1, file, Issue.SEVERITY_ERROR))
+            # we cannot continue after that
+            raise FatalParseException
+
+    def beginSquare(self, value, offset, file, issue_handler):
+        top = self._stack[-1]
+        if top.type == Node.COMMAND:
+            node = LocalizedNode(Node.OPTIONAL_ARGUMENT, offset, offset + 1, file=file)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.TEXT:
+            top.value += "["
+
+        elif top.type == Node.MANDATORY_ARGUMENT:
+            node = LocalizedNode(Node.TEXT, offset, offset + 1, "[", file)
+            top.append(node)
+            self._stack.append(node)
+
+        else:
+            issue_handler.issue(Issue("Unexpected BEGIN_SQUARE token with %s on stack" % top.type, offset, offset + 1, file, Issue.SEVERITY_ERROR))
+
+    def endSquare(self, value, offset, file, issue_handler):
+        try:
+            node = [node for node in self._stack if node.type == Node.OPTIONAL_ARGUMENT][-1]
+
+            # open optional argument found
+            # this square closes it, so pop stack until there
+
+            while self._stack[-1].type != Node.OPTIONAL_ARGUMENT:
+                self._stack.pop()
+            node = self._stack.pop()
+            node.end = offset + 1
+
+        except IndexError:
+            # no open optional argument, so this "]" is TEXT
+
+            top = self._stack[-1]
+            if top.type == Node.TEXT:
+                top.value += "]"
+
+            elif top.type == Node.COMMAND:
+                try:
+                    self._stack.pop()
+                    self.endSquare(value, offset, file, issue_handler)
+                except IndexError:
+                    issue_handler.issue(Issue("Undefined Parse Error", offset, offset + 1, file, Issue.SEVERITY_ERROR))
+
+            elif top.type == Node.MANDATORY_ARGUMENT or top.type == Node.DOCUMENT or top.type == Node.OPTIONAL_ARGUMENT:
+                node = LocalizedNode(Node.TEXT, offset, offset + 1, "]", file)
+                top.append(node)
+                self._stack.append(node)
+
+            else:
+                issue_handler.issue(Issue("Unexpected END_SQUARE token with %s on stack and no optional argument" % top.type, offset, offset + 1, file, Issue.SEVERITY_ERROR))
+
+    _PATTERN = compile("(TODO|FIXME)\w?\:?(?P<text>.*)")
+
+    def comment(self, value, offset, file, issue_handler):
+        """
+        Extract TODOs and FIXMEs
+        """
+        match = self._PATTERN.search(value)
+        if match:
+            text = match.group("text").strip()
+            # TODO: escape
+            issue_handler.issue(Issue(text, offset + match.start(), offset + match.end() + 1, file, Issue.SEVERITY_TASK))
+
+    def verbatim(self, value, offset, file, issue_handler):
+        pass
+
+    #~ def __del__(self):
+        #~ print "Properly destroyed %s" % self
 
 
 class PrefixParser(object):
-	"""
-	A light-weight LaTeX parser used for parsing just a prefix in
-	the code completion.
-	
-	The differences between the full parser and this one include:
-	 * we don't collect issues (we just raise an exception)
-	 * we don't store node offsets
-	 * we indicate whether the last argument is closed or not
-	"""
-	
-	# TODO: use another Lexer here that doesn't count offsets (faster)
-	
-	def parse(self, string, documentNode):
-		
-		# TODO: change semantic 
-		
-		self._stack = [documentNode]
-		
-		callables = {Token.COMMAND : self.command, 
-					 Token.TEXT : self.text, 
-					 Token.BEGIN_CURLY : self.beginCurly,
-					 Token.END_CURLY : self.endCurly, 
-					 Token.BEGIN_SQUARE : self.beginSquare, 
-					 Token.END_SQUARE : self.endSquare,
-					 Token.COMMENT : self.comment, 
-					 Token.VERBATIM : self.verbatim}
-		
-		try:
-			for token in Lexer(string, skipWs=False, skipComment=False):
-				callables[token.type].__call__(token.value)
-		except FatalParseException:
-			return
-		
-	def command(self, value):
-		top = self._stack[-1]
-		
-		if top.type == Node.DOCUMENT \
-				or top.type == Node.MANDATORY_ARGUMENT \
-				or top.type == Node.OPTIONAL_ARGUMENT \
-				or top.type == Node.EMBRACED:
-			node = Node(Node.COMMAND, value)
-			top.append(node)
-			self._stack.append(node)
-			
-		elif top.type == Node.COMMAND \
-				or top.type == Node.TEXT:
-			try:
-				self._stack.pop()
-				self.command(value)
-			except IndexError:
-				raise FatalParseException
-	
-	def text(self, value):
-		top = self._stack[-1]
-
-		if top.type == Node.DOCUMENT \
-				or top.type == Node.MANDATORY_ARGUMENT \
-				or top.type == Node.OPTIONAL_ARGUMENT \
-				or top.type == Node.EMBRACED:
-			node = Node(Node.TEXT, value)
-			top.append(node)
-			self._stack.append(node)
-			
-		elif top.type == Node.COMMAND:
-			self._stack.pop()
-			self.text(value)
-			
-		elif top.type == Node.TEXT:
-			try:
-				self._stack.pop()
-				self.text(value)
-			except IndexError:
-				raise FatalParseException
-		else:
-			# TODO: possible?
-			raise FatalParseException
-	
-	def beginCurly(self, value):
-		top = self._stack[-1]
-		
-		if top.type == Node.COMMAND:
-			node = Node(Node.MANDATORY_ARGUMENT)
-			top.append(node)
-			self._stack.append(node)
-		
-		elif top.type == Node.DOCUMENT \
-				or top.type == Node.MANDATORY_ARGUMENT \
-				or top.type == Node.OPTIONAL_ARGUMENT \
-				or top.type == Node.EMBRACED:
-			node = Node(Node.EMBRACED)
-			top.append(node)
-			self._stack.append(node)
-		
-		elif top.type == Node.TEXT:
-			try:
-				self._stack.pop()
-				self.beginCurly(value)
-			except IndexError:
-				raise FatalParseException
-		else:
-			# TODO: possible?
-			raise FatalParseException
-	
-	def endCurly(self, value):
-		try:
-			# pop from stack until MANDATORY_ARGUMENT or EMBRACED
-			while True:
-				top = self._stack[-1]
-				if top.type == Node.MANDATORY_ARGUMENT or top.type == Node.EMBRACED:
-					node = self._stack.pop()
-					break
-				self._stack.pop()
-				
-			node.closed = True
-		except IndexError:
-			raise FatalParseException
-	
-	def beginSquare(self, value):
-		top = self._stack[-1]
-		if top.type == Node.COMMAND:
-			node = Node(Node.OPTIONAL_ARGUMENT)
-			top.append(node)
-			self._stack.append(node)
-		
-		elif top.type == Node.TEXT:
-			top.value += "["
-		
-		elif top.type == Node.MANDATORY_ARGUMENT:
-			node = Node(Node.TEXT, "[")
-			top.append(node)
-			self._stack.append(node)
-		
-		else:
-			raise FatalParseException
-	
-	def endSquare(self, value):
-		try:
-			# check whether an optional argument is open at all
-			#
-			# for this we address the top OPTIONAL_ARGUMENT node on the stack, if it doesn't
-			# exist an IndexError is thrown
-			node = [node for node in self._stack if node.type == Node.OPTIONAL_ARGUMENT][-1]
-			
-			# open optional argument found 
-			# this square closes it, so pop stack until there
-			
-			while self._stack[-1].type != Node.OPTIONAL_ARGUMENT:
-				self._stack.pop()
-			node = self._stack.pop()
-			node.closed = True
-			
-		except IndexError:
-			# no open optional argument, so this "]" is TEXT
-			
-			top = self._stack[-1]
-			if top.type == Node.TEXT:
-				top.value += "]"
-				
-			elif top.type == Node.COMMAND:
-				try:
-					self._stack.pop()
-					self.endSquare(value)
-				except IndexError:
-					raise FatalParseException
-			
-			elif top.type == Node.MANDATORY_ARGUMENT or top.type == Node.DOCUMENT or top.type == Node.OPTIONAL_ARGUMENT:
-				node = Node(Node.TEXT, "]")
-				top.append(node)
-				self._stack.append(node)
-			
-			else:
-				raise FatalParseException
-	
-	def comment(self, value):
-		pass
-		
-	def verbatim(self, value):
-		pass
-
-		
\ No newline at end of file
+    """
+    A light-weight LaTeX parser used for parsing just a prefix in
+    the code completion.
+
+    The differences between the full parser and this one include:
+     * we don't collect issues (we just raise an exception)
+     * we don't store node offsets
+     * we indicate whether the last argument is closed or not
+    """
+
+    # TODO: use another Lexer here that doesn't count offsets (faster)
+
+    def parse(self, string, documentNode):
+
+        # TODO: change semantic
+
+        self._stack = [documentNode]
+
+        callables = {Token.COMMAND : self.command,
+                     Token.TEXT : self.text,
+                     Token.BEGIN_CURLY : self.beginCurly,
+                     Token.END_CURLY : self.endCurly,
+                     Token.BEGIN_SQUARE : self.beginSquare,
+                     Token.END_SQUARE : self.endSquare,
+                     Token.COMMENT : self.comment,
+                     Token.VERBATIM : self.verbatim}
+
+        try:
+            for token in Lexer(string, skipWs=False, skipComment=False):
+                callables[token.type].__call__(token.value)
+        except FatalParseException:
+            return
+
+    def command(self, value):
+        top = self._stack[-1]
+
+        if top.type == Node.DOCUMENT \
+                or top.type == Node.MANDATORY_ARGUMENT \
+                or top.type == Node.OPTIONAL_ARGUMENT \
+                or top.type == Node.EMBRACED:
+            node = Node(Node.COMMAND, value)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.COMMAND \
+                or top.type == Node.TEXT:
+            try:
+                self._stack.pop()
+                self.command(value)
+            except IndexError:
+                raise FatalParseException
+
+    def text(self, value):
+        top = self._stack[-1]
+
+        if top.type == Node.DOCUMENT \
+                or top.type == Node.MANDATORY_ARGUMENT \
+                or top.type == Node.OPTIONAL_ARGUMENT \
+                or top.type == Node.EMBRACED:
+            node = Node(Node.TEXT, value)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.COMMAND:
+            self._stack.pop()
+            self.text(value)
+
+        elif top.type == Node.TEXT:
+            try:
+                self._stack.pop()
+                self.text(value)
+            except IndexError:
+                raise FatalParseException
+        else:
+            # TODO: possible?
+            raise FatalParseException
+
+    def beginCurly(self, value):
+        top = self._stack[-1]
+
+        if top.type == Node.COMMAND:
+            node = Node(Node.MANDATORY_ARGUMENT)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.DOCUMENT \
+                or top.type == Node.MANDATORY_ARGUMENT \
+                or top.type == Node.OPTIONAL_ARGUMENT \
+                or top.type == Node.EMBRACED:
+            node = Node(Node.EMBRACED)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.TEXT:
+            try:
+                self._stack.pop()
+                self.beginCurly(value)
+            except IndexError:
+                raise FatalParseException
+        else:
+            # TODO: possible?
+            raise FatalParseException
+
+    def endCurly(self, value):
+        try:
+            # pop from stack until MANDATORY_ARGUMENT or EMBRACED
+            while True:
+                top = self._stack[-1]
+                if top.type == Node.MANDATORY_ARGUMENT or top.type == Node.EMBRACED:
+                    node = self._stack.pop()
+                    break
+                self._stack.pop()
+
+            node.closed = True
+        except IndexError:
+            raise FatalParseException
+
+    def beginSquare(self, value):
+        top = self._stack[-1]
+        if top.type == Node.COMMAND:
+            node = Node(Node.OPTIONAL_ARGUMENT)
+            top.append(node)
+            self._stack.append(node)
+
+        elif top.type == Node.TEXT:
+            top.value += "["
+
+        elif top.type == Node.MANDATORY_ARGUMENT:
+            node = Node(Node.TEXT, "[")
+            top.append(node)
+            self._stack.append(node)
+
+        else:
+            raise FatalParseException
+
+    def endSquare(self, value):
+        try:
+            # check whether an optional argument is open at all
+            #
+            # for this we address the top OPTIONAL_ARGUMENT node on the stack, if it doesn't
+            # exist an IndexError is thrown
+            node = [node for node in self._stack if node.type == Node.OPTIONAL_ARGUMENT][-1]
+
+            # open optional argument found
+            # this square closes it, so pop stack until there
+
+            while self._stack[-1].type != Node.OPTIONAL_ARGUMENT:
+                self._stack.pop()
+            node = self._stack.pop()
+            node.closed = True
+
+        except IndexError:
+            # no open optional argument, so this "]" is TEXT
+
+            top = self._stack[-1]
+            if top.type == Node.TEXT:
+                top.value += "]"
+
+            elif top.type == Node.COMMAND:
+                try:
+                    self._stack.pop()
+                    self.endSquare(value)
+                except IndexError:
+                    raise FatalParseException
+
+            elif top.type == Node.MANDATORY_ARGUMENT or top.type == Node.DOCUMENT or top.type == Node.OPTIONAL_ARGUMENT:
+                node = Node(Node.TEXT, "]")
+                top.append(node)
+                self._stack.append(node)
+
+            else:
+                raise FatalParseException
+
+    def comment(self, value):
+        pass
+
+    def verbatim(self, value):
+        pass
+
diff --git a/latex/latex/preview.py b/latex/latex/preview.py
index 2f89813..30624cf 100644
--- a/latex/latex/preview.py
+++ b/latex/latex/preview.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -31,135 +31,135 @@ from environment import Environment
 from gi.repository import Gdk, GdkPixbuf
 
 class ImageToolGenerator(object):
-	"""
-	This generates Tools for rendering images from LaTeX source
-	"""
-	
-	FORMAT_PNG, FORMAT_JPEG, FORMAT_GIF = 1, 2, 3
-	
-	PNG_MODE_MONOCHROME, PNG_MODE_GRAYSCALE, PNG_MODE_RGB, PNG_MODE_RGBA = 1, 2, 3, 4
-	
-	def __init__(self):
-		self._names = {self.FORMAT_PNG : "PNG Image", self.FORMAT_JPEG : "JPEG Image", self.FORMAT_GIF : "GIF Image"}
-		self._png_modes = {self.PNG_MODE_MONOCHROME : "mono", self.PNG_MODE_GRAYSCALE : "gray", self.PNG_MODE_RGB : "16m",
-						self.PNG_MODE_RGBA : "alpha"}
-		
-		# default settings
-		self.format = self.FORMAT_PNG
-		self.png_mode = self.PNG_MODE_RGBA
-		self.render_box = True
-		self.resolution = int(round(Environment().screen_dpi))
-		self.antialias_factor = 4
-		self.open = False
-	
-	def generate(self):
-		"""
-		@return: a Tool object
-		"""
-		tool = Tool(label=self._names[self.format], jobs=[], description="", accelerator="", extensions=[])
-		
-		# use rubber to render a DVI
-		tool.jobs.append(Job("rubber --force --short --inplace \"$filename\"", True, RubberPostProcessor))
-		
-		if self.render_box:
-			# DVI -> PS
-			
-			# -D num	resolution in DPI
-			# -q		quiet mode
-			# -E		generate an EPSF file with a tight bounding box
-			tool.jobs.append(Job("dvips -D %s -q -E -o \"$shortname.eps\" \"$shortname.dvi\"" % self.resolution, True, GenericPostProcessor))
-			
-			# EPS -> PNG|JPG|GIF
-			if self.format == self.FORMAT_PNG:
-				command = "$plugin_path/util/eps2png.pl -png%s -resolution=%s -antialias=%s \"$shortname.eps\"" % (self._png_modes[self.png_mode], 
-																				self.resolution, self.antialias_factor)
-			elif self.format == self.FORMAT_JPEG:
-				command = "$plugin_path/util/eps2png.pl -jpeg -resolution=%s -antialias=%s \"$shortname.eps\"" % (self.resolution, self.antialias_factor)
-			elif self.format == self.FORMAT_GIF:
-				command = "$plugin_path/util/eps2png.pl -gif -resolution=%s -antialias=%s \"$shortname.eps\"" % (self.resolution, self.antialias_factor)
-			
-			tool.jobs.append(Job(command, True, GenericPostProcessor))
-		else:
-			# dvips
-			tool.jobs.append(Job("dvips -D %s -q -o \"$shortname.ps\" \"$shortname.dvi\"" % self.resolution, True, GenericPostProcessor))
-			
-			if self.format == self.FORMAT_PNG:
-				tool.jobs.append(Job("gs -q -dNOPAUSE -r%s -dTextAlphaBits=%s -dGraphicsAlphaBits=%s -sDEVICE=png%s -sOutputFile=$shortname.png $shortname.ps quit.ps" 
-									% (self.resolution, self.antialias_factor, self.antialias_factor, self._png_modes[self.png_mode]), True, GenericPostProcessor))
-			elif self.format == self.FORMAT_JPEG:
-				tool.jobs.append(Job("gs -q -dNOPAUSE -r%s -dTextAlphaBits=%s -dGraphicsAlphaBits=%s -sDEVICE=jpeg -sOutputFile=$shortname.jpg $shortname.ps quit.ps" 
-									% (self.resolution, self.antialias_factor, self.antialias_factor), True, GenericPostProcessor))
-			elif self.format == self.FORMAT_GIF:
-				tool.jobs.append(Job("gs -q -dNOPAUSE -r%s -dTextAlphaBits=%s -dGraphicsAlphaBits=%s -sDEVICE=ppm -sOutputFile=$shortname.ppm $shortname.ps quit.ps" 
-									% (self.resolution, self.antialias_factor, self.antialias_factor), True, GenericPostProcessor))
-				# ppmtogif
-				tool.jobs.append(Job("ppmtogif $shortname.ppm > $shortname.gif", True, GenericPostProcessor))
-		
-		if self.open:
-			extension = {self.FORMAT_PNG : "png", self.FORMAT_JPEG: "jpg", self.FORMAT_GIF : "gif"}[self.format] 
-			tool.jobs.append(Job("gnome-open \"$shortname.%s\"" % extension, True, GenericPostProcessor))
-		
-		return tool
+    """
+    This generates Tools for rendering images from LaTeX source
+    """
+
+    FORMAT_PNG, FORMAT_JPEG, FORMAT_GIF = 1, 2, 3
+
+    PNG_MODE_MONOCHROME, PNG_MODE_GRAYSCALE, PNG_MODE_RGB, PNG_MODE_RGBA = 1, 2, 3, 4
+
+    def __init__(self):
+        self._names = {self.FORMAT_PNG : "PNG Image", self.FORMAT_JPEG : "JPEG Image", self.FORMAT_GIF : "GIF Image"}
+        self._png_modes = {self.PNG_MODE_MONOCHROME : "mono", self.PNG_MODE_GRAYSCALE : "gray", self.PNG_MODE_RGB : "16m",
+                        self.PNG_MODE_RGBA : "alpha"}
+
+        # default settings
+        self.format = self.FORMAT_PNG
+        self.png_mode = self.PNG_MODE_RGBA
+        self.render_box = True
+        self.resolution = int(round(Environment().screen_dpi))
+        self.antialias_factor = 4
+        self.open = False
+
+    def generate(self):
+        """
+        @return: a Tool object
+        """
+        tool = Tool(label=self._names[self.format], jobs=[], description="", accelerator="", extensions=[])
+
+        # use rubber to render a DVI
+        tool.jobs.append(Job("rubber --force --short --inplace \"$filename\"", True, RubberPostProcessor))
+
+        if self.render_box:
+            # DVI -> PS
+
+            # -D num    resolution in DPI
+            # -q        quiet mode
+            # -E        generate an EPSF file with a tight bounding box
+            tool.jobs.append(Job("dvips -D %s -q -E -o \"$shortname.eps\" \"$shortname.dvi\"" % self.resolution, True, GenericPostProcessor))
+
+            # EPS -> PNG|JPG|GIF
+            if self.format == self.FORMAT_PNG:
+                command = "$plugin_path/util/eps2png.pl -png%s -resolution=%s -antialias=%s \"$shortname.eps\"" % (self._png_modes[self.png_mode],
+                                                                                self.resolution, self.antialias_factor)
+            elif self.format == self.FORMAT_JPEG:
+                command = "$plugin_path/util/eps2png.pl -jpeg -resolution=%s -antialias=%s \"$shortname.eps\"" % (self.resolution, self.antialias_factor)
+            elif self.format == self.FORMAT_GIF:
+                command = "$plugin_path/util/eps2png.pl -gif -resolution=%s -antialias=%s \"$shortname.eps\"" % (self.resolution, self.antialias_factor)
+
+            tool.jobs.append(Job(command, True, GenericPostProcessor))
+        else:
+            # dvips
+            tool.jobs.append(Job("dvips -D %s -q -o \"$shortname.ps\" \"$shortname.dvi\"" % self.resolution, True, GenericPostProcessor))
+
+            if self.format == self.FORMAT_PNG:
+                tool.jobs.append(Job("gs -q -dNOPAUSE -r%s -dTextAlphaBits=%s -dGraphicsAlphaBits=%s -sDEVICE=png%s -sOutputFile=$shortname.png $shortname.ps quit.ps"
+                                    % (self.resolution, self.antialias_factor, self.antialias_factor, self._png_modes[self.png_mode]), True, GenericPostProcessor))
+            elif self.format == self.FORMAT_JPEG:
+                tool.jobs.append(Job("gs -q -dNOPAUSE -r%s -dTextAlphaBits=%s -dGraphicsAlphaBits=%s -sDEVICE=jpeg -sOutputFile=$shortname.jpg $shortname.ps quit.ps"
+                                    % (self.resolution, self.antialias_factor, self.antialias_factor), True, GenericPostProcessor))
+            elif self.format == self.FORMAT_GIF:
+                tool.jobs.append(Job("gs -q -dNOPAUSE -r%s -dTextAlphaBits=%s -dGraphicsAlphaBits=%s -sDEVICE=ppm -sOutputFile=$shortname.ppm $shortname.ps quit.ps"
+                                    % (self.resolution, self.antialias_factor, self.antialias_factor), True, GenericPostProcessor))
+                # ppmtogif
+                tool.jobs.append(Job("ppmtogif $shortname.ppm > $shortname.gif", True, GenericPostProcessor))
+
+        if self.open:
+            extension = {self.FORMAT_PNG : "png", self.FORMAT_JPEG: "jpg", self.FORMAT_GIF : "gif"}[self.format]
+            tool.jobs.append(Job("gnome-open \"$shortname.%s\"" % extension, True, GenericPostProcessor))
+
+        return tool
 
 
 from tempfile import NamedTemporaryFile
 
 class PreviewRenderer(ToolRunner):
-	def render(self, source):
-		"""
-		Render a preview image from LaTeX source
-		
-		@param source: some LaTeX source without \begin{document}
-		"""
-		# create temp file with source
-		self._temp_file = NamedTemporaryFile(mode="w", suffix=".tex")
-		self._temp_file.write("\\documentclass{article}\\pagestyle{empty}\\begin{document}%s\\end{document}" % source)
-		self._temp_file.flush()
-		
-		# generate Tool
-		tool = ImageToolGenerator().generate()
-		self._file = File(self._temp_file.name)
-		issue_handler = MockStructuredIssueHandler()
-		
-		# run the Tool
-		self.run(self._file, tool, issue_handler)
-	
-	def _on_tool_succeeded(self):
-		# see ToolRunner._on_tool_succeeded
-		pixbuf = GdkPixbuf.Pixbuf.new_from_file(self._file.shortname + ".png")
-		self.__cleanup()
-		self._on_render_succeeded(pixbuf)
-	
-	def _on_tool_failed(self):
-		# see ToolRunner._on_tool_failed
-		self.__cleanup()
-		self._on_render_failed()
-	
-	def __cleanup(self):
-		"""
-		Remove the files created during the render process
-		"""
-		# delete the temp file
-		self._temp_file.close()
-		
-		# delete all files created during the build process
-		for file in self._file.siblings:
-			try:
-				file.delete()
-				self._log.debug("Removed %s" % file)
-			except OSError:
-				self._log.error("Failed to remove '%s'" % file)
-	
-	def _on_render_succeeded(self, pixbuf):
-		"""
-		The rendering process has finished successfully
-		
-		@param pixbuf: a GdkPixbuf.Pixbuf containing the result image
-		"""
-	
-	def _on_render_failed(self):
-		"""
-		The rendering process has failed
-		"""
-		
-		
+    def render(self, source):
+        """
+        Render a preview image from LaTeX source
+
+        @param source: some LaTeX source without \begin{document}
+        """
+        # create temp file with source
+        self._temp_file = NamedTemporaryFile(mode="w", suffix=".tex")
+        self._temp_file.write("\\documentclass{article}\\pagestyle{empty}\\begin{document}%s\\end{document}" % source)
+        self._temp_file.flush()
+
+        # generate Tool
+        tool = ImageToolGenerator().generate()
+        self._file = File(self._temp_file.name)
+        issue_handler = MockStructuredIssueHandler()
+
+        # run the Tool
+        self.run(self._file, tool, issue_handler)
+
+    def _on_tool_succeeded(self):
+        # see ToolRunner._on_tool_succeeded
+        pixbuf = GdkPixbuf.Pixbuf.new_from_file(self._file.shortname + ".png")
+        self.__cleanup()
+        self._on_render_succeeded(pixbuf)
+
+    def _on_tool_failed(self):
+        # see ToolRunner._on_tool_failed
+        self.__cleanup()
+        self._on_render_failed()
+
+    def __cleanup(self):
+        """
+        Remove the files created during the render process
+        """
+        # delete the temp file
+        self._temp_file.close()
+
+        # delete all files created during the build process
+        for file in self._file.siblings:
+            try:
+                file.delete()
+                self._log.debug("Removed %s" % file)
+            except OSError:
+                self._log.error("Failed to remove '%s'" % file)
+
+    def _on_render_succeeded(self, pixbuf):
+        """
+        The rendering process has finished successfully
+
+        @param pixbuf: a GdkPixbuf.Pixbuf containing the result image
+        """
+
+    def _on_render_failed(self):
+        """
+        The rendering process has failed
+        """
+
+
diff --git a/latex/latex/validator.py b/latex/latex/validator.py
index 8c6aa50..76df3cf 100644
--- a/latex/latex/validator.py
+++ b/latex/latex/validator.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -33,208 +33,208 @@ from environment import Environment
 
 
 class LaTeXValidator(object):
-	"""
-	This validates the following aspects by walking the document model 
-	and using the outline:
-	
-	 * unused labels
-	 * unclosed environments, also "\[" and "\]"
-	"""
-	
-	_log = getLogger("LaTeXValidator")
-	
-	def __init__(self):
-		self._environment = Environment()
-	
-	def validate(self, document_node, outline, issue_handler):
-		"""
-		Validate a LaTeX document
-		
-		@param document_node: the root node of the document tree
-		@param outline: a LaTeX outline object
-		@param issue_handler: an object implementing IIssueHandler
-		"""
-		
-		self._log.debug("validate")
-		
-		#~ # TODO: this is dangerous, the outline object could be outdated		
-		#~ self._outline = outline
-		
-		# prepare a map for checking labels
-		self._labels = {}
-		for label in outline.labels:
-			self._labels[label.value] = [label, False]	
-		
-		self._environStack = []
-		
-		self._checkRefs = True
-		
-		self._run(document_node, issue_handler)
-		
-		# evaluate label map
-		for label, used in self._labels.values():
-			if not used:
-				# FIXME: we need to know in which File the label was defined!
-				issue_handler.issue(Issue("Label <b>%s</b> is never used" % escape(label.value), label.start, label.end, label.file, Issue.SEVERITY_WARNING))
-		
-	def _run(self, parentNode, issue_handler):
-		"""
-		Recursive method validation
-		"""
-		for node in parentNode:
-			recurse = True
-#			if node.type == Node.DOCUMENT:
-#				
-#				self._log.debug("DOCUMENT: %s" % node.value)
-#				
-#				# the document node contains the File object as value
-#				self._file = node.value
-				
-			if node.type == Node.COMMAND:
-				if node.value == "begin":
-					try:
-						# push environment on stack
-						environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						self._environStack.append((environ, node.start, node.lastEnd))
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-						
-				elif node.value == "end":
-					try:
-						# check environment
-						environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						try:
-							tEnviron, tStart, tEnd = self._environStack.pop()
-							if tEnviron != environ:
-								issue_handler.issue(Issue("Environment <b>%s</b> has to be ended before <b>%s</b>" % (escape(tEnviron), escape(environ)), node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-						except IndexError:
-							issue_handler.issue(Issue("Environment <b>%s</b> has no beginning" % escape(environ), node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-						
-				elif node.value == "[":
-					# push eqn env on stack
-					self._environStack.append(("[", node.start, node.end))
-				
-				elif node.value == "]":
-					try:
-						tEnviron, tStart, tEnd = self._environStack.pop()
-						if tEnviron != "[":
-							issue_handler.issue(Issue("Environment <b>%s</b> has to be ended before <b>]</b>" % escape(tEnviron), node.start, node.end, node.file, Issue.SEVERITY_ERROR))
-					except IndexError:
-						issue_handler.issue(Issue("Environment <b>%s</b> has no beginning" % escape(environ), node.start, node.end, node.file, Issue.SEVERITY_ERROR))
-				
-				elif node.value == "ref" or node.value == "eqref" or node.value == "pageref":
-					# mark label as used
-					try:
-						label = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						try:
-							self._labels[label][1] = True
-						except KeyError:
-							issue_handler.issue(Issue("Label <b>%s</b> has not been defined" % escape(label), node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-						
-				elif self._checkRefs and (node.value == "includegraphics"):
-					try:
-						# check referenced image file
-						target = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						if len(target) > 0:
-							if File.is_absolute(target):
-								filename = target
-							else:
-								file = File.create_from_relative_path(target, node.file.dirname)
-								filename = file.path
-							
-							# an image may be specified without the extension
-							potential_extensions = ["", ".eps", ".pdf", ".jpg", ".jpeg", ".gif", ".png"]
-							
-							found = False
-							for ext in potential_extensions:
-								if exists(filename + ext):
-									found = True
-									break
-							
-							if not found:
-								issue_handler.issue(Issue("Image <b>%s</b> could not be found" % escape(target), node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
-						else:
-							issue_handler.issue(Issue("No image file specified", node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-						
-				elif self._checkRefs and (node.value == "include" or node.value == "input"):
-					# check referenced tex file
-					try:
-						target = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						if len(target) > 0:
-							if File.is_absolute(target):
-								filename = target
-							else:
-								file = File.create_from_relative_path(target, node.file.dirname)
-								filename = file.path
-							
-							# an input may be specified without the extension
-							potential_extensions = ["", ".tex"]
-							
-							found = False
-							for ext in potential_extensions:
-								if exists(filename + ext):
-									found = True
-									break
-							
-							if not found:
-								issue_handler.issue(Issue("Document <b>%s</b> could not be found" % escape(filename), node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
-					except IndexError:		# firstOfType failed
-						# this happens on old-style syntax like "\input myfile"
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-								
-				elif self._checkRefs and node.value == "bibliography":
-					try:
-						# check referenced BibTeX file(s)
-						value = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText							
-						for target in value.split(","):
-							if File.is_absolute(target):
-								filename = target
-							else:
-								file = File.create_from_relative_path(target, node.file.dirname)
-								filename = file.path
-							
-							# a bib file may be specified without the extension
-							potential_extensions = ["", ".bib"]
-							
-							found = False
-							for ext in potential_extensions:
-								if exists(filename + ext):
-									found = True
-									break
-							
-							if not found:
-								issue_handler.issue(Issue("Bibliography <b>%s</b> could not be found" % escape(filename), node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
-					except IndexError:		# firstOfType failed
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-						
-				elif node.value == "newcommand":
-					# don't validate in newcommand definitions
-					recurse = False
-				
-				elif node.value == "bibliographystyle":
-					try:
-						# check if style exists
-						value = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
-						
-						# search the TeX environment
-						if not self._environment.file_exists("%s.bst" % value):
-							# search the working directory
-							bst_file = File.create_from_relative_path("%s.bst" % value, node.file.dirname)
-							if not bst_file.exists:
-								issue_handler.issue(Issue("Bibliography style <b>%s</b> could not be found" % escape(value), node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
-					except IndexError:
-						issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
-						
-			if recurse:
-				self._run(node, issue_handler)
-				
-	#~ def __del__(self):
-		#~ print "Properly destroyed %s" % self
-				
-				
+    """
+    This validates the following aspects by walking the document model
+    and using the outline:
+
+     * unused labels
+     * unclosed environments, also "\[" and "\]"
+    """
+
+    _log = getLogger("LaTeXValidator")
+
+    def __init__(self):
+        self._environment = Environment()
+
+    def validate(self, document_node, outline, issue_handler):
+        """
+        Validate a LaTeX document
+
+        @param document_node: the root node of the document tree
+        @param outline: a LaTeX outline object
+        @param issue_handler: an object implementing IIssueHandler
+        """
+
+        self._log.debug("validate")
+
+        #~ # TODO: this is dangerous, the outline object could be outdated
+        #~ self._outline = outline
+
+        # prepare a map for checking labels
+        self._labels = {}
+        for label in outline.labels:
+            self._labels[label.value] = [label, False]
+
+        self._environStack = []
+
+        self._checkRefs = True
+
+        self._run(document_node, issue_handler)
+
+        # evaluate label map
+        for label, used in self._labels.values():
+            if not used:
+                # FIXME: we need to know in which File the label was defined!
+                issue_handler.issue(Issue("Label <b>%s</b> is never used" % escape(label.value), label.start, label.end, label.file, Issue.SEVERITY_WARNING))
+
+    def _run(self, parentNode, issue_handler):
+        """
+        Recursive method validation
+        """
+        for node in parentNode:
+            recurse = True
+#            if node.type == Node.DOCUMENT:
+#
+#                self._log.debug("DOCUMENT: %s" % node.value)
+#
+#                # the document node contains the File object as value
+#                self._file = node.value
+
+            if node.type == Node.COMMAND:
+                if node.value == "begin":
+                    try:
+                        # push environment on stack
+                        environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        self._environStack.append((environ, node.start, node.lastEnd))
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif node.value == "end":
+                    try:
+                        # check environment
+                        environ = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        try:
+                            tEnviron, tStart, tEnd = self._environStack.pop()
+                            if tEnviron != environ:
+                                issue_handler.issue(Issue("Environment <b>%s</b> has to be ended before <b>%s</b>" % (escape(tEnviron), escape(environ)), node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+                        except IndexError:
+                            issue_handler.issue(Issue("Environment <b>%s</b> has no beginning" % escape(environ), node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif node.value == "[":
+                    # push eqn env on stack
+                    self._environStack.append(("[", node.start, node.end))
+
+                elif node.value == "]":
+                    try:
+                        tEnviron, tStart, tEnd = self._environStack.pop()
+                        if tEnviron != "[":
+                            issue_handler.issue(Issue("Environment <b>%s</b> has to be ended before <b>]</b>" % escape(tEnviron), node.start, node.end, node.file, Issue.SEVERITY_ERROR))
+                    except IndexError:
+                        issue_handler.issue(Issue("Environment <b>%s</b> has no beginning" % escape(environ), node.start, node.end, node.file, Issue.SEVERITY_ERROR))
+
+                elif node.value == "ref" or node.value == "eqref" or node.value == "pageref":
+                    # mark label as used
+                    try:
+                        label = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        try:
+                            self._labels[label][1] = True
+                        except KeyError:
+                            issue_handler.issue(Issue("Label <b>%s</b> has not been defined" % escape(label), node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif self._checkRefs and (node.value == "includegraphics"):
+                    try:
+                        # check referenced image file
+                        target = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        if len(target) > 0:
+                            if File.is_absolute(target):
+                                filename = target
+                            else:
+                                file = File.create_from_relative_path(target, node.file.dirname)
+                                filename = file.path
+
+                            # an image may be specified without the extension
+                            potential_extensions = ["", ".eps", ".pdf", ".jpg", ".jpeg", ".gif", ".png"]
+
+                            found = False
+                            for ext in potential_extensions:
+                                if exists(filename + ext):
+                                    found = True
+                                    break
+
+                            if not found:
+                                issue_handler.issue(Issue("Image <b>%s</b> could not be found" % escape(target), node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
+                        else:
+                            issue_handler.issue(Issue("No image file specified", node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif self._checkRefs and (node.value == "include" or node.value == "input"):
+                    # check referenced tex file
+                    try:
+                        target = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        if len(target) > 0:
+                            if File.is_absolute(target):
+                                filename = target
+                            else:
+                                file = File.create_from_relative_path(target, node.file.dirname)
+                                filename = file.path
+
+                            # an input may be specified without the extension
+                            potential_extensions = ["", ".tex"]
+
+                            found = False
+                            for ext in potential_extensions:
+                                if exists(filename + ext):
+                                    found = True
+                                    break
+
+                            if not found:
+                                issue_handler.issue(Issue("Document <b>%s</b> could not be found" % escape(filename), node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
+                    except IndexError:        # firstOfType failed
+                        # this happens on old-style syntax like "\input myfile"
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif self._checkRefs and node.value == "bibliography":
+                    try:
+                        # check referenced BibTeX file(s)
+                        value = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+                        for target in value.split(","):
+                            if File.is_absolute(target):
+                                filename = target
+                            else:
+                                file = File.create_from_relative_path(target, node.file.dirname)
+                                filename = file.path
+
+                            # a bib file may be specified without the extension
+                            potential_extensions = ["", ".bib"]
+
+                            found = False
+                            for ext in potential_extensions:
+                                if exists(filename + ext):
+                                    found = True
+                                    break
+
+                            if not found:
+                                issue_handler.issue(Issue("Bibliography <b>%s</b> could not be found" % escape(filename), node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
+                    except IndexError:        # firstOfType failed
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+                elif node.value == "newcommand":
+                    # don't validate in newcommand definitions
+                    recurse = False
+
+                elif node.value == "bibliographystyle":
+                    try:
+                        # check if style exists
+                        value = node.firstOfType(Node.MANDATORY_ARGUMENT).innerText
+
+                        # search the TeX environment
+                        if not self._environment.file_exists("%s.bst" % value):
+                            # search the working directory
+                            bst_file = File.create_from_relative_path("%s.bst" % value, node.file.dirname)
+                            if not bst_file.exists:
+                                issue_handler.issue(Issue("Bibliography style <b>%s</b> could not be found" % escape(value), node.start, node.lastEnd, node.file, Issue.SEVERITY_WARNING))
+                    except IndexError:
+                        issue_handler.issue(Issue("Malformed command", node.start, node.lastEnd, node.file, Issue.SEVERITY_ERROR))
+
+            if recurse:
+                self._run(node, issue_handler)
+
+    #~ def __del__(self):
+        #~ print "Properly destroyed %s" % self
+
+
diff --git a/latex/latex/views.py b/latex/latex/views.py
index 9a44726..3c55cec 100755
--- a/latex/latex/views.py
+++ b/latex/latex/views.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -38,140 +38,140 @@ from ..issues import Issue
 
 
 class SymbolCollection(object):
-	"""
-	A collection of symbols read from an XML file
-	"""
-	
-	_log = getLogger("SymbolCollection")
-	
-	
-	class Group(object):
-		def __init__(self, label):
-			"""
-			@param label: a label for this Group
-			"""
-			self.label = label
-			self.symbols = []
-		
-		
-	class Symbol(object):
-		def __init__(self, template, icon):
-			"""
-			@param template: a Template instance
-			@param icon: an icon filename
-			"""
-			self.template = template
-			self.icon = icon
-	
-	
-	def __init__(self):
-		filename = find_resource("symbols.xml")
-		
-		self.groups = []
-		
-		symbols_el = ElementTree.parse(filename).getroot()
-		for group_el in symbols_el.findall("group"):
-			group = self.Group(group_el.get("label"))
-			for symbol_el in group_el.findall("symbol"):
-				symbol = self.Symbol(Template(symbol_el.text.strip()), find_resource("icons/%s" % symbol_el.get("icon")))
-				group.symbols.append(symbol)
-			self.groups.append(group)
-
-		
+    """
+    A collection of symbols read from an XML file
+    """
+
+    _log = getLogger("SymbolCollection")
+
+
+    class Group(object):
+        def __init__(self, label):
+            """
+            @param label: a label for this Group
+            """
+            self.label = label
+            self.symbols = []
+
+
+    class Symbol(object):
+        def __init__(self, template, icon):
+            """
+            @param template: a Template instance
+            @param icon: an icon filename
+            """
+            self.template = template
+            self.icon = icon
+
+
+    def __init__(self):
+        filename = find_resource("symbols.xml")
+
+        self.groups = []
+
+        symbols_el = ElementTree.parse(filename).getroot()
+        for group_el in symbols_el.findall("group"):
+            group = self.Group(group_el.get("label"))
+            for symbol_el in group_el.findall("symbol"):
+                symbol = self.Symbol(Template(symbol_el.text.strip()), find_resource("icons/%s" % symbol_el.get("icon")))
+                group.symbols.append(symbol)
+            self.groups.append(group)
+
+
 class LaTeXSymbolMapView(SideView):
-	"""
-	"""
-	__log = getLogger("LaTeXSymbolMapView")
-	
-	label = "Symbols"
-	icon = Gtk.Image.new_from_stock(Gtk.STOCK_INDEX,Gtk.IconSize.MENU)
-	scope = View.SCOPE_WINDOW
-	
-	def init(self, context):
-		self.__log.debug("init")
-		
-		self.__context = context
-		self.__preferences = Preferences()
-		
-		scrolled = Gtk.ScrolledWindow()
-		scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
-		scrolled.set_shadow_type(Gtk.ShadowType.NONE)
-		
-		self.__box = Gtk.VBox()
-		scrolled.add_with_viewport(self.__box)
-		
-		self.add(scrolled)
-		self.show_all()
-		
-		self.__load_collection(SymbolCollection())
-	
-	def __load_collection(self, collection):
-		self.__expanded_groups = set(self.__preferences.get("expanded-symbol-groups", "").split(","))
-		
-		for group in collection.groups:
-			self.__add_group(group)
-	
-	def __add_group(self, group):
-		model = Gtk.ListStore(GdkPixbuf.Pixbuf, str, object)		# icon, tooltip, Template
-		
-		for symbol in group.symbols:
-			try:
-				model.append([GdkPixbuf.Pixbuf.new_from_file(symbol.icon), str(symbol.template), symbol.template])
-			except GError, s:
-				print s
-		
-		view = Gtk.IconView(model=model)
-		view.set_pixbuf_column(0)
-		view.set_selection_mode(Gtk.SelectionMode.SINGLE)
-		view.connect("selection-changed", self.__on_symbol_selected)
-		view.set_item_width(-1)
-		view.set_spacing(0)
-		view.set_column_spacing(0)
-		view.set_row_spacing(0)
-		view.set_columns(-1)
-		view.set_text_column(-1)
-		
-		view.set_tooltip_column(1)		# this requires PyGTK 2.12
-		
-		view.show()
-		
-		expander = Gtk.Expander(label=group.label)
-		expander.add(view)
-		expander.show_all()
-		
-		if group.label in self.__expanded_groups:
-			expander.set_expanded(True)
-		
-		expander.connect("notify::expanded", self.__on_group_expanded, group.label)
-		
-		self.__box.pack_start(expander, False, False, 0)
-	
-	def __on_group_expanded(self, expander, paramSpec, group_label):
-		"""
-		The Expander for a symbol group has been expanded
-		"""
-		if expander.get_expanded():
-			self.__expanded_groups.add(group_label)
-		else:
-			self.__expanded_groups.remove(group_label)
-		
-		self.__preferences.set("expanded-symbol-groups", ",".join(self.__expanded_groups))
-	
-	def __on_symbol_selected(self, icon_view):
-		"""
-		A symbol has been selected
-		
-		@param icon_view: the Gtk.IconView 
-		"""
-		try: 
-			path = icon_view.get_selected_items()[0]
-			template = icon_view.get_model()[path][2]
-			
-			self.__context.active_editor.insert(template)
-			
-			icon_view.unselect_all()
-		except IndexError:
-			pass				# must be caught after unselect_all()
+    """
+    """
+    __log = getLogger("LaTeXSymbolMapView")
+
+    label = "Symbols"
+    icon = Gtk.Image.new_from_stock(Gtk.STOCK_INDEX,Gtk.IconSize.MENU)
+    scope = View.SCOPE_WINDOW
+
+    def init(self, context):
+        self.__log.debug("init")
+
+        self.__context = context
+        self.__preferences = Preferences()
+
+        scrolled = Gtk.ScrolledWindow()
+        scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+        scrolled.set_shadow_type(Gtk.ShadowType.NONE)
+
+        self.__box = Gtk.VBox()
+        scrolled.add_with_viewport(self.__box)
+
+        self.add(scrolled)
+        self.show_all()
+
+        self.__load_collection(SymbolCollection())
+
+    def __load_collection(self, collection):
+        self.__expanded_groups = set(self.__preferences.get("expanded-symbol-groups", "").split(","))
+
+        for group in collection.groups:
+            self.__add_group(group)
+
+    def __add_group(self, group):
+        model = Gtk.ListStore(GdkPixbuf.Pixbuf, str, object)        # icon, tooltip, Template
+
+        for symbol in group.symbols:
+            try:
+                model.append([GdkPixbuf.Pixbuf.new_from_file(symbol.icon), str(symbol.template), symbol.template])
+            except GError, s:
+                print s
+
+        view = Gtk.IconView(model=model)
+        view.set_pixbuf_column(0)
+        view.set_selection_mode(Gtk.SelectionMode.SINGLE)
+        view.connect("selection-changed", self.__on_symbol_selected)
+        view.set_item_width(-1)
+        view.set_spacing(0)
+        view.set_column_spacing(0)
+        view.set_row_spacing(0)
+        view.set_columns(-1)
+        view.set_text_column(-1)
+
+        view.set_tooltip_column(1)        # this requires PyGTK 2.12
+
+        view.show()
+
+        expander = Gtk.Expander(label=group.label)
+        expander.add(view)
+        expander.show_all()
+
+        if group.label in self.__expanded_groups:
+            expander.set_expanded(True)
+
+        expander.connect("notify::expanded", self.__on_group_expanded, group.label)
+
+        self.__box.pack_start(expander, False, False, 0)
+
+    def __on_group_expanded(self, expander, paramSpec, group_label):
+        """
+        The Expander for a symbol group has been expanded
+        """
+        if expander.get_expanded():
+            self.__expanded_groups.add(group_label)
+        else:
+            self.__expanded_groups.remove(group_label)
+
+        self.__preferences.set("expanded-symbol-groups", ",".join(self.__expanded_groups))
+
+    def __on_symbol_selected(self, icon_view):
+        """
+        A symbol has been selected
+
+        @param icon_view: the Gtk.IconView
+        """
+        try:
+            path = icon_view.get_selected_items()[0]
+            template = icon_view.get_model()[path][2]
+
+            self.__context.active_editor.insert(template)
+
+            icon_view.unselect_all()
+        except IndexError:
+            pass                # must be caught after unselect_all()
 
 
 from os import system
@@ -182,206 +182,206 @@ from outline import OutlineNode
 
 
 class LaTeXOutlineView(BaseOutlineView):
-	"""
-	A View showing an outline of the edited LaTeX document
-	"""
-	
-	_log = getLogger("LaTeXOutlineView")
-	
-	label = "Outline"
-	scope = View.SCOPE_EDITOR
-	
-	def __init__(self, context, editor):
-		BaseOutlineView.__init__(self, context, editor)
-		self._handlers = {}
-	
-	@property
-	def icon(self):
-		image = Gtk.Image()
-		image.set_from_file(find_resource("icons/outline.png"))
-		return image
-	
-	def init(self, context):
-		BaseOutlineView.init(self, context)
-		
-		self._offset_map = OutlineOffsetMap()
-		
-		# additional toolbar buttons
-		btn_graphics = Gtk.ToggleToolButton()
-		btn_graphics.set_icon_widget(Gtk.Image.new_from_file(find_resource("icons/tree_includegraphics.png")))
-		btn_graphics.set_tooltip_text("Show graphics")
-		self._toolbar.insert(btn_graphics, -1)
-		
-		btn_tables = Gtk.ToggleToolButton()
-		btn_tables.set_icon_widget(Gtk.Image.new_from_file(find_resource("icons/tree_table.png")))
-		btn_tables.set_tooltip_text("Show tables")
-		self._toolbar.insert(btn_tables, -1)
-		
-		btn_graphics.set_active(Preferences().get_bool("outline-show-graphics"))
-		btn_tables.set_active(Preferences().get_bool("outline-show-tables"))
-		
-		self._handlers[btn_graphics] = btn_graphics.connect("toggled", self._on_graphics_toggled)
-		self._handlers[btn_tables] = btn_tables.connect("toggled", self._on_tables_toggled)
-	
-	def set_outline(self, outline):
-		"""
-		Load a new outline model
-		"""
-		self._log.debug("set_outline")
-		
-		self.assure_init()
-		
-		self._save_state()
-		
-		self._offset_map = OutlineOffsetMap()
-		OutlineConverter().convert(self._store, outline, self._offset_map, self._editor.edited_file)
-		
-		self._restore_state()
-	
-	def _on_node_selected(self, node):
-		"""
-		An outline node has been selected
-		"""
-		if Preferences().get_bool("outline-connect-to-editor"):
-			if node.file == self._editor.edited_file:
-				self._editor.select(node.start, node.end)
-	
-	def _on_node_activated(self, node):
-		"""
-		An outline node has been double-clicked on
-		
-		@param node: an instance of latex.outline.OutlineNode
-		"""
-		if node.type == OutlineNode.GRAPHICS:
-			# use 'gnome-open' to open the graphics file
-			
-			target = node.value
-			
-			if not target:
-				return
-
-			# the way we use a mixture of File objects and filenames is not optimal...
-			if File.is_absolute(target):
-				filename = target
-			else:
-				filename = File.create_from_relative_path(target, node.file.dirname).path
-			
-			# an image may be specified without the extension
-			potential_extensions = ["", ".eps", ".pdf", ".jpg", ".jpeg", ".gif", ".png"]
-			
-			found = False
-			for ext in potential_extensions:
-				f = File(filename + ext)
-				if f.exists:
-					found = True
-					break
-			
-			if not found:
-				self._log.error("File not found: %s" % filename)
-				return
-			
-			system("gnome-open %s" % f.uri)
-			
-		else:
-			# open/activate the referenced file, if the node is 'foreign'
-			if node.file != self._editor.edited_file:
-				self._context.activate_editor(node.file)
-	
-	def _on_tables_toggled(self, toggle_button):
-		value = toggle_button.get_active()
-#		Settings().set("LatexOutlineTables", value)
-#		self.trigger("tablesToggled", value)
-		Preferences().set("outline-show-tables", value)
-	
-	def _on_graphics_toggled(self, toggle_button):
-		value = toggle_button.get_active()
-#		Settings().set("LatexOutlineGraphics", value)
-#		self.trigger("graphicsToggled", value)
-		Preferences().set("outline-show-graphics", value)
-	
-	def destroy(self):
-		for obj in self._handlers:
-			obj.disconnect(self._handlers[obj])
-		BaseOutlineView.destroy(self)
-	
+    """
+    A View showing an outline of the edited LaTeX document
+    """
+
+    _log = getLogger("LaTeXOutlineView")
+
+    label = "Outline"
+    scope = View.SCOPE_EDITOR
+
+    def __init__(self, context, editor):
+        BaseOutlineView.__init__(self, context, editor)
+        self._handlers = {}
+
+    @property
+    def icon(self):
+        image = Gtk.Image()
+        image.set_from_file(find_resource("icons/outline.png"))
+        return image
+
+    def init(self, context):
+        BaseOutlineView.init(self, context)
+
+        self._offset_map = OutlineOffsetMap()
+
+        # additional toolbar buttons
+        btn_graphics = Gtk.ToggleToolButton()
+        btn_graphics.set_icon_widget(Gtk.Image.new_from_file(find_resource("icons/tree_includegraphics.png")))
+        btn_graphics.set_tooltip_text("Show graphics")
+        self._toolbar.insert(btn_graphics, -1)
+
+        btn_tables = Gtk.ToggleToolButton()
+        btn_tables.set_icon_widget(Gtk.Image.new_from_file(find_resource("icons/tree_table.png")))
+        btn_tables.set_tooltip_text("Show tables")
+        self._toolbar.insert(btn_tables, -1)
+
+        btn_graphics.set_active(Preferences().get_bool("outline-show-graphics"))
+        btn_tables.set_active(Preferences().get_bool("outline-show-tables"))
+
+        self._handlers[btn_graphics] = btn_graphics.connect("toggled", self._on_graphics_toggled)
+        self._handlers[btn_tables] = btn_tables.connect("toggled", self._on_tables_toggled)
+
+    def set_outline(self, outline):
+        """
+        Load a new outline model
+        """
+        self._log.debug("set_outline")
+
+        self.assure_init()
+
+        self._save_state()
+
+        self._offset_map = OutlineOffsetMap()
+        OutlineConverter().convert(self._store, outline, self._offset_map, self._editor.edited_file)
+
+        self._restore_state()
+
+    def _on_node_selected(self, node):
+        """
+        An outline node has been selected
+        """
+        if Preferences().get_bool("outline-connect-to-editor"):
+            if node.file == self._editor.edited_file:
+                self._editor.select(node.start, node.end)
+
+    def _on_node_activated(self, node):
+        """
+        An outline node has been double-clicked on
+
+        @param node: an instance of latex.outline.OutlineNode
+        """
+        if node.type == OutlineNode.GRAPHICS:
+            # use 'gnome-open' to open the graphics file
+
+            target = node.value
+
+            if not target:
+                return
+
+            # the way we use a mixture of File objects and filenames is not optimal...
+            if File.is_absolute(target):
+                filename = target
+            else:
+                filename = File.create_from_relative_path(target, node.file.dirname).path
+
+            # an image may be specified without the extension
+            potential_extensions = ["", ".eps", ".pdf", ".jpg", ".jpeg", ".gif", ".png"]
+
+            found = False
+            for ext in potential_extensions:
+                f = File(filename + ext)
+                if f.exists:
+                    found = True
+                    break
+
+            if not found:
+                self._log.error("File not found: %s" % filename)
+                return
+
+            system("gnome-open %s" % f.uri)
+
+        else:
+            # open/activate the referenced file, if the node is 'foreign'
+            if node.file != self._editor.edited_file:
+                self._context.activate_editor(node.file)
+
+    def _on_tables_toggled(self, toggle_button):
+        value = toggle_button.get_active()
+#        Settings().set("LatexOutlineTables", value)
+#        self.trigger("tablesToggled", value)
+        Preferences().set("outline-show-tables", value)
+
+    def _on_graphics_toggled(self, toggle_button):
+        value = toggle_button.get_active()
+#        Settings().set("LatexOutlineGraphics", value)
+#        self.trigger("graphicsToggled", value)
+        Preferences().set("outline-show-graphics", value)
+
+    def destroy(self):
+        for obj in self._handlers:
+            obj.disconnect(self._handlers[obj])
+        BaseOutlineView.destroy(self)
+
 
 from os.path import basename
 
 
 class OutlineConverter(object):
-	"""
-	This creates a Gtk.TreeStore object from a LaTeX outline model
-	"""
-	
-	_ICON_LABEL = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/label.png"))
-	_ICON_TABLE = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_table.png"))
-	_ICON_GRAPHICS = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_includegraphics.png"))
-	
-	_LEVEL_ICONS = { 1 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_part.png")),
-				2 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_chapter.png")),
-				3 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_section.png")),
-				4 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_subsection.png")),
-				5 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_subsubsection.png")),
-				6 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_paragraph.png")),
-				7 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_paragraph.png")) }
-	
-	def __init__(self):
-		self._preferences = Preferences()
-	
-	def convert(self, tree_store, outline, offset_map, file):
-		"""
-		Convert an Outline object to a Gtk.TreeStore and update an OutlineOffsetMap
-		
-		@param tree_store: Gtk.TreeStore
-		@param outline: latex.outline.Outline
-		@param offset_map: outline.OutlineOffsetMap
-		@param file: the edited File (to identify foreign outline nodes)
-		"""
-		self._offsetMap = offset_map
-		self._treeStore = tree_store
-		self._treeStore.clear()
-		self._file = file
-		
-		self._append(None, outline.rootNode)
-	
-	def _append(self, parent, node):
-		"""
-		Recursively append an outline node to the TreeStore
-		
-		@param parent: a Gtk.TreeIter or None
-		@param node: an OutlineNode
-		"""
-		value = node.value
-		
-		color = self._preferences.get("light-foreground-color")
-		
-		if node.file and node.file != self._file:
-			value = "%s <span color='%s'>%s</span>" % (value, color, node.file.shortbasename)
-		
-		if node.type == OutlineNode.STRUCTURE:
-			icon = self._LEVEL_ICONS[node.level]
-			parent = self._treeStore.append(parent, [value, icon, node])
-		elif node.type == OutlineNode.LABEL:
-			parent = self._treeStore.append(parent, [value, self._ICON_LABEL, node])
-		elif node.type == OutlineNode.TABLE:
-			parent = self._treeStore.append(parent, [value, self._ICON_TABLE, node])
-		elif node.type == OutlineNode.GRAPHICS:
-			label = basename(node.value)
-			
-			if node.file and node.file != self._file:
-				label = "%s <span color='%s'>%s</span>" % (label, color, node.file.shortbasename)
-				
-			parent = self._treeStore.append(parent, [label, self._ICON_GRAPHICS, node])
-		
-		# store path in offset map for all non-foreign nodes
-		# check for parent to ignore root node
-		if parent and not node.foreign:
-			path = self._treeStore.get_path(parent)
-			self._offsetMap.put(node.start, path)
-			
-		for child in node:
-			self._append(parent, child)
-
-
-
-	
+    """
+    This creates a Gtk.TreeStore object from a LaTeX outline model
+    """
+
+    _ICON_LABEL = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/label.png"))
+    _ICON_TABLE = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_table.png"))
+    _ICON_GRAPHICS = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_includegraphics.png"))
+
+    _LEVEL_ICONS = { 1 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_part.png")),
+                2 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_chapter.png")),
+                3 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_section.png")),
+                4 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_subsection.png")),
+                5 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_subsubsection.png")),
+                6 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_paragraph.png")),
+                7 : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/tree_paragraph.png")) }
+
+    def __init__(self):
+        self._preferences = Preferences()
+
+    def convert(self, tree_store, outline, offset_map, file):
+        """
+        Convert an Outline object to a Gtk.TreeStore and update an OutlineOffsetMap
+
+        @param tree_store: Gtk.TreeStore
+        @param outline: latex.outline.Outline
+        @param offset_map: outline.OutlineOffsetMap
+        @param file: the edited File (to identify foreign outline nodes)
+        """
+        self._offsetMap = offset_map
+        self._treeStore = tree_store
+        self._treeStore.clear()
+        self._file = file
+
+        self._append(None, outline.rootNode)
+
+    def _append(self, parent, node):
+        """
+        Recursively append an outline node to the TreeStore
+
+        @param parent: a Gtk.TreeIter or None
+        @param node: an OutlineNode
+        """
+        value = node.value
+
+        color = self._preferences.get("light-foreground-color")
+
+        if node.file and node.file != self._file:
+            value = "%s <span color='%s'>%s</span>" % (value, color, node.file.shortbasename)
+
+        if node.type == OutlineNode.STRUCTURE:
+            icon = self._LEVEL_ICONS[node.level]
+            parent = self._treeStore.append(parent, [value, icon, node])
+        elif node.type == OutlineNode.LABEL:
+            parent = self._treeStore.append(parent, [value, self._ICON_LABEL, node])
+        elif node.type == OutlineNode.TABLE:
+            parent = self._treeStore.append(parent, [value, self._ICON_TABLE, node])
+        elif node.type == OutlineNode.GRAPHICS:
+            label = basename(node.value)
+
+            if node.file and node.file != self._file:
+                label = "%s <span color='%s'>%s</span>" % (label, color, node.file.shortbasename)
+
+            parent = self._treeStore.append(parent, [label, self._ICON_GRAPHICS, node])
+
+        # store path in offset map for all non-foreign nodes
+        # check for parent to ignore root node
+        if parent and not node.foreign:
+            path = self._treeStore.get_path(parent)
+            self._offsetMap.put(node.start, path)
+
+        for child in node:
+            self._append(parent, child)
+
+
+
+
diff --git a/latex/outline.py b/latex/outline.py
index 087746b..45e2178 100644
--- a/latex/outline.py
+++ b/latex/outline.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -33,304 +33,304 @@ from base.resources import find_resource
 
 
 class BaseOutlineView(SideView):
-	"""
-	Base class for the BibTeX and LaTeX outline views
-	"""
-	
-	__log = getLogger("BaseOutlineView")
-	
-	label = "Outline"
-	scope = View.SCOPE_EDITOR
-
-	def __init__(self, context, editor):
-		SideView.__init__(self, context)
-		self._editor = editor
-		self._base_handlers = {}
-		
-	@property
-	def icon(self):
-		image = Gtk.Image()
-		image.set_from_file(find_resource("icons/outline.png"))
-		return image
-	
-	def init(self, context):
-		self._log.debug("init")
-		
-		self._context = context
-		
-		self._preferences = Preferences()
-
-		# toolbar
-		
-		btn_follow = Gtk.ToggleToolButton.new_from_stock(Gtk.STOCK_CONNECT)
-		btn_follow.set_tooltip_text("Follow Editor")
-		btn_follow.set_active(self._preferences.get_bool("outline-connect-to-editor"))
-		self._base_handlers[btn_follow] = btn_follow.connect("toggled", self._on_follow_toggled)
-		
-		btn_expand = Gtk.ToolButton.new_from_stock(Gtk.STOCK_ZOOM_IN)
-		btn_expand.set_tooltip_text("Expand All")
-		self._base_handlers[btn_expand] = btn_expand.connect("clicked", self._on_expand_clicked)
-		
-		btn_collapse = Gtk.ToolButton.new_from_stock(Gtk.STOCK_ZOOM_OUT)
-		btn_collapse.set_tooltip_text("Collapse All")
-		self._base_handlers[btn_collapse] = btn_collapse.connect("clicked", self._on_collapse_clicked)
-		
-		self._toolbar = Gtk.Toolbar()
-		self._toolbar.set_style(Gtk.ToolbarStyle.ICONS)
-		self._toolbar.set_icon_size(Gtk.IconSize.MENU)
-		self._toolbar.insert(btn_follow, -1)
-		self._toolbar.insert(Gtk.SeparatorToolItem(), -1)
-		self._toolbar.insert(btn_expand, -1)
-		self._toolbar.insert(btn_collapse, -1)
-		self._toolbar.insert(Gtk.SeparatorToolItem(), -1)
-		
-		self.pack_start(self._toolbar, False, True, 0)
-		
-		# tree view
-		
-		column = Gtk.TreeViewColumn()
-		
-		pixbuf_renderer = Gtk.CellRendererPixbuf()
-		column.pack_start(pixbuf_renderer, False)
-		column.add_attribute(pixbuf_renderer, "pixbuf", 1)
-		
-		text_renderer = Gtk.CellRendererText()
-		column.pack_start(text_renderer, True)
-		column.add_attribute(text_renderer, "markup", 0)
-		
-		self._offset_map = OutlineOffsetMap()
-		
-		self._store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object)	# label, icon, node object
-		
-		self._view = Gtk.TreeView(model=self._store)
-		self._view.append_column(column)
-		self._view.set_headers_visible(False)
-		self._cursor_changed_id = self._view.connect("cursor-changed", self._on_cursor_changed)
-		self._base_handlers[self._view] = self._view.connect("row-activated", self._on_row_activated)
-		
-		scrolled = Gtk.ScrolledWindow()
-		scrolled.add(self._view)
-		scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
-		
-		self.pack_start(scrolled, True, True, 0)
-
-		# theme like gtk3
-		ctx = scrolled.get_style_context()
-		ctx.set_junction_sides(Gtk.JunctionSides.TOP)
-
-		ctx = self._toolbar.get_style_context()
-		ctx.set_junction_sides(Gtk.JunctionSides.TOP | Gtk.JunctionSides.BOTTOM)
-		ctx.add_class("inline-toolbar")
-
-		# this holds a list of the currently expanded paths
-		self._expandedPaths = None
-		
-		self.show_all()
-	
-	def _on_follow_toggled(self, toggle_button):
-		value = toggle_button.get_active()
-		self._preferences.set("outline-connect-to-editor", value)
-	
-	def _on_expand_clicked(self, button):
-		self._view.expand_all()
-	
-	def _on_collapse_clicked(self, button):
-		self._view.collapse_all()
-	
-	def select_path_by_offset(self, offset):
-		"""
-		Select the path corresponding to a given offset in the source
-		
-		Called by the Editor
-		"""
-		self.assure_init()
-		
-		try:
-			path = self._offset_map.lookup(offset)
-			self._select_path(path)
-		except KeyError:
-			pass
-	
-	def _save_state(self):
-		"""
-		Save the current expand state
-		"""
-		self._expanded_paths = []
-		self._view.map_expanded_rows(self._save_state_map_function,None)
-	
-	def _save_state_map_function(self, view, path):
-		"""
-		Mapping function for saving the current expand state
-		"""
-		self._expanded_paths.append(path)
-	
-	def _restore_state(self):
-		"""
-		Restore the last expand state
-		"""
-		self._view.collapse_all()
-		
-		if self._expanded_paths:
-			for path in self._expanded_paths:
-				self._view.expand_to_path(path)
-#		else:
-#			self._view.expand_to_path((0,))
-			
-	def _on_cursor_changed(self, view):
-		store, it = view.get_selection().get_selected()
-		if not it: 
-			return
-			
-		outline_node = store.get_value(it, 2)
-		
-		self._on_node_selected(outline_node)
-	
-	def _on_row_activated(self, view, path, column):
-		it = self._store.get_iter(path)
-		node = self._store.get(it, 2)[0]
-		
-		self._on_node_activated(node)
-	
-	def _select_path(self, path):
-		"""
-		Expand a path and select the last node
-		"""
-		# disconnect from 'cursor-changed'
-		self._view.disconnect(self._cursor_changed_id)
-		
-		# select path
-		self._view.expand_to_path(path)
-		self._view.set_cursor(path, None, False)
-		
-		# connect to 'cursor-changed' again
-		self._cursor_changed_id = self._view.connect("cursor-changed", self._on_cursor_changed)
-	
-	#
-	# methods to be overridden by the subclass
-	#
-	
-	def _on_node_selected(self, node):
-		"""
-		To be overridden
-		"""
-	
-	def _on_node_activated(self, node):
-		"""
-		To be overridden
-		"""
-		
-	def set_outline(self, outline):
-		"""
-		Load a new outline model
-		
-		To be overridden
-		"""
-
-	def destroy(self):
-		self._view.disconnect(self._cursor_changed_id)
-		for obj in self._base_handlers:
-			obj.disconnect(self._base_handlers[obj])
-		del self._editor
-		SideView.destroy(self)
-		
+    """
+    Base class for the BibTeX and LaTeX outline views
+    """
+
+    __log = getLogger("BaseOutlineView")
+
+    label = "Outline"
+    scope = View.SCOPE_EDITOR
+
+    def __init__(self, context, editor):
+        SideView.__init__(self, context)
+        self._editor = editor
+        self._base_handlers = {}
+
+    @property
+    def icon(self):
+        image = Gtk.Image()
+        image.set_from_file(find_resource("icons/outline.png"))
+        return image
+
+    def init(self, context):
+        self._log.debug("init")
+
+        self._context = context
+
+        self._preferences = Preferences()
+
+        # toolbar
+
+        btn_follow = Gtk.ToggleToolButton.new_from_stock(Gtk.STOCK_CONNECT)
+        btn_follow.set_tooltip_text("Follow Editor")
+        btn_follow.set_active(self._preferences.get_bool("outline-connect-to-editor"))
+        self._base_handlers[btn_follow] = btn_follow.connect("toggled", self._on_follow_toggled)
+
+        btn_expand = Gtk.ToolButton.new_from_stock(Gtk.STOCK_ZOOM_IN)
+        btn_expand.set_tooltip_text("Expand All")
+        self._base_handlers[btn_expand] = btn_expand.connect("clicked", self._on_expand_clicked)
+
+        btn_collapse = Gtk.ToolButton.new_from_stock(Gtk.STOCK_ZOOM_OUT)
+        btn_collapse.set_tooltip_text("Collapse All")
+        self._base_handlers[btn_collapse] = btn_collapse.connect("clicked", self._on_collapse_clicked)
+
+        self._toolbar = Gtk.Toolbar()
+        self._toolbar.set_style(Gtk.ToolbarStyle.ICONS)
+        self._toolbar.set_icon_size(Gtk.IconSize.MENU)
+        self._toolbar.insert(btn_follow, -1)
+        self._toolbar.insert(Gtk.SeparatorToolItem(), -1)
+        self._toolbar.insert(btn_expand, -1)
+        self._toolbar.insert(btn_collapse, -1)
+        self._toolbar.insert(Gtk.SeparatorToolItem(), -1)
+
+        self.pack_start(self._toolbar, False, True, 0)
+
+        # tree view
+
+        column = Gtk.TreeViewColumn()
+
+        pixbuf_renderer = Gtk.CellRendererPixbuf()
+        column.pack_start(pixbuf_renderer, False)
+        column.add_attribute(pixbuf_renderer, "pixbuf", 1)
+
+        text_renderer = Gtk.CellRendererText()
+        column.pack_start(text_renderer, True)
+        column.add_attribute(text_renderer, "markup", 0)
+
+        self._offset_map = OutlineOffsetMap()
+
+        self._store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object)    # label, icon, node object
+
+        self._view = Gtk.TreeView(model=self._store)
+        self._view.append_column(column)
+        self._view.set_headers_visible(False)
+        self._cursor_changed_id = self._view.connect("cursor-changed", self._on_cursor_changed)
+        self._base_handlers[self._view] = self._view.connect("row-activated", self._on_row_activated)
+
+        scrolled = Gtk.ScrolledWindow()
+        scrolled.add(self._view)
+        scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+
+        self.pack_start(scrolled, True, True, 0)
+
+        # theme like gtk3
+        ctx = scrolled.get_style_context()
+        ctx.set_junction_sides(Gtk.JunctionSides.TOP)
+
+        ctx = self._toolbar.get_style_context()
+        ctx.set_junction_sides(Gtk.JunctionSides.TOP | Gtk.JunctionSides.BOTTOM)
+        ctx.add_class("inline-toolbar")
+
+        # this holds a list of the currently expanded paths
+        self._expandedPaths = None
+
+        self.show_all()
+
+    def _on_follow_toggled(self, toggle_button):
+        value = toggle_button.get_active()
+        self._preferences.set("outline-connect-to-editor", value)
+
+    def _on_expand_clicked(self, button):
+        self._view.expand_all()
+
+    def _on_collapse_clicked(self, button):
+        self._view.collapse_all()
+
+    def select_path_by_offset(self, offset):
+        """
+        Select the path corresponding to a given offset in the source
+
+        Called by the Editor
+        """
+        self.assure_init()
+
+        try:
+            path = self._offset_map.lookup(offset)
+            self._select_path(path)
+        except KeyError:
+            pass
+
+    def _save_state(self):
+        """
+        Save the current expand state
+        """
+        self._expanded_paths = []
+        self._view.map_expanded_rows(self._save_state_map_function,None)
+
+    def _save_state_map_function(self, view, path):
+        """
+        Mapping function for saving the current expand state
+        """
+        self._expanded_paths.append(path)
+
+    def _restore_state(self):
+        """
+        Restore the last expand state
+        """
+        self._view.collapse_all()
+
+        if self._expanded_paths:
+            for path in self._expanded_paths:
+                self._view.expand_to_path(path)
+#        else:
+#            self._view.expand_to_path((0,))
+
+    def _on_cursor_changed(self, view):
+        store, it = view.get_selection().get_selected()
+        if not it:
+            return
+
+        outline_node = store.get_value(it, 2)
+
+        self._on_node_selected(outline_node)
+
+    def _on_row_activated(self, view, path, column):
+        it = self._store.get_iter(path)
+        node = self._store.get(it, 2)[0]
+
+        self._on_node_activated(node)
+
+    def _select_path(self, path):
+        """
+        Expand a path and select the last node
+        """
+        # disconnect from 'cursor-changed'
+        self._view.disconnect(self._cursor_changed_id)
+
+        # select path
+        self._view.expand_to_path(path)
+        self._view.set_cursor(path, None, False)
+
+        # connect to 'cursor-changed' again
+        self._cursor_changed_id = self._view.connect("cursor-changed", self._on_cursor_changed)
+
+    #
+    # methods to be overridden by the subclass
+    #
+
+    def _on_node_selected(self, node):
+        """
+        To be overridden
+        """
+
+    def _on_node_activated(self, node):
+        """
+        To be overridden
+        """
+
+    def set_outline(self, outline):
+        """
+        Load a new outline model
+
+        To be overridden
+        """
+
+    def destroy(self):
+        self._view.disconnect(self._cursor_changed_id)
+        for obj in self._base_handlers:
+            obj.disconnect(self._base_handlers[obj])
+        del self._editor
+        SideView.destroy(self)
+
 
 class Item(object):
-	def __init__(self, key, value):
-		self.key = key
-		self.value = value
-		
-	def __cmp__(self, item):
-		return self.key.__cmp__(item.key)
+    def __init__(self, key, value):
+        self.key = key
+        self.value = value
+
+    def __cmp__(self, item):
+        return self.key.__cmp__(item.key)
 
 
 class OffsetLookupTree(object):
-	def __init__(self):
-		self.items = []
-	
-	def insert(self, key, value):
-		"""
-		Insert a value
-		"""
-		self.items.append(Item(key, value))
-	
-	def prepare(self):
-		"""
-		Prepare the structure for being searched
-		"""
-		self.items.sort()
-	
-	def find(self, key):
-		"""
-		Find a value by its key
-		"""
-		return self._find(key, 0, len(self.items) - 1)
-	
-	def _find(self, key, lo, hi):
-		if hi - lo == 0:
-			raise KeyError
-		
-		i = (hi - lo)/2
-		item = self.items[i]
-		if item.key == key:
-			return item.value
-		elif item.key > key:
-			return self._find(key, lo, i - 1)
-		elif item.key < key:
-			return self._find(key, i + 1, hi)
+    def __init__(self):
+        self.items = []
+
+    def insert(self, key, value):
+        """
+        Insert a value
+        """
+        self.items.append(Item(key, value))
+
+    def prepare(self):
+        """
+        Prepare the structure for being searched
+        """
+        self.items.sort()
+
+    def find(self, key):
+        """
+        Find a value by its key
+        """
+        return self._find(key, 0, len(self.items) - 1)
+
+    def _find(self, key, lo, hi):
+        if hi - lo == 0:
+            raise KeyError
+
+        i = (hi - lo)/2
+        item = self.items[i]
+        if item.key == key:
+            return item.value
+        elif item.key > key:
+            return self._find(key, lo, i - 1)
+        elif item.key < key:
+            return self._find(key, i + 1, hi)
 
 
 class OutlineOffsetMap(object):
-	"""
-	This stores a mapping from the start offsets of outline elements 
-	to paths in the outline tree.
-	
-	We need this for the 'connect-outline-to-editor' feature.
-	
-	We may not use a simple dictionary for that because we need some
-	kind of neighborhood lookup as we never get the exact offsets.
-	"""
-	
-	# TODO: find a faster structure for this - this is a tree with a special
-	# find method
-	
-	def __init__(self):
-		self._map = {}
-	
-	def clear(self):
-		self._map = {}
-	
-	def put(self, offset, path):
-		self._map[offset] = path
-	
-	def lookup(self, offset):
-		"""
-		Returns the outline element containing a certain offset
-		"""
-		
-		# sort offsets
-		offsets = self._map.keys()
-		offsets.sort()
-		
-		# find nearest offset
-		nearestOffset = None
-		for o in offsets:
-			if o > offset:
-				break
-			nearestOffset = o
-		
-		if not nearestOffset:
-			raise KeyError
-		
-		return self._map[nearestOffset]
-	
-	def __str__(self):
-		s = "<OutlineOffsetMap>"
-		
-		ofs = self._map.keys()
-		ofs.sort()
-		
-		for o in ofs:
-			s += "\n\t%s : %s" % (o, self._map[o])
-		s += "\n</OutlineOffsetMap>"
-		return s
+    """
+    This stores a mapping from the start offsets of outline elements
+    to paths in the outline tree.
+
+    We need this for the 'connect-outline-to-editor' feature.
+
+    We may not use a simple dictionary for that because we need some
+    kind of neighborhood lookup as we never get the exact offsets.
+    """
+
+    # TODO: find a faster structure for this - this is a tree with a special
+    # find method
+
+    def __init__(self):
+        self._map = {}
+
+    def clear(self):
+        self._map = {}
+
+    def put(self, offset, path):
+        self._map[offset] = path
+
+    def lookup(self, offset):
+        """
+        Returns the outline element containing a certain offset
+        """
+
+        # sort offsets
+        offsets = self._map.keys()
+        offsets.sort()
+
+        # find nearest offset
+        nearestOffset = None
+        for o in offsets:
+            if o > offset:
+                break
+            nearestOffset = o
+
+        if not nearestOffset:
+            raise KeyError
+
+        return self._map[nearestOffset]
+
+    def __str__(self):
+        s = "<OutlineOffsetMap>"
+
+        ofs = self._map.keys()
+        ofs.sort()
+
+        for o in ofs:
+            s += "\n\t%s : %s" % (o, self._map[o])
+        s += "\n</OutlineOffsetMap>"
+        return s
 
diff --git a/latex/preferences/__init__.py b/latex/preferences/__init__.py
index ac0531c..bb4bf7c 100644
--- a/latex/preferences/__init__.py
+++ b/latex/preferences/__init__.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -33,35 +33,35 @@ from ..util import singleton
 @singleton
 class Preferences(GObject.GObject):
 
-	__gsignals__ = {
-		"preferences-changed": (
-			GObject.SignalFlags.RUN_LAST, None, [str, str]),
-	}
+    __gsignals__ = {
+        "preferences-changed": (
+            GObject.SignalFlags.RUN_LAST, None, [str, str]),
+    }
 
-	"""
-	A simple map storing preferences as key-value-pairs
-	"""
-	
-	_log = logging.getLogger("Preferences")
-	
-	TEMPLATE_DIR = os.path.join(GLib.get_user_data_dir(), "gedit", "latex", "templates")
-	
-	def __init__(self):
-		GObject.GObject.__init__(self)
-		self._settings = Gio.Settings("org.gnome.gedit.plugins.latex")
-		self._log.debug("Constructed")
-	
-	def get(self, key, default=None):
-		if default:
-			return default
-		return self._settings[key]
-	
-	def get_bool(self, key):
-		return self._settings[key]
-	
-	def set(self, key, value):
-		self._settings[key] = value
-		self.emit("preferences-changed", str(key), str(value))
-	
-	def save(self):
-		pass
+    """
+    A simple map storing preferences as key-value-pairs
+    """
+
+    _log = logging.getLogger("Preferences")
+
+    TEMPLATE_DIR = os.path.join(GLib.get_user_data_dir(), "gedit", "latex", "templates")
+
+    def __init__(self):
+        GObject.GObject.__init__(self)
+        self._settings = Gio.Settings("org.gnome.gedit.plugins.latex")
+        self._log.debug("Constructed")
+
+    def get(self, key, default=None):
+        if default:
+            return default
+        return self._settings[key]
+
+    def get_bool(self, key):
+        return self._settings[key]
+
+    def set(self, key, value):
+        self._settings[key] = value
+        self.emit("preferences-changed", str(key), str(value))
+
+    def save(self):
+        pass
diff --git a/latex/preferences/dialog.py b/latex/preferences/dialog.py
index 101e5bb..80ba7c0 100644
--- a/latex/preferences/dialog.py
+++ b/latex/preferences/dialog.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -34,487 +34,487 @@ from . import Preferences
 from .tools import ToolPreferences
 
 def _insert_column_with_attributes(view, pos, title, rend, **kwargs):
-	print kwargs
-	tv = Gtk.TreeViewColumn(title)
-	tv.pack_start(rend, True)
-	for k in kwargs:
-		tv.add_attribute(rend, k, kwargs[k])
-	view.insert_column(tv, pos)
+    print kwargs
+    tv = Gtk.TreeViewColumn(title)
+    tv.pack_start(rend, True)
+    for k in kwargs:
+        tv.add_attribute(rend, k, kwargs[k])
+    view.insert_column(tv, pos)
 
 class PreferencesSpinButtonProxy(object):
-	def __init__(self, widget, key):
-		"""
-		@param widget: a SpinButton widget
-		@param key: 
-		@param default_value: 
-		"""
-		self._widget = widget
-		self._key = key
-		self._preferences = Preferences()
-		
-		self._widget.set_value(int(self._preferences.get(key)))
-		
-		self._widget.connect("value-changed", self._on_value_changed)
-	
-	def _on_value_changed(self, spin_button):
-		self._preferences.set(self._key, spin_button.get_value_as_int())
-		
+    def __init__(self, widget, key):
+        """
+        @param widget: a SpinButton widget
+        @param key:
+        @param default_value:
+        """
+        self._widget = widget
+        self._key = key
+        self._preferences = Preferences()
+
+        self._widget.set_value(int(self._preferences.get(key)))
+
+        self._widget.connect("value-changed", self._on_value_changed)
+
+    def _on_value_changed(self, spin_button):
+        self._preferences.set(self._key, spin_button.get_value_as_int())
+
 class ConfigureToolDialog(GladeInterface):
-	"""
-	Wraps the dialog for setting up a Tool
-	"""
-	
-	filename = find_resource("ui/configure_tool.ui")
-	
-	_dialog = None
-	
-	def run(self, tool):
-		"""
-		Runs the dialog and returns the updated Tool or None on abort
-		"""
-		dialog = self._get_dialog()
-		
-		self._tool = tool
-		
-		# load Tool
-		self._entry_label.set_text(tool.label)
-		self._entry_description.set_text(tool.description)
-		
-		self._store_job.clear()
-		for job in tool.jobs:
-			self._store_job.append([job.command_template, job.must_succeed, job.post_processor.name])
-		
-		self._store_extension.clear()
-		for ext in tool.extensions:
-			self._store_extension.append([ext])
-		
-		if tool.accelerator is None:
-			self._radio_user_accel.set_active(False)
-			self._entry_accel.set_text("")
-		else:
-			self._radio_user_accel.set_active(True)
-			self._entry_accel.set_text(tool.accelerator)
-		
-		if dialog.run() == 1:
-			#
-			# okay clicked - update the Tool object
-			#
-			tool.label = self._entry_label.get_text()
-			tool.description = self._entry_description.get_text()
-			
-			tool.jobs = []
-			for row in self._store_job:
-				pp_class = self._tool_preferences.POST_PROCESSORS[row[2]]
-				tool.jobs.append(Job(row[0], row[1], pp_class))
-				
-			tool.extensions = []
-			for row in self._store_extension:
-				tool.extensions.append(row[0])
-			
-			# TODO: validate accelerator!
-			if self._radio_user_accel.get_active():
-				tool.accelerator = self._entry_accel.get_text()
-			else:
-				tool.accelerator = None
-			
-			return tool
-		else:
-			return None
-	
-	def _get_dialog(self):
-		if not self._dialog:
-			# 
-			# build the dialog
-			#
-			self._preferences = Preferences()
-			
-			self._dialog = self.find_widget("dialogConfigureTool")
-			self._button_okay = self.find_widget("buttonOkay")
-			self._labelProfileValidate = self.find_widget("labelHint")
-			
-			#
-			# label
-			#
-			self._entry_label = self.find_widget("entryLabel")
-			
-			#
-			# jobs
-			#
-			self._entry_new_job = self.find_widget("entryNewJob")
-			self._button_add_job = self.find_widget("buttonAddJob")
-			self._button_remove_job = self.find_widget("buttonRemoveJob")
-			self._button_job_up = self.find_widget("buttonMoveUpJob")
-			self._view_job = self.find_widget("treeviewJob")
-			
-			self._store_job = Gtk.ListStore(str, bool, str)   # command, mustSucceed, postProcessor
-			
-			self._view_job.set_model(self._store_job)
-			
-			mustSucceedRenderer = Gtk.CellRendererToggle()
-			mustSucceedRenderer.connect("toggled", self._on_must_succeed_toggled)
-			
-			commandRenderer = Gtk.CellRendererText()
-			commandRenderer.connect("edited", self._on_job_command_edited)
-
-			self._store_pp = Gtk.ListStore(str)
-			for p in self._tool_preferences.POST_PROCESSORS.iterkeys():
-				self._store_pp.append([p])
-			
-			ppRenderer = Gtk.CellRendererCombo()
-			ppRenderer.set_property("editable", True)
-			ppRenderer.set_property("model", self._store_pp)
-			ppRenderer.set_property("text_column", 0)
-			ppRenderer.set_property("has_entry", False)
-			
-			ppRenderer.connect("edited", self._on_job_pp_edited)
-			
-			_insert_column_with_attributes(self._view_job, -1, "Command", commandRenderer, text=0, editable=True)
-			_insert_column_with_attributes(self._view_job, -1, "Must Succeed", mustSucceedRenderer, active=1)
-			_insert_column_with_attributes(self._view_job, -1, "Post-Processor", ppRenderer, text=2)
-			
-			#
-			# description
-			#
-			self._entry_description = self.find_widget("entryDescription")
-			
-			#
-			# extensions
-			#
-			self._entry_new_extension = self.find_widget("entryNewExtension")
-			
-			self._store_extension = Gtk.ListStore(str)
-			
-			self._view_extension = self.find_widget("treeviewExtension")
-			self._view_extension.set_model(self._store_extension)
-			_insert_column_with_attributes(self._view_extension, -1, "", Gtk.CellRendererText(), text=0)
-			self._view_extension.set_headers_visible(False)
-			
-			self._button_add_extension = self.find_widget("buttonAddExtension")
-			self._button_remove_extension = self.find_widget("buttonRemoveExtension")
-			
-			self._radio_user_accel = self.find_widget("radioAccelUser")
-			self._entry_accel = self.find_widget("entryAccel")
-			
-			self.connect_signals({ "on_entryNewJob_changed" : self._on_new_job_changed,
-								   "on_entryNewExtension_changed" : self._on_new_extension_changed,
-								   "on_buttonAddJob_clicked" : self._on_add_job_clicked,
-								   "on_buttonRemoveJob_clicked" : self._on_remove_job_clicked,
-								   "on_treeviewJob_cursor_changed" : self._on_job_cursor_changed,
-								   "on_treeviewExtension_cursor_changed" : self._on_extension_cursor_changed,
-								   "on_buttonAbort_clicked" : self._on_abort_clicked,
-								   "on_buttonOkay_clicked" : self._on_okay_clicked,
-								   "on_buttonRemoveExtension_clicked" : self._on_remove_extension_clicked,
-								   "on_buttonAddExtension_clicked" : self._on_add_extension_clicked,
-								   "on_buttonMoveUpJob_clicked" : self._on_move_up_job_clicked,
-								   "on_radioAccelUser_toggled" : self._on_accel_user_toggled })
-		
-		return self._dialog
-	
-	def _on_accel_user_toggled(self, togglebutton):
-		enabled = togglebutton.get_active()
-		self._entry_accel.set_sensitive(enabled)
-	
-	def _on_move_up_job_clicked(self, button):
-		store, iter = self._view_job.get_selection().get_selected()
-		store.swap(iter)
-	
-	def _on_add_extension_clicked(self, button):
-		extension = self._entry_new_extension.get_text()
-		self._store_extension.append([extension])
-	
-	def _on_remove_extension_clicked(self, button):
-		store, it = self._view_extension.get_selection().get_selected()
-		store.remove(it)
-	
-	def _on_job_command_edited(self, renderer, path, text):
-		"""
-		The command template has been edited
-		"""
-		self._store_job.set(self._store_job.get_iter_from_string(path), 0, text)
-	
-	def _on_job_pp_edited(self, renderer, path, text):
-		"""
-		Another post processor has been selected
-		"""
-		self._store_job.set(self._store_job.get_iter_from_string(path), 2, text)
-	
-	def _on_must_succeed_toggled(self, renderer, path):
-		"""
-		The 'must succeed' flag has been toggled
-		"""
-		value = self._store_job.get(self._store_job.get_iter_from_string(path), 1)[0]
-		self._store_job.set(self._store_job.get_iter_from_string(path), 1, not value)
-	
-	def _on_add_job_clicked(self, button):
-		"""
-		Add a new job
-		"""
-		command = self._entry_new_job.get_text()
-		self._store_job.append([command, True, "GenericPostProcessor"])
-	
-	def _on_remove_job_clicked(self, button):
-		store, it = self._view_job.get_selection().get_selected()
-		store.remove(it)
-	
-	def _on_new_job_changed(self, widget):
-		"""
-		The entry for a new command template has changed
-		"""
-		self._button_add_job.set_sensitive(len(self._entry_new_job.get_text()) > 0)
-	
-	def _on_new_extension_changed(self, widget):
-		self._button_add_extension.set_sensitive(len(self._entry_new_extension.get_text()) > 0)
-	
-	def _on_job_cursor_changed(self, tree_view):
-		store, iter = tree_view.get_selection().get_selected()
-		if not iter: 
-			return
-		self._button_remove_job.set_sensitive(True)
-		
-		first_row_selected = (store.get_string_from_iter(iter) == "0")
-		self._button_job_up.set_sensitive(not first_row_selected)
-	
-	def _on_extension_cursor_changed(self, tree_view):
-		store, it = tree_view.get_selection().get_selected()
-		if not it: 
-			return
-		self._button_remove_extension.set_sensitive(True)
-	
-	def _on_abort_clicked(self, button):
-		self._dialog.hide()
-	
-	def _on_okay_clicked(self, button):
-		self._dialog.hide()
-	
-	def _validate_tool(self):
-		"""
-		Validate the dialog contents
-		"""
-		errors = []
-		
-		if len(self._store_job) == 0:
-			errors.append("You have not specified any jobs.")
-		
-		if len(errors):
-			self._buttonApply.set_sensitive(False)
-		else:
-			self._buttonApply.set_sensitive(True)
-		
-		if len(errors) == 1:
-			self._labelProfileValidate.set_markup(errors[0])
-		elif len(errors) > 1:
-			self._labelProfileValidate.set_markup("\n".join([" * %s" % e for e in errors]))
-		else:
-			self._labelProfileValidate.set_markup("Remember to run all commands in batch mode (e.g. append <tt>-interaction batchmode</tt> to <tt>latex</tt>)")
-	
+    """
+    Wraps the dialog for setting up a Tool
+    """
+
+    filename = find_resource("ui/configure_tool.ui")
+
+    _dialog = None
+
+    def run(self, tool):
+        """
+        Runs the dialog and returns the updated Tool or None on abort
+        """
+        dialog = self._get_dialog()
+
+        self._tool = tool
+
+        # load Tool
+        self._entry_label.set_text(tool.label)
+        self._entry_description.set_text(tool.description)
+
+        self._store_job.clear()
+        for job in tool.jobs:
+            self._store_job.append([job.command_template, job.must_succeed, job.post_processor.name])
+
+        self._store_extension.clear()
+        for ext in tool.extensions:
+            self._store_extension.append([ext])
+
+        if tool.accelerator is None:
+            self._radio_user_accel.set_active(False)
+            self._entry_accel.set_text("")
+        else:
+            self._radio_user_accel.set_active(True)
+            self._entry_accel.set_text(tool.accelerator)
+
+        if dialog.run() == 1:
+            #
+            # okay clicked - update the Tool object
+            #
+            tool.label = self._entry_label.get_text()
+            tool.description = self._entry_description.get_text()
+
+            tool.jobs = []
+            for row in self._store_job:
+                pp_class = self._tool_preferences.POST_PROCESSORS[row[2]]
+                tool.jobs.append(Job(row[0], row[1], pp_class))
+
+            tool.extensions = []
+            for row in self._store_extension:
+                tool.extensions.append(row[0])
+
+            # TODO: validate accelerator!
+            if self._radio_user_accel.get_active():
+                tool.accelerator = self._entry_accel.get_text()
+            else:
+                tool.accelerator = None
+
+            return tool
+        else:
+            return None
+
+    def _get_dialog(self):
+        if not self._dialog:
+            #
+            # build the dialog
+            #
+            self._preferences = Preferences()
+
+            self._dialog = self.find_widget("dialogConfigureTool")
+            self._button_okay = self.find_widget("buttonOkay")
+            self._labelProfileValidate = self.find_widget("labelHint")
+
+            #
+            # label
+            #
+            self._entry_label = self.find_widget("entryLabel")
+
+            #
+            # jobs
+            #
+            self._entry_new_job = self.find_widget("entryNewJob")
+            self._button_add_job = self.find_widget("buttonAddJob")
+            self._button_remove_job = self.find_widget("buttonRemoveJob")
+            self._button_job_up = self.find_widget("buttonMoveUpJob")
+            self._view_job = self.find_widget("treeviewJob")
+
+            self._store_job = Gtk.ListStore(str, bool, str)   # command, mustSucceed, postProcessor
+
+            self._view_job.set_model(self._store_job)
+
+            mustSucceedRenderer = Gtk.CellRendererToggle()
+            mustSucceedRenderer.connect("toggled", self._on_must_succeed_toggled)
+
+            commandRenderer = Gtk.CellRendererText()
+            commandRenderer.connect("edited", self._on_job_command_edited)
+
+            self._store_pp = Gtk.ListStore(str)
+            for p in self._tool_preferences.POST_PROCESSORS.iterkeys():
+                self._store_pp.append([p])
+
+            ppRenderer = Gtk.CellRendererCombo()
+            ppRenderer.set_property("editable", True)
+            ppRenderer.set_property("model", self._store_pp)
+            ppRenderer.set_property("text_column", 0)
+            ppRenderer.set_property("has_entry", False)
+
+            ppRenderer.connect("edited", self._on_job_pp_edited)
+
+            _insert_column_with_attributes(self._view_job, -1, "Command", commandRenderer, text=0, editable=True)
+            _insert_column_with_attributes(self._view_job, -1, "Must Succeed", mustSucceedRenderer, active=1)
+            _insert_column_with_attributes(self._view_job, -1, "Post-Processor", ppRenderer, text=2)
+
+            #
+            # description
+            #
+            self._entry_description = self.find_widget("entryDescription")
+
+            #
+            # extensions
+            #
+            self._entry_new_extension = self.find_widget("entryNewExtension")
+
+            self._store_extension = Gtk.ListStore(str)
+
+            self._view_extension = self.find_widget("treeviewExtension")
+            self._view_extension.set_model(self._store_extension)
+            _insert_column_with_attributes(self._view_extension, -1, "", Gtk.CellRendererText(), text=0)
+            self._view_extension.set_headers_visible(False)
+
+            self._button_add_extension = self.find_widget("buttonAddExtension")
+            self._button_remove_extension = self.find_widget("buttonRemoveExtension")
+
+            self._radio_user_accel = self.find_widget("radioAccelUser")
+            self._entry_accel = self.find_widget("entryAccel")
+
+            self.connect_signals({ "on_entryNewJob_changed" : self._on_new_job_changed,
+                                   "on_entryNewExtension_changed" : self._on_new_extension_changed,
+                                   "on_buttonAddJob_clicked" : self._on_add_job_clicked,
+                                   "on_buttonRemoveJob_clicked" : self._on_remove_job_clicked,
+                                   "on_treeviewJob_cursor_changed" : self._on_job_cursor_changed,
+                                   "on_treeviewExtension_cursor_changed" : self._on_extension_cursor_changed,
+                                   "on_buttonAbort_clicked" : self._on_abort_clicked,
+                                   "on_buttonOkay_clicked" : self._on_okay_clicked,
+                                   "on_buttonRemoveExtension_clicked" : self._on_remove_extension_clicked,
+                                   "on_buttonAddExtension_clicked" : self._on_add_extension_clicked,
+                                   "on_buttonMoveUpJob_clicked" : self._on_move_up_job_clicked,
+                                   "on_radioAccelUser_toggled" : self._on_accel_user_toggled })
+
+        return self._dialog
+
+    def _on_accel_user_toggled(self, togglebutton):
+        enabled = togglebutton.get_active()
+        self._entry_accel.set_sensitive(enabled)
+
+    def _on_move_up_job_clicked(self, button):
+        store, iter = self._view_job.get_selection().get_selected()
+        store.swap(iter)
+
+    def _on_add_extension_clicked(self, button):
+        extension = self._entry_new_extension.get_text()
+        self._store_extension.append([extension])
+
+    def _on_remove_extension_clicked(self, button):
+        store, it = self._view_extension.get_selection().get_selected()
+        store.remove(it)
+
+    def _on_job_command_edited(self, renderer, path, text):
+        """
+        The command template has been edited
+        """
+        self._store_job.set(self._store_job.get_iter_from_string(path), 0, text)
+
+    def _on_job_pp_edited(self, renderer, path, text):
+        """
+        Another post processor has been selected
+        """
+        self._store_job.set(self._store_job.get_iter_from_string(path), 2, text)
+
+    def _on_must_succeed_toggled(self, renderer, path):
+        """
+        The 'must succeed' flag has been toggled
+        """
+        value = self._store_job.get(self._store_job.get_iter_from_string(path), 1)[0]
+        self._store_job.set(self._store_job.get_iter_from_string(path), 1, not value)
+
+    def _on_add_job_clicked(self, button):
+        """
+        Add a new job
+        """
+        command = self._entry_new_job.get_text()
+        self._store_job.append([command, True, "GenericPostProcessor"])
+
+    def _on_remove_job_clicked(self, button):
+        store, it = self._view_job.get_selection().get_selected()
+        store.remove(it)
+
+    def _on_new_job_changed(self, widget):
+        """
+        The entry for a new command template has changed
+        """
+        self._button_add_job.set_sensitive(len(self._entry_new_job.get_text()) > 0)
+
+    def _on_new_extension_changed(self, widget):
+        self._button_add_extension.set_sensitive(len(self._entry_new_extension.get_text()) > 0)
+
+    def _on_job_cursor_changed(self, tree_view):
+        store, iter = tree_view.get_selection().get_selected()
+        if not iter:
+            return
+        self._button_remove_job.set_sensitive(True)
+
+        first_row_selected = (store.get_string_from_iter(iter) == "0")
+        self._button_job_up.set_sensitive(not first_row_selected)
+
+    def _on_extension_cursor_changed(self, tree_view):
+        store, it = tree_view.get_selection().get_selected()
+        if not it:
+            return
+        self._button_remove_extension.set_sensitive(True)
+
+    def _on_abort_clicked(self, button):
+        self._dialog.hide()
+
+    def _on_okay_clicked(self, button):
+        self._dialog.hide()
+
+    def _validate_tool(self):
+        """
+        Validate the dialog contents
+        """
+        errors = []
+
+        if len(self._store_job) == 0:
+            errors.append("You have not specified any jobs.")
+
+        if len(errors):
+            self._buttonApply.set_sensitive(False)
+        else:
+            self._buttonApply.set_sensitive(True)
+
+        if len(errors) == 1:
+            self._labelProfileValidate.set_markup(errors[0])
+        elif len(errors) > 1:
+            self._labelProfileValidate.set_markup("\n".join([" * %s" % e for e in errors]))
+        else:
+            self._labelProfileValidate.set_markup("Remember to run all commands in batch mode (e.g. append <tt>-interaction batchmode</tt> to <tt>latex</tt>)")
+
 
 
 class PreferencesDialog(GladeInterface):
-	"""
-	This controls the configure dialog
-	"""
-	
-	_log = getLogger("PreferencesWizard")
-	
-	filename = find_resource("ui/configure.ui")
-	_dialog = None
-	
-	@property
-	def dialog(self):
-		if not self._dialog:
-			self._preferences = Preferences()
-			
-			self._dialog = self.find_widget("notebook1")
-			
-			#
-			# recent bibliographies
-			#
-			self._storeBibs = Gtk.ListStore(str)
-			
-#			for bib in self._preferences.bibliographies:
-#				self._storeBibs.append([bib.filename])
-				
-			self._viewBibs = self.find_widget("treeviewBibs")
-			self._viewBibs.set_model(self._storeBibs)
-			_insert_column_with_attributes(self._viewBibs, -1, "Filename", Gtk.CellRendererText(), text=0)
-			
-			#
-			# tools
-			#
-			self._tool_preferences = ToolPreferences()
-			
-			# grab widgets
-
-			self._button_delete_tool = self.find_widget("buttonDeleteTool")
-			self._button_move_up_tool = self.find_widget("buttonMoveUpTool")
-			self._button_configure_tool = self.find_widget("buttonConfigureTool")
-			
-			self._store_tool = Gtk.ListStore(str, str, object)     # label markup, extensions, Tool instance
-				
-			self._view_tool = self.find_widget("treeviewProfiles")
-			self._view_tool.set_model(self._store_tool)
-			_insert_column_with_attributes(self._view_tool, -1, "Label", Gtk.CellRendererText(), markup=0)
-			_insert_column_with_attributes(self._view_tool, -1, "File Extensions", Gtk.CellRendererText(), text=1)
-			
-			# init tool and listen to tool changes
-			self._load_tools()
-			self._tool_preferences.connect("tools-changed", self._on_tools_changed)
-
-			
-			# misc
-			check_hide_box = self.find_widget("checkHideBox")
-			check_hide_box.set_active(self._preferences.get_bool("hide-box-warnings"))
-			
-			
-			check_show_toolbar = self.find_widget("checkShowToolbar")
-			check_show_toolbar.set_active(self._preferences.get_bool("show-latex-toolbar"))
-			
-			
-			filechooser_tmp = self.find_widget("filechooserTemplates")
-			filechooser_tmp.set_filename(self._preferences.TEMPLATE_DIR)
-			
-			
-			#
-			# proxies for SpinButtons
-			#
-			self._proxies = [PreferencesSpinButtonProxy(self.find_widget("spinMaxBibSize"), "maximum-bibtex-size")]
-			
-			#
-			# signals
-			#
-			self.connect_signals({ "on_buttonClose_clicked" : self._on_close_clicked,
-								   "on_treeviewProfiles_cursor_changed" : self._on_tool_cursor_changed,
-								   "on_buttonAddBib_clicked" : self._on_add_bib_clicked,
-								   "on_buttonRemoveBib_clicked" : self._on_delete_bib_clicked,
-								   "on_buttonNewProfile_clicked" : self._on_new_tool_clicked,
-								   "on_buttonMoveUpTool_clicked" : self._on_tool_up_clicked,
-								   "on_buttonMoveDownTool_clicked" : self._on_tool_down_clicked,
-								   "on_buttonConfigureTool_clicked" : self._on_configure_tool_clicked,
-								   "on_buttonDeleteTool_clicked" : self._on_delete_tool_clicked,
-								   "on_checkHideBox_toggled" : self._on_hide_box_toggled,
-								   "on_filechooserTemplates_selection_changed" : self._on_templates_dir_changed,
-								   "on_checkShowToolbar_toggled" : self._on_show_toolbar_toggled })
-			
-		return self._dialog
-	
-	def _on_show_toolbar_toggled(self, togglebutton):
-		value = togglebutton.get_active()
-		self._preferences.set("show-latex-toolbar", value)
-	
-	def _on_templates_dir_changed(self, filechooser):
-		folder = filechooser.get_filename()
-		if folder is None:
-			return
-		
-	def _on_hide_box_toggled(self, togglebutton):
-		value = togglebutton.get_active()
-		self._preferences.set("hide-box-warnings", value)
-	
-	def _on_tools_changed(self, tools):
-		self._load_tools()
-	
-	def _load_tools(self):
-		# save cursor
-		store, iter = self._view_tool.get_selection().get_selected()
-		if iter is None:
-			restore_cursor = False
-		else:
-			path = store.get_string_from_iter(iter)
-			restore_cursor = True
-		
-		# reload tools
-		self._store_tool.clear()
-		for tool in self._tool_preferences.tools:
-			self._store_tool.append(["<b>%s</b>" % tool.label, ", ".join(tool.extensions), tool])
-		
-		# restore cursor
-		if restore_cursor:
-			self._view_tool.set_cursor(path)
-	
-	def _on_configure_tool_clicked(self, button):
-		store, it = self._view_tool.get_selection().get_selected()
-		tool = store.get_value(it, 2)
-		
-		dialog = ConfigureToolDialog()
-		
-		if not dialog.run(tool) is None:
-			self._preferences.save_or_update_tool(tool)
-	
-	def _on_delete_tool_clicked(self, button):
-		store, it = self._view_tool.get_selection().get_selected()
-		tool = store.get_value(it, 2)
-		
-		self._preferences.delete_tool(tool)
-	
-	def _on_tool_up_clicked(self, button):
-		"""
-		Move up the selected tool
-		"""
-		store, iter = self._view_tool.get_selection().get_selected()
-		tool_1 = store.get_value(iter, 2)
-		
-		index_previous = int(store.get_string_from_iter(iter)) - 1
-		assert index_previous >= 0
-		
-		iter_previous = store.get_iter_from_string(str(index_previous))
-		tool_2 = store.get_value(iter_previous, 2)
-		
-		self._preferences.swap_tools(tool_1, tool_2)
-	
-		# adjust selection
-		self._view_tool.get_selection().select_path(str(index_previous))
-	
-	def _on_tool_down_clicked(self, button):
-		"""
-		Move down the selected tool
-		"""
-		store, iter = self._view_tool.get_selection().get_selected()
-		tool_1 = store.get_value(iter, 2)
-	
-		index_next = int(store.get_string_from_iter(iter)) + 1
-		assert index_next < len(store)
-		
-		iter_next = store.get_iter_from_string(str(index_next))
-		tool_2 = store.get_value(iter_next, 2)
-		
-		# swap tools in preferences
-		self._tool_preferences.swap_tools(tool_1, tool_2)
-		
-		# adjust selection
-		self._view_tool.get_selection().select_path(str(index_next))
-	
-	def _on_new_tool_clicked(self, button):
-		dialog = ConfigureToolDialog()
-		
-		tool = Tool("New Tool", [], "", [".tex"])
-		
-		if not dialog.run(tool) is None:
-			self._tool_preferences.save_or_update_tool(tool)
-	
-	def _on_delete_bib_clicked(self, button):
-		pass
-
-	def _on_add_bib_clicked(self, button):
-		pass
-			
-	def _on_close_clicked(self, button):
-		self._dialog.hide()
-	
-	def _on_tool_cursor_changed(self, treeView):
-		"""
-		The cursor in the tools view has changed
-		"""
-		store, it = treeView.get_selection().get_selected()
-		if not it: 
-			return
-		
-		self._profile = store.get_value(it, 1)
-		
-		self._button_delete_tool.set_sensitive(True)
-		self._button_move_up_tool.set_sensitive(True)
-		self._button_configure_tool.set_sensitive(True)
-		
-			
-	
-	
-	
+    """
+    This controls the configure dialog
+    """
+
+    _log = getLogger("PreferencesWizard")
+
+    filename = find_resource("ui/configure.ui")
+    _dialog = None
+
+    @property
+    def dialog(self):
+        if not self._dialog:
+            self._preferences = Preferences()
+
+            self._dialog = self.find_widget("notebook1")
+
+            #
+            # recent bibliographies
+            #
+            self._storeBibs = Gtk.ListStore(str)
+
+#            for bib in self._preferences.bibliographies:
+#                self._storeBibs.append([bib.filename])
+
+            self._viewBibs = self.find_widget("treeviewBibs")
+            self._viewBibs.set_model(self._storeBibs)
+            _insert_column_with_attributes(self._viewBibs, -1, "Filename", Gtk.CellRendererText(), text=0)
+
+            #
+            # tools
+            #
+            self._tool_preferences = ToolPreferences()
+
+            # grab widgets
+
+            self._button_delete_tool = self.find_widget("buttonDeleteTool")
+            self._button_move_up_tool = self.find_widget("buttonMoveUpTool")
+            self._button_configure_tool = self.find_widget("buttonConfigureTool")
+
+            self._store_tool = Gtk.ListStore(str, str, object)     # label markup, extensions, Tool instance
+
+            self._view_tool = self.find_widget("treeviewProfiles")
+            self._view_tool.set_model(self._store_tool)
+            _insert_column_with_attributes(self._view_tool, -1, "Label", Gtk.CellRendererText(), markup=0)
+            _insert_column_with_attributes(self._view_tool, -1, "File Extensions", Gtk.CellRendererText(), text=1)
+
+            # init tool and listen to tool changes
+            self._load_tools()
+            self._tool_preferences.connect("tools-changed", self._on_tools_changed)
+
+
+            # misc
+            check_hide_box = self.find_widget("checkHideBox")
+            check_hide_box.set_active(self._preferences.get_bool("hide-box-warnings"))
+
+
+            check_show_toolbar = self.find_widget("checkShowToolbar")
+            check_show_toolbar.set_active(self._preferences.get_bool("show-latex-toolbar"))
+
+
+            filechooser_tmp = self.find_widget("filechooserTemplates")
+            filechooser_tmp.set_filename(self._preferences.TEMPLATE_DIR)
+
+
+            #
+            # proxies for SpinButtons
+            #
+            self._proxies = [PreferencesSpinButtonProxy(self.find_widget("spinMaxBibSize"), "maximum-bibtex-size")]
+
+            #
+            # signals
+            #
+            self.connect_signals({ "on_buttonClose_clicked" : self._on_close_clicked,
+                                   "on_treeviewProfiles_cursor_changed" : self._on_tool_cursor_changed,
+                                   "on_buttonAddBib_clicked" : self._on_add_bib_clicked,
+                                   "on_buttonRemoveBib_clicked" : self._on_delete_bib_clicked,
+                                   "on_buttonNewProfile_clicked" : self._on_new_tool_clicked,
+                                   "on_buttonMoveUpTool_clicked" : self._on_tool_up_clicked,
+                                   "on_buttonMoveDownTool_clicked" : self._on_tool_down_clicked,
+                                   "on_buttonConfigureTool_clicked" : self._on_configure_tool_clicked,
+                                   "on_buttonDeleteTool_clicked" : self._on_delete_tool_clicked,
+                                   "on_checkHideBox_toggled" : self._on_hide_box_toggled,
+                                   "on_filechooserTemplates_selection_changed" : self._on_templates_dir_changed,
+                                   "on_checkShowToolbar_toggled" : self._on_show_toolbar_toggled })
+
+        return self._dialog
+
+    def _on_show_toolbar_toggled(self, togglebutton):
+        value = togglebutton.get_active()
+        self._preferences.set("show-latex-toolbar", value)
+
+    def _on_templates_dir_changed(self, filechooser):
+        folder = filechooser.get_filename()
+        if folder is None:
+            return
+
+    def _on_hide_box_toggled(self, togglebutton):
+        value = togglebutton.get_active()
+        self._preferences.set("hide-box-warnings", value)
+
+    def _on_tools_changed(self, tools):
+        self._load_tools()
+
+    def _load_tools(self):
+        # save cursor
+        store, iter = self._view_tool.get_selection().get_selected()
+        if iter is None:
+            restore_cursor = False
+        else:
+            path = store.get_string_from_iter(iter)
+            restore_cursor = True
+
+        # reload tools
+        self._store_tool.clear()
+        for tool in self._tool_preferences.tools:
+            self._store_tool.append(["<b>%s</b>" % tool.label, ", ".join(tool.extensions), tool])
+
+        # restore cursor
+        if restore_cursor:
+            self._view_tool.set_cursor(path)
+
+    def _on_configure_tool_clicked(self, button):
+        store, it = self._view_tool.get_selection().get_selected()
+        tool = store.get_value(it, 2)
+
+        dialog = ConfigureToolDialog()
+
+        if not dialog.run(tool) is None:
+            self._preferences.save_or_update_tool(tool)
+
+    def _on_delete_tool_clicked(self, button):
+        store, it = self._view_tool.get_selection().get_selected()
+        tool = store.get_value(it, 2)
+
+        self._preferences.delete_tool(tool)
+
+    def _on_tool_up_clicked(self, button):
+        """
+        Move up the selected tool
+        """
+        store, iter = self._view_tool.get_selection().get_selected()
+        tool_1 = store.get_value(iter, 2)
+
+        index_previous = int(store.get_string_from_iter(iter)) - 1
+        assert index_previous >= 0
+
+        iter_previous = store.get_iter_from_string(str(index_previous))
+        tool_2 = store.get_value(iter_previous, 2)
+
+        self._preferences.swap_tools(tool_1, tool_2)
+
+        # adjust selection
+        self._view_tool.get_selection().select_path(str(index_previous))
+
+    def _on_tool_down_clicked(self, button):
+        """
+        Move down the selected tool
+        """
+        store, iter = self._view_tool.get_selection().get_selected()
+        tool_1 = store.get_value(iter, 2)
+
+        index_next = int(store.get_string_from_iter(iter)) + 1
+        assert index_next < len(store)
+
+        iter_next = store.get_iter_from_string(str(index_next))
+        tool_2 = store.get_value(iter_next, 2)
+
+        # swap tools in preferences
+        self._tool_preferences.swap_tools(tool_1, tool_2)
+
+        # adjust selection
+        self._view_tool.get_selection().select_path(str(index_next))
+
+    def _on_new_tool_clicked(self, button):
+        dialog = ConfigureToolDialog()
+
+        tool = Tool("New Tool", [], "", [".tex"])
+
+        if not dialog.run(tool) is None:
+            self._tool_preferences.save_or_update_tool(tool)
+
+    def _on_delete_bib_clicked(self, button):
+        pass
+
+    def _on_add_bib_clicked(self, button):
+        pass
+
+    def _on_close_clicked(self, button):
+        self._dialog.hide()
+
+    def _on_tool_cursor_changed(self, treeView):
+        """
+        The cursor in the tools view has changed
+        """
+        store, it = treeView.get_selection().get_selected()
+        if not it:
+            return
+
+        self._profile = store.get_value(it, 1)
+
+        self._button_delete_tool.set_sensitive(True)
+        self._button_move_up_tool.set_sensitive(True)
+        self._button_configure_tool.set_sensitive(True)
+
+
+
+
+
diff --git a/latex/preferences/tools.py b/latex/preferences/tools.py
index 3192d46..091273b 100644
--- a/latex/preferences/tools.py
+++ b/latex/preferences/tools.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -30,205 +30,205 @@ from ..tools.postprocess import GenericPostProcessor, RubberPostProcessor, LaTeX
 from ..util import singleton
 
 def str_to_bool(x):
-	"""
-	Converts a string to a boolean value
-	"""
-	if type(x) is bool:
-		return x
-	elif type(x) is str or type(x) is unicode:
-		try:
-			return {"false" : False, "0" : False, "true" : True, "1" : True}[x.strip().lower()]
-		except KeyError:
-			print "str_to_bool: unsupported value %s" % x
-	else:
-		print "str_to_bool: unsupported type %s" % str(type(x))
+    """
+    Converts a string to a boolean value
+    """
+    if type(x) is bool:
+        return x
+    elif type(x) is str or type(x) is unicode:
+        try:
+            return {"false" : False, "0" : False, "true" : True, "1" : True}[x.strip().lower()]
+        except KeyError:
+            print "str_to_bool: unsupported value %s" % x
+    else:
+        print "str_to_bool: unsupported type %s" % str(type(x))
 
 @singleton
 class ToolPreferences(GObject.GObject):
 
-	__gsignals__ = {
-		"tools-changed": (
-			GObject.SignalFlags.RUN_LAST, None, []),
-	}
-
-	_log = getLogger("ToolPreferences")
-
-	# maps names to classes
-	POST_PROCESSORS = {"GenericPostProcessor" : GenericPostProcessor, 
-					   "LaTeXPostProcessor" : LaTeXPostProcessor, 
-					   "RubberPostProcessor" : RubberPostProcessor}
-
-	def __init__(self):
-		GObject.GObject.__init__(self)
-		self.__tool_objects = None
-		self.__tool_ids = None
-		self.__tools_changed = False
-		self.__tools = ElementTree.parse(
-							find_resource("tools.xml", MODE_READWRITE)).getroot()
-		self._log.debug("Constructed")
-
-	def __notify_tools_changed(self):
-		self.emit("tools-changed")
-	
-	@property
-	def tools(self):
-		"""
-		Return all Tools
-		"""
-		self.__tool_ids = {}
-		
-		tools = []
-		
-		for tool_element in self.__tools.findall("tool"):
-			jobs = []
-			for job_element in tool_element.findall("job"):
-				jobs.append(Job(job_element.text.strip(), str_to_bool(job_element.get("mustSucceed")), self.POST_PROCESSORS[job_element.get("postProcessor")]))
-			
-			assert not tool_element.get("extensions") is None
-			
-			extensions = tool_element.get("extensions").split()
-			accelerator = tool_element.get("accelerator")
-			id = tool_element.get("id")
-			tool = Tool(tool_element.get("label"), jobs, tool_element.get("description"), accelerator, extensions)
-			self.__tool_ids[tool] = id
-			
-			tools.append(tool)
-			
-		return tools
-	
-	def __find_tool_element(self, id):
-		"""
-		Find the tool element with the given id 
-		"""
-		for element in self.__tools.findall("tool"):
-			if element.get("id") == id:
-				return element
-		self._log.warning("<tool id='%s'> not found" % id)
-		return None
-	
-	def save_or_update_tool(self, tool):
-		"""
-		Save or update the XML subtree for the given Tool
-		
-		@param tool: a Tool object
-		"""
-		tool_element = None
-		if tool in self.__tool_ids:
-			# find tool tag
-			self._log.debug("Tool element found, updating...")
-			
-			id = self.__tool_ids[tool]
-			tool_element = self.__find_tool_element(id)
-		else:
-			# create new tool tag
-			self._log.debug("Creating new Tool...")
-			
-			id = str(uuid4())
-			self.__tool_ids[tool] = id
-			
-			tool_element = ElementTree.SubElement(self.__tools, "tool")
-			tool_element.set("id", id)
-		
-		tool_element.set("label", tool.label)
-		tool_element.set("description", tool.description)
-		tool_element.set("extensions", " ".join(tool.extensions))
-		if tool.accelerator is None:
-			if "accelerator" in tool_element.attrib.keys():
-				del tool_element.attrib["accelerator"]
-		else:
-			tool_element.set("accelerator", tool.accelerator)
-		
-		# remove all jobs
-		for job_element in tool_element.findall("job"):
-			tool_element.remove(job_element)
-			
-		# append new jobs
-		for job in tool.jobs:
-			job_element = ElementTree.SubElement(tool_element, "job")
-			job_element.set("mustSucceed", str(job.must_succeed))
-			job_element.set("postProcessor", job.post_processor.name)
-			job_element.text = job.command_template
-		
-		self.__tools_changed = True
-		self.__notify_tools_changed()
-	
-	def swap_tools(self, tool_1, tool_2):
-		"""
-		Swap the order of two Tools
-		"""
-		# grab their ids
-		id_1 = self.__tool_ids[tool_1]
-		id_2 = self.__tool_ids[tool_2]
-		
-		if id_1 == id_2:
-			self._log.warning("Two tools have the same id. Please modify tools.xml to have unique id's.")
-			return
-		
-		self._log.debug("Tool IDs are {%s: %s, %s, %s}" % (tool_1.label, id_1, tool_2.label, id_2))
-		
-		tool_element_1 = None
-		tool_element_2 = None
-		
-		# find the XML elements and current indexes of the tools
-		i = 0
-		for tool_element in self.__tools:
-			if tool_element.get("id") == id_1:
-				tool_element_1 = tool_element
-				index_1 = i
-			elif tool_element.get("id") == id_2:
-				tool_element_2 = tool_element
-				index_2 = i
-			
-			if not (tool_element_1 is None or tool_element_2 is None):
-				break
-			
-			i += 1
-		
-		self._log.debug("Found XML elements, indexes are {%s: %s, %s, %s}" % (tool_1.label, index_1, tool_2.label, index_2))
-		
-		# successively replace each of them by the other in the XML model
-		self.__tools.remove(tool_element_1)
-		self.__tools.insert(index_1, tool_element_2)
-		
-		self._log.debug("Replaced first tool by second in list")
-				
-		self.__tools.remove(tool_element_2)
-		self.__tools.insert(index_2, tool_element_1)
-		
-		self._log.debug("Replaced second tool by first in list")
-		
-		# notify changes
-		self.__tools_changed = True
-		self.__notify_tools_changed()
-	
-	def delete_tool(self, tool):
-		"""
-		Delete the given Tool
-		
-		@param tool: a Tool object
-		"""
-		try:
-			id = self.__tool_ids[tool]
-			tool_element = self.__find_tool_element(id)
-			self.__tools.remove(tool_element)
-			
-			del self.__tool_ids[tool]
-			
-			self.__tools_changed = True
-		except KeyError, e:
-			self._log.error("delete_tool: %s" % e)
-		
-		self.__notify_tools_changed()
-
-	def save(self):
-		"""
-		Save the preferences to XML
-		"""
-		if self.__tools_changed:
-			self._log.debug("Saving tools...")
-		
-			tree = ElementTree.ElementTree(self.__tools)
-			tree.write(find_resource("tools.xml", MODE_READWRITE), encoding="utf-8")
-			
-			self.__tools_changed = False
-	
+    __gsignals__ = {
+        "tools-changed": (
+            GObject.SignalFlags.RUN_LAST, None, []),
+    }
+
+    _log = getLogger("ToolPreferences")
+
+    # maps names to classes
+    POST_PROCESSORS = {"GenericPostProcessor" : GenericPostProcessor,
+                       "LaTeXPostProcessor" : LaTeXPostProcessor,
+                       "RubberPostProcessor" : RubberPostProcessor}
+
+    def __init__(self):
+        GObject.GObject.__init__(self)
+        self.__tool_objects = None
+        self.__tool_ids = None
+        self.__tools_changed = False
+        self.__tools = ElementTree.parse(
+                            find_resource("tools.xml", MODE_READWRITE)).getroot()
+        self._log.debug("Constructed")
+
+    def __notify_tools_changed(self):
+        self.emit("tools-changed")
+
+    @property
+    def tools(self):
+        """
+        Return all Tools
+        """
+        self.__tool_ids = {}
+
+        tools = []
+
+        for tool_element in self.__tools.findall("tool"):
+            jobs = []
+            for job_element in tool_element.findall("job"):
+                jobs.append(Job(job_element.text.strip(), str_to_bool(job_element.get("mustSucceed")), self.POST_PROCESSORS[job_element.get("postProcessor")]))
+
+            assert not tool_element.get("extensions") is None
+
+            extensions = tool_element.get("extensions").split()
+            accelerator = tool_element.get("accelerator")
+            id = tool_element.get("id")
+            tool = Tool(tool_element.get("label"), jobs, tool_element.get("description"), accelerator, extensions)
+            self.__tool_ids[tool] = id
+
+            tools.append(tool)
+
+        return tools
+
+    def __find_tool_element(self, id):
+        """
+        Find the tool element with the given id
+        """
+        for element in self.__tools.findall("tool"):
+            if element.get("id") == id:
+                return element
+        self._log.warning("<tool id='%s'> not found" % id)
+        return None
+
+    def save_or_update_tool(self, tool):
+        """
+        Save or update the XML subtree for the given Tool
+
+        @param tool: a Tool object
+        """
+        tool_element = None
+        if tool in self.__tool_ids:
+            # find tool tag
+            self._log.debug("Tool element found, updating...")
+
+            id = self.__tool_ids[tool]
+            tool_element = self.__find_tool_element(id)
+        else:
+            # create new tool tag
+            self._log.debug("Creating new Tool...")
+
+            id = str(uuid4())
+            self.__tool_ids[tool] = id
+
+            tool_element = ElementTree.SubElement(self.__tools, "tool")
+            tool_element.set("id", id)
+
+        tool_element.set("label", tool.label)
+        tool_element.set("description", tool.description)
+        tool_element.set("extensions", " ".join(tool.extensions))
+        if tool.accelerator is None:
+            if "accelerator" in tool_element.attrib.keys():
+                del tool_element.attrib["accelerator"]
+        else:
+            tool_element.set("accelerator", tool.accelerator)
+
+        # remove all jobs
+        for job_element in tool_element.findall("job"):
+            tool_element.remove(job_element)
+
+        # append new jobs
+        for job in tool.jobs:
+            job_element = ElementTree.SubElement(tool_element, "job")
+            job_element.set("mustSucceed", str(job.must_succeed))
+            job_element.set("postProcessor", job.post_processor.name)
+            job_element.text = job.command_template
+
+        self.__tools_changed = True
+        self.__notify_tools_changed()
+
+    def swap_tools(self, tool_1, tool_2):
+        """
+        Swap the order of two Tools
+        """
+        # grab their ids
+        id_1 = self.__tool_ids[tool_1]
+        id_2 = self.__tool_ids[tool_2]
+
+        if id_1 == id_2:
+            self._log.warning("Two tools have the same id. Please modify tools.xml to have unique id's.")
+            return
+
+        self._log.debug("Tool IDs are {%s: %s, %s, %s}" % (tool_1.label, id_1, tool_2.label, id_2))
+
+        tool_element_1 = None
+        tool_element_2 = None
+
+        # find the XML elements and current indexes of the tools
+        i = 0
+        for tool_element in self.__tools:
+            if tool_element.get("id") == id_1:
+                tool_element_1 = tool_element
+                index_1 = i
+            elif tool_element.get("id") == id_2:
+                tool_element_2 = tool_element
+                index_2 = i
+
+            if not (tool_element_1 is None or tool_element_2 is None):
+                break
+
+            i += 1
+
+        self._log.debug("Found XML elements, indexes are {%s: %s, %s, %s}" % (tool_1.label, index_1, tool_2.label, index_2))
+
+        # successively replace each of them by the other in the XML model
+        self.__tools.remove(tool_element_1)
+        self.__tools.insert(index_1, tool_element_2)
+
+        self._log.debug("Replaced first tool by second in list")
+
+        self.__tools.remove(tool_element_2)
+        self.__tools.insert(index_2, tool_element_1)
+
+        self._log.debug("Replaced second tool by first in list")
+
+        # notify changes
+        self.__tools_changed = True
+        self.__notify_tools_changed()
+
+    def delete_tool(self, tool):
+        """
+        Delete the given Tool
+
+        @param tool: a Tool object
+        """
+        try:
+            id = self.__tool_ids[tool]
+            tool_element = self.__find_tool_element(id)
+            self.__tools.remove(tool_element)
+
+            del self.__tool_ids[tool]
+
+            self.__tools_changed = True
+        except KeyError, e:
+            self._log.error("delete_tool: %s" % e)
+
+        self.__notify_tools_changed()
+
+    def save(self):
+        """
+        Save the preferences to XML
+        """
+        if self.__tools_changed:
+            self._log.debug("Saving tools...")
+
+            tree = ElementTree.ElementTree(self.__tools)
+            tree.write(find_resource("tools.xml", MODE_READWRITE), encoding="utf-8")
+
+            self.__tools_changed = False
+
diff --git a/latex/relpath.py b/latex/relpath.py
index 5aedd2f..9bb4767 100644
--- a/latex/relpath.py
+++ b/latex/relpath.py
@@ -3,15 +3,15 @@
 # http://www.koders.com/python/fid663562CE0A76349E3DA5EE4E0747B1B733A510AD.aspx
 
 #----------------------------------------------------------------------
-# Name:		relpath.py
+# Name:        relpath.py
 # Purpose:
 #
-# Author:	  Riaan Booysen
+# Author:      Riaan Booysen
 #
-# Created:	 1999
-# RCS-ID:	  $Id: relpath.py,v 1.9 2006/10/09 15:14:32 riaan Exp $
+# Created:     1999
+# RCS-ID:      $Id: relpath.py,v 1.9 2006/10/09 15:14:32 riaan Exp $
 # Copyright:   (c) 1999 - 2003 Riaan Booysen
-# Licence:	 GPL
+# Licence:     GPL
 #----------------------------------------------------------------------
 
 ##b = 'c:\\a\\b\\f\\d'
@@ -22,68 +22,68 @@
 import os
 
 def splitpath(apath):
-	""" Splits a path into a list of directory names """
-	path_list = []
-	drive, apath = os.path.splitdrive(apath)
-	head, tail = os.path.split(apath)
-	while 1:
-		if tail:
-			path_list.insert(0, tail)
-		newhead, tail = os.path.split(head)
-		if newhead == head:
-			break
-		else:
-			head = newhead
-	if drive:
-		path_list.insert(0, drive)
-	return path_list
+    """ Splits a path into a list of directory names """
+    path_list = []
+    drive, apath = os.path.splitdrive(apath)
+    head, tail = os.path.split(apath)
+    while 1:
+        if tail:
+            path_list.insert(0, tail)
+        newhead, tail = os.path.split(head)
+        if newhead == head:
+            break
+        else:
+            head = newhead
+    if drive:
+        path_list.insert(0, drive)
+    return path_list
 
 
 def relpath(base, comp):
-	""" Return a path to file comp relative to path base. """
-	protsplitbase = base.split('://')
-	if len(protsplitbase) == 1:
-		baseprot, nbase = 'file', protsplitbase[0]
-	elif len(protsplitbase) == 2:
-		baseprot, nbase = protsplitbase
-	elif len(protsplitbase) == 3:
-		baseprot, nbase, zipentry = protsplitbase
-	else:
-		raise Exception, 'Unhandled path %s'%`protsplitbase`
+    """ Return a path to file comp relative to path base. """
+    protsplitbase = base.split('://')
+    if len(protsplitbase) == 1:
+        baseprot, nbase = 'file', protsplitbase[0]
+    elif len(protsplitbase) == 2:
+        baseprot, nbase = protsplitbase
+    elif len(protsplitbase) == 3:
+        baseprot, nbase, zipentry = protsplitbase
+    else:
+        raise Exception, 'Unhandled path %s'%`protsplitbase`
 
-	protsplitcomp = comp.split('://')
-	if len(protsplitcomp) == 1:
-		compprot, ncomp  = 'file', protsplitcomp[0]
-	elif len(protsplitcomp) == 2:
-		compprot, ncomp = protsplitcomp
-	elif len(protsplitcomp) == 3:
-		compprot, ncomp, zipentry = protsplitcomp
-	else:
-		raise Exception, 'Unhandled path %s'%`protsplitcomp`
+    protsplitcomp = comp.split('://')
+    if len(protsplitcomp) == 1:
+        compprot, ncomp  = 'file', protsplitcomp[0]
+    elif len(protsplitcomp) == 2:
+        compprot, ncomp = protsplitcomp
+    elif len(protsplitcomp) == 3:
+        compprot, ncomp, zipentry = protsplitcomp
+    else:
+        raise Exception, 'Unhandled path %s'%`protsplitcomp`
 
-	if baseprot != compprot:
-		return comp
+    if baseprot != compprot:
+        return comp
 
-	base_drive, base_path = os.path.splitdrive(nbase)
-	comp_drive, comp_path = os.path.splitdrive(ncomp)
-	base_path_list = splitpath(base_path)
-	comp_path_list = splitpath(comp_path)
+    base_drive, base_path = os.path.splitdrive(nbase)
+    comp_drive, comp_path = os.path.splitdrive(ncomp)
+    base_path_list = splitpath(base_path)
+    comp_path_list = splitpath(comp_path)
 
-	if base_drive != comp_drive:
-		return comp
+    if base_drive != comp_drive:
+        return comp
 
-	# relative path defaults to the list of files with
-	# a greater index then the entire base
-	rel_path = comp_path_list[len(base_path_list):]
-	# find the first directory for which the 2 paths differ
-	found = -1
-	idx = 0
-	for idx in range(len(base_path_list)):
-		if base_path_list[idx].lower() != comp_path_list[idx].lower():
-			rel_path = comp_path_list[idx:]
-			found = 0
-			break
-	for cnt in range(max(len(base_path_list) - idx + found, 0)):
-		rel_path.insert(0, os.pardir)
+    # relative path defaults to the list of files with
+    # a greater index then the entire base
+    rel_path = comp_path_list[len(base_path_list):]
+    # find the first directory for which the 2 paths differ
+    found = -1
+    idx = 0
+    for idx in range(len(base_path_list)):
+        if base_path_list[idx].lower() != comp_path_list[idx].lower():
+            rel_path = comp_path_list[idx:]
+            found = 0
+            break
+    for cnt in range(max(len(base_path_list) - idx + found, 0)):
+        rel_path.insert(0, os.pardir)
 
-	return os.path.join(*rel_path)
+    return os.path.join(*rel_path)
diff --git a/latex/tools/__init__.py b/latex/tools/__init__.py
index 45708bc..1b5f43d 100644
--- a/latex/tools/__init__.py
+++ b/latex/tools/__init__.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -29,109 +29,109 @@ from logging import getLogger
 
 
 class Tool(object):
-	"""
-	The model of a tool. This is to be stored in preferences.
-	"""
-	def __init__(self, label, jobs, description, accelerator, extensions=[]):
-		"""
-		@param label: a label used when displaying the Tool in the UI
-		@param jobs: a list of Job objects
-		@param description: a descriptive string used as tooltip
-		@param accelerator: a key combination for activating this tool
-		@param extensions: a list of file extensions for which this Tool can be used
-		"""
-		self.label = label
-		self.jobs = jobs
-		self.description = description
-		self.extensions = extensions
-		self.accelerator = accelerator
-	
-	def __str__(self):
-		return "Tool{%s}" % self.label
-	
-	
+    """
+    The model of a tool. This is to be stored in preferences.
+    """
+    def __init__(self, label, jobs, description, accelerator, extensions=[]):
+        """
+        @param label: a label used when displaying the Tool in the UI
+        @param jobs: a list of Job objects
+        @param description: a descriptive string used as tooltip
+        @param accelerator: a key combination for activating this tool
+        @param extensions: a list of file extensions for which this Tool can be used
+        """
+        self.label = label
+        self.jobs = jobs
+        self.description = description
+        self.extensions = extensions
+        self.accelerator = accelerator
+
+    def __str__(self):
+        return "Tool{%s}" % self.label
+
+
 class Job(object):
-	"""
-	A Job is one command to be executed in a Tool
-	
-	Command templates may contain the following placeholders:
-	
-	 * $filename : the full filename of the processed file
-	 * $directory : the parent directory of the processed file
-	 * $shortname : the filename of the processed file without extension ('/dir/doc' for '/dir/doc.tex')
-	"""
-	def __init__(self, command_template, must_succeed, post_processor):
-		"""
-		Construct a Job
-		
-		@param command_template: a template string for the command to be executed
-		@param must_succeed: if True this Job may cause the whole Tool to fail
-		@param post_processor: a class implementing IPostProcessor
-		"""
-		self._command_template = command_template
-		self._must_succeed = must_succeed
-		self._post_processor = post_processor
-	
-	@property
-	def command_template(self):
-		return self._command_template
-	
-	@property
-	def must_succeed(self):
-		return self._must_succeed
-	
-	@property
-	def post_processor(self):
-		return self._post_processor
+    """
+    A Job is one command to be executed in a Tool
+
+    Command templates may contain the following placeholders:
+
+     * $filename : the full filename of the processed file
+     * $directory : the parent directory of the processed file
+     * $shortname : the filename of the processed file without extension ('/dir/doc' for '/dir/doc.tex')
+    """
+    def __init__(self, command_template, must_succeed, post_processor):
+        """
+        Construct a Job
+
+        @param command_template: a template string for the command to be executed
+        @param must_succeed: if True this Job may cause the whole Tool to fail
+        @param post_processor: a class implementing IPostProcessor
+        """
+        self._command_template = command_template
+        self._must_succeed = must_succeed
+        self._post_processor = post_processor
+
+    @property
+    def command_template(self):
+        return self._command_template
+
+    @property
+    def must_succeed(self):
+        return self._must_succeed
+
+    @property
+    def post_processor(self):
+        return self._post_processor
 
 
 from gi.repository import Gtk
 
 from ..base import Action
 
-	
+
 class ToolAction(Action):
-	"""
-	This hooks Tools in the UI. A ToolAction is instantiated for each registered Tool.
-	"""
-	
-	_log = getLogger("ToolAction")
-	
-	def __init__(self, tool):
-		self._tool = tool
-		self._runner = ToolRunner()
-	
-	@property
-	def label(self):
-		return self._tool.label
-	
-	@property
-	def stock_id(self):
-		return Gtk.STOCK_EXECUTE
-	
-	@property
-	def accelerator(self):
-		return None
-	
-	@property
-	def tooltip(self):
-		return self._tool.description
-	
-	def activate(self, context):
-		self._log.debug("activate: %s" % self._tool)
-		
-		tool_view = context.find_view(None, "ToolView")
-		
-		if context.active_editor:
-			from ..preferences import Preferences		# FIXME: circ dep
-			# FIXME: find better way to save
-			context._window_decorator.save_file()
-			
-			self._runner.run(context.active_editor.file, self._tool, tool_view)
-			self._log.debug("activate: " + str(context.active_editor.file))
-		else:
-			self._log.error("No active editor")
-		
+    """
+    This hooks Tools in the UI. A ToolAction is instantiated for each registered Tool.
+    """
+
+    _log = getLogger("ToolAction")
+
+    def __init__(self, tool):
+        self._tool = tool
+        self._runner = ToolRunner()
+
+    @property
+    def label(self):
+        return self._tool.label
+
+    @property
+    def stock_id(self):
+        return Gtk.STOCK_EXECUTE
+
+    @property
+    def accelerator(self):
+        return None
+
+    @property
+    def tooltip(self):
+        return self._tool.description
+
+    def activate(self, context):
+        self._log.debug("activate: %s" % self._tool)
+
+        tool_view = context.find_view(None, "ToolView")
+
+        if context.active_editor:
+            from ..preferences import Preferences        # FIXME: circ dep
+            # FIXME: find better way to save
+            context._window_decorator.save_file()
+
+            self._runner.run(context.active_editor.file, self._tool, tool_view)
+            self._log.debug("activate: " + str(context.active_editor.file))
+        else:
+            self._log.error("No active editor")
+
 
 from os import chdir
 from util import Process
@@ -141,131 +141,131 @@ from ..base.resources import PLUGIN_PATH
 
 
 class ToolRunner(Process):
-	"""
-	This runs a Tool in a subprocess
-	"""
-	
-	_log = getLogger("ToolRunner")
-	
-	def run(self, file, tool, issue_handler):
-		"""
-		@param file: a File object
-		@param tool: a Tool object
-		@param issue_handler: an object implementing IStructuredIssueHandler
-		"""
-		self._file = file
-		self._stdout_text = ""
-		self._stderr_text = ""
-		self._job_iter = iter(tool.jobs)
-		
-		# init the IStructuredIssueHandler
-		self._issue_handler = issue_handler
-		self._issue_handler.clear()
-		self._root_issue_partition = self._issue_handler.add_partition("<b>%s</b>" % tool.label, "running", None)
-		self._issue_partitions = {}
-		for job in tool.jobs:
-			self._issue_partitions[job] = self._issue_handler.add_partition(job.command_template, "running", self._root_issue_partition)
-		
-		# change working directory to prevent issues with relative paths
-		chdir(file.dirname)
-		
-		# enable abort
-		self._issue_handler.set_abort_enabled(True, self.abort)
-		
-		# run
-		self.__proceed()
-	
-	def __proceed(self):
-		try:
-			self._job = self._job_iter.next()
-			
-			command_template = Template(self._job.command_template)
-			command = command_template.safe_substitute({"filename" : self._file.path, 
-														"shortname" : self._file.shortname,
-														"directory" : self._file.dirname,
-														"plugin_path" : PLUGIN_PATH})
-			
-			self._issue_handler.set_partition_state(self._issue_partitions[self._job], "running")
-			
-			self.execute(command)
-		except StopIteration:
-			# Tool finished successfully
-			self._issue_handler.set_partition_state(self._root_issue_partition, "succeeded")
-			# disable abort
-
-			# FIXME: CRASHES
-			# self._issue_handler.set_abort_enabled(False, None)
-			
-			self._on_tool_succeeded()
-	
-	def _on_stdout(self, text):
-		"""
-		"""
-		self._log.error("_stdout: " + text)
-		self._stdout_text += text
-		
-	def _on_stderr(self, text):
-		"""
-		"""
-		self._log.debug("_stderr: " + text)
-		self._stderr_text += text
-	
-	def _on_abort(self):
-		"""
-		"""
-		self._log.debug("_abort")
-		# disable abort
-		self._issue_handler.set_abort_enabled(False, None)
-		# mark Tool and all Jobs as aborted
-		self._issue_handler.set_partition_state(self._root_issue_partition, "aborted")
-		self._issue_handler.set_partition_state(self._issue_partitions[self._job], "aborted")
-		for job in self._job_iter:
-			self._issue_handler.set_partition_state(self._issue_partitions[job], "aborted")
-	
-	def _on_exit(self, condition):
-		"""
-		"""
-		self._log.debug("_exit")
-		
-		assert self._job
-		
-		# create post-processor instance
-		post_processor = self._job.post_processor()
-		
-		# run post-processor
-		post_processor.process(self._file, self._stdout_text, self._stderr_text, condition)
-		
-		# show issues
-		self._issue_handler.append_issues(self._issue_partitions[self._job], post_processor.issues)
-		
-		if post_processor.successful:
-			self._issue_handler.set_partition_state(self._issue_partitions[self._job], "succeeded")
-			self.__proceed()
-		else:
-			self._issue_handler.set_partition_state(self._issue_partitions[self._job], "failed")
-			if self._job.must_succeed:
-				# whole Tool failed
-				self._issue_handler.set_partition_state(self._root_issue_partition, "failed")
-				# disable abort
-				self._issue_handler.set_abort_enabled(False, None)
-				
-				self._on_tool_failed()
-			else:
-				self.__proceed()
-	
-	def _on_tool_succeeded(self):
-		"""
-		The Tool has finished successfully
-		
-		To be overridden by subclass
-		"""
-	
-	def _on_tool_failed(self):
-		"""
-		The Tool has failed
-		
-		To be overridden by subclass
-		"""
-			
+    """
+    This runs a Tool in a subprocess
+    """
+
+    _log = getLogger("ToolRunner")
+
+    def run(self, file, tool, issue_handler):
+        """
+        @param file: a File object
+        @param tool: a Tool object
+        @param issue_handler: an object implementing IStructuredIssueHandler
+        """
+        self._file = file
+        self._stdout_text = ""
+        self._stderr_text = ""
+        self._job_iter = iter(tool.jobs)
+
+        # init the IStructuredIssueHandler
+        self._issue_handler = issue_handler
+        self._issue_handler.clear()
+        self._root_issue_partition = self._issue_handler.add_partition("<b>%s</b>" % tool.label, "running", None)
+        self._issue_partitions = {}
+        for job in tool.jobs:
+            self._issue_partitions[job] = self._issue_handler.add_partition(job.command_template, "running", self._root_issue_partition)
+
+        # change working directory to prevent issues with relative paths
+        chdir(file.dirname)
+
+        # enable abort
+        self._issue_handler.set_abort_enabled(True, self.abort)
+
+        # run
+        self.__proceed()
+
+    def __proceed(self):
+        try:
+            self._job = self._job_iter.next()
+
+            command_template = Template(self._job.command_template)
+            command = command_template.safe_substitute({"filename" : self._file.path,
+                                                        "shortname" : self._file.shortname,
+                                                        "directory" : self._file.dirname,
+                                                        "plugin_path" : PLUGIN_PATH})
+
+            self._issue_handler.set_partition_state(self._issue_partitions[self._job], "running")
+
+            self.execute(command)
+        except StopIteration:
+            # Tool finished successfully
+            self._issue_handler.set_partition_state(self._root_issue_partition, "succeeded")
+            # disable abort
+
+            # FIXME: CRASHES
+            # self._issue_handler.set_abort_enabled(False, None)
+
+            self._on_tool_succeeded()
+
+    def _on_stdout(self, text):
+        """
+        """
+        self._log.error("_stdout: " + text)
+        self._stdout_text += text
+
+    def _on_stderr(self, text):
+        """
+        """
+        self._log.debug("_stderr: " + text)
+        self._stderr_text += text
+
+    def _on_abort(self):
+        """
+        """
+        self._log.debug("_abort")
+        # disable abort
+        self._issue_handler.set_abort_enabled(False, None)
+        # mark Tool and all Jobs as aborted
+        self._issue_handler.set_partition_state(self._root_issue_partition, "aborted")
+        self._issue_handler.set_partition_state(self._issue_partitions[self._job], "aborted")
+        for job in self._job_iter:
+            self._issue_handler.set_partition_state(self._issue_partitions[job], "aborted")
+
+    def _on_exit(self, condition):
+        """
+        """
+        self._log.debug("_exit")
+
+        assert self._job
+
+        # create post-processor instance
+        post_processor = self._job.post_processor()
+
+        # run post-processor
+        post_processor.process(self._file, self._stdout_text, self._stderr_text, condition)
+
+        # show issues
+        self._issue_handler.append_issues(self._issue_partitions[self._job], post_processor.issues)
+
+        if post_processor.successful:
+            self._issue_handler.set_partition_state(self._issue_partitions[self._job], "succeeded")
+            self.__proceed()
+        else:
+            self._issue_handler.set_partition_state(self._issue_partitions[self._job], "failed")
+            if self._job.must_succeed:
+                # whole Tool failed
+                self._issue_handler.set_partition_state(self._root_issue_partition, "failed")
+                # disable abort
+                self._issue_handler.set_abort_enabled(False, None)
+
+                self._on_tool_failed()
+            else:
+                self.__proceed()
+
+    def _on_tool_succeeded(self):
+        """
+        The Tool has finished successfully
+
+        To be overridden by subclass
+        """
+
+    def _on_tool_failed(self):
+        """
+        The Tool has failed
+
+        To be overridden by subclass
+        """
+
 
 
diff --git a/latex/tools/postprocess.py b/latex/tools/postprocess.py
index 18f0976..33467bd 100644
--- a/latex/tools/postprocess.py
+++ b/latex/tools/postprocess.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -30,198 +30,197 @@ from ..util import escape
 
 
 class PostProcessor(object):
-	"""
-	The contract for a post-processor
-	"""
-	
-	def process(self, file, stdout, stderr, condition):
-		"""
-		@param file: the File processed by the Tool
-		@param stdout: the output written to STDOUT
-		@param stderr: the output written to STDERR
-		@param condition: the exit condition of the Tool process
-		"""
-		raise NotImplementedError
-	
-	@property
-	def successful(self):
-		"""
-		Return whether the Tool process was successful
-		"""
-		raise NotImplementedError
-		
-	@property
-	def issues(self):
-		"""
-		Return a list of Issues
-		"""
-		raise NotImplementedError
-	
-	@property
-	def summary(self):
-		"""
-		Return a short string summarizing the result of the Tool process
-		"""
-		raise NotImplementedError
-	
-	
+    """
+    The contract for a post-processor
+    """
+
+    def process(self, file, stdout, stderr, condition):
+        """
+        @param file: the File processed by the Tool
+        @param stdout: the output written to STDOUT
+        @param stderr: the output written to STDERR
+        @param condition: the exit condition of the Tool process
+        """
+        raise NotImplementedError
+
+    @property
+    def successful(self):
+        """
+        Return whether the Tool process was successful
+        """
+        raise NotImplementedError
+
+    @property
+    def issues(self):
+        """
+        Return a list of Issues
+        """
+        raise NotImplementedError
+
+    @property
+    def summary(self):
+        """
+        Return a short string summarizing the result of the Tool process
+        """
+        raise NotImplementedError
+
+
 class GenericPostProcessor(PostProcessor):
-	"""
-	This just interprets the exit condition of the process
-	"""
-	
-	_log = getLogger("GenericPostProcessor")
-	
-	name = "GenericPostProcessor"
-	
-	def __init__(self):
-		self._issues = None
-		self._summary = None
-	
-	def process(self, file, stdout, stderr, condition):
-		self._issues = []
-		self._summary = ""
-		self._condition = condition
-	
-	@property
-	def successful(self):
-		return not bool(self._condition)
-	
-	@property
-	def issues(self):
-		return self._issues
-	
-	@property
-	def summary(self):
-		return self._summary
-	
-	
+    """
+    This just interprets the exit condition of the process
+    """
+
+    _log = getLogger("GenericPostProcessor")
+
+    name = "GenericPostProcessor"
+
+    def __init__(self):
+        self._issues = None
+        self._summary = None
+
+    def process(self, file, stdout, stderr, condition):
+        self._issues = []
+        self._summary = ""
+        self._condition = condition
+
+    @property
+    def successful(self):
+        return not bool(self._condition)
+
+    @property
+    def issues(self):
+        return self._issues
+
+    @property
+    def summary(self):
+        return self._summary
+
+
 class LaTeXPostProcessor(PostProcessor):
-	"""
-	This post-processor generates messages from a standard LaTeX log with
-	default error format (NOT using "-file-line-error")
-	"""
-	
-	_log = getLogger("LatexPostProcessor")
-	
-	name = "LaTeXPostProcessor"
-	
-	_PATTERN = pattern = re.compile(r"(^! (?P<text>.*?)$)|(^l\.(?P<line>[0-9]+))", re.MULTILINE)
-	
-	def __init__(self):
-		pass
-	
-	def process(self, file, stdout, stderr, condition):
-		self._file = file
-	
-	@property
-	def successful(self):
-		return True
-	
-	@property
-	def summary(self):
-		return self._summary
-	
-	@property
-	def issues(self):
-		try:
-			log = open("%s.log" % self._file.shortname).read()
-			
-			# check for wrong format
-			if log.find("file:line:error") > 0:
-				return [Issue("The file:line:error format is not supported. Please remove that switch.", None, None, self._file, Issue.SEVERITY_ERROR)]
-			
-			# process log file and extract tuples like (message, line)
-			tuples = []
-			for match in self._PATTERN.finditer(log):
-				if match.group("text"):
-					tuple = [match.group("text"), 0]
-				elif match.group("line"):
-					tuple[1] = match.group("line")
-					tuples.append(tuple)
-			
-			# generate issues from tuples
-			self._issues = []
-			for tuple in tuples:
-				text = tuple[0]
-				line = int(tuple[1]) - 1
-				self._issues.append(Issue(text, line, None, self._file, Issue.SEVERITY_ERROR, Issue.POSITION_LINE))
-				
-			return self._issues
-		
-		except IOError:
-			return [Issue("No LaTeX log file found", None, None, self._file, Issue.SEVERITY_ERROR)]
+    """
+    This post-processor generates messages from a standard LaTeX log with
+    default error format (NOT using "-file-line-error")
+    """
+
+    _log = getLogger("LatexPostProcessor")
+
+    name = "LaTeXPostProcessor"
+
+    _PATTERN = pattern = re.compile(r"(^! (?P<text>.*?)$)|(^l\.(?P<line>[0-9]+))", re.MULTILINE)
+
+    def __init__(self):
+        pass
+
+    def process(self, file, stdout, stderr, condition):
+        self._file = file
+
+    @property
+    def successful(self):
+        return True
+
+    @property
+    def summary(self):
+        return self._summary
+
+    @property
+    def issues(self):
+        try:
+            log = open("%s.log" % self._file.shortname).read()
+
+            # check for wrong format
+            if log.find("file:line:error") > 0:
+                return [Issue("The file:line:error format is not supported. Please remove that switch.", None, None, self._file, Issue.SEVERITY_ERROR)]
+
+            # process log file and extract tuples like (message, line)
+            tuples = []
+            for match in self._PATTERN.finditer(log):
+                if match.group("text"):
+                    tuple = [match.group("text"), 0]
+                elif match.group("line"):
+                    tuple[1] = match.group("line")
+                    tuples.append(tuple)
+
+            # generate issues from tuples
+            self._issues = []
+            for tuple in tuples:
+                text = tuple[0]
+                line = int(tuple[1]) - 1
+                self._issues.append(Issue(text, line, None, self._file, Issue.SEVERITY_ERROR, Issue.POSITION_LINE))
+
+            return self._issues
+
+        except IOError:
+            return [Issue("No LaTeX log file found", None, None, self._file, Issue.SEVERITY_ERROR)]
 
 
 class RubberPostProcessor(PostProcessor):
-	"""
-	This is a post-processor for rubber
-	"""
-	
-	_log = getLogger("RubberPostProcessor")
-	
-	name = "RubberPostProcessor"
-	
-	_pattern = re.compile(r"(?P<file>[a-zA-Z0-9./_-]+)(:(?P<line>[0-9\-]+))?:(?P<text>.*?)$", re.MULTILINE)
-	
-	def __init__(self):
-		self._issues = None
-		self._summary = None
-		self._successful = False
-		
-		# FIXME: circ dep
-		from ..preferences import Preferences
-		
-		self._hide_box_warnings = Preferences().get_bool("hide-box-warnings")
-	
-	@property
-	def successful(self):
-		return self._successful
-	
-	@property
-	def issues(self):
-		return self._issues
-	
-	@property
-	def summary(self):
-		return self._summary
-	
-	def process(self, file, stdout, stderr, condition):
-		from ..base import File		# FIXME: this produces a circ dep on toplevel
-		
-		self._issues = []
-		
-		self._log.debug("process(): stdout=\"%s\", stderr=\"%s\"" % (stdout, stderr))
-		
-		self._successful = not bool(condition)
-		
-		for match in self._pattern.finditer(stderr):
-			severity = Issue.SEVERITY_ERROR
-			
-			# text
-			text = match.group("text")
-			
-			if "Underfull" in text or "Overfull" in text:
-				if self._hide_box_warnings:
-					continue
-				else:
-					severity = Issue.SEVERITY_WARNING
-			
-			# line(s)
-			lineFrom, lineTo = None, None
-			
-			line = match.group("line")
-			
-			if line:
-				parts = line.split("-")
-				lineFrom = int(parts[0]) - 1
-				if len(parts) > 1:
-					lineTo = int(parts[1]) - 1
-			
-			# filename
-			filename = "%s/%s" % (file.dirname, match.group("file"))
-			
-			self._issues.append(Issue(escape(text), lineFrom, lineTo, File(filename), severity, Issue.POSITION_LINE))
-			
-	
-	
\ No newline at end of file
+    """
+    This is a post-processor for rubber
+    """
+
+    _log = getLogger("RubberPostProcessor")
+
+    name = "RubberPostProcessor"
+
+    _pattern = re.compile(r"(?P<file>[a-zA-Z0-9./_-]+)(:(?P<line>[0-9\-]+))?:(?P<text>.*?)$", re.MULTILINE)
+
+    def __init__(self):
+        self._issues = None
+        self._summary = None
+        self._successful = False
+
+        # FIXME: circ dep
+        from ..preferences import Preferences
+
+        self._hide_box_warnings = Preferences().get_bool("hide-box-warnings")
+
+    @property
+    def successful(self):
+        return self._successful
+
+    @property
+    def issues(self):
+        return self._issues
+
+    @property
+    def summary(self):
+        return self._summary
+
+    def process(self, file, stdout, stderr, condition):
+        from ..base import File        # FIXME: this produces a circ dep on toplevel
+
+        self._issues = []
+
+        self._log.debug("process(): stdout=\"%s\", stderr=\"%s\"" % (stdout, stderr))
+
+        self._successful = not bool(condition)
+
+        for match in self._pattern.finditer(stderr):
+            severity = Issue.SEVERITY_ERROR
+
+            # text
+            text = match.group("text")
+
+            if "Underfull" in text or "Overfull" in text:
+                if self._hide_box_warnings:
+                    continue
+                else:
+                    severity = Issue.SEVERITY_WARNING
+
+            # line(s)
+            lineFrom, lineTo = None, None
+
+            line = match.group("line")
+
+            if line:
+                parts = line.split("-")
+                lineFrom = int(parts[0]) - 1
+                if len(parts) > 1:
+                    lineTo = int(parts[1]) - 1
+
+            # filename
+            filename = "%s/%s" % (file.dirname, match.group("file"))
+
+            self._issues.append(Issue(escape(text), lineFrom, lineTo, File(filename), severity, Issue.POSITION_LINE))
+
+
diff --git a/latex/tools/util.py b/latex/tools/util.py
index 466d233..a923141 100644
--- a/latex/tools/util.py
+++ b/latex/tools/util.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -31,92 +31,91 @@ from gi.repository import GObject
 
 
 class Process(object):
-	"""
-	This runs a command in a child process and polls the output
-	"""
-	
-	__log = getLogger("Process")
-	
-	# intervall of polling stdout of the child process
-	__POLL_INTERVAL = 250
-	
-	def execute(self, command):
-		self.__log.debug("execute: %s" % command)
-		
-		# run child process
-		self.__process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, 
-										stderr=subprocess.PIPE)
-		
-		# unblock pipes
-		fcntl.fcntl(self.__process.stdout, fcntl.F_SETFL, os.O_NONBLOCK)
-		fcntl.fcntl(self.__process.stderr, fcntl.F_SETFL, os.O_NONBLOCK)
-		
-		# monitor process and pipes
-		self.__handlers = [ GObject.timeout_add(self.__POLL_INTERVAL, self.__on_stdout),
-							GObject.timeout_add(self.__POLL_INTERVAL, self.__on_stderr),
-							GObject.child_watch_add(self.__process.pid, self.__on_exit) ]
-	
-	def abort(self):
-		"""
-		Abort the running process
-		"""
-		if self.__process:
-			for handler in self.__handlers:
-				GObject.source_remove(handler)
-			
-			try:
-				os.kill(self.__process.pid, signal.SIGTERM)
-				
-				self._on_abort()
-			except OSError, e:
-				self.__log.error("Failed to abort process: %s" % e)
-			
-	def __on_stdout(self):
-		try:
-			s = self.__process.stdout.read()
-			if len(s):
-				self._on_stdout(s)
-		except IOError:
-			pass
-		return True
-	
-	def __on_stderr(self):
-		try:
-			s = self.__process.stderr.read()
-			if len(s):
-				self._on_stderr(s)
-		except IOError:
-			pass
-		return True
-	
-	def __on_exit(self, pid, condition):
-		for handler in self.__handlers:
-			GObject.source_remove(handler)
-		
-		# read remaining output
-		self.__on_stdout()
-		self.__on_stderr()
-		
-		self._on_exit(condition)
-		
-	def _on_stdout(self, text):
-		"""
-		To be overridden
-		"""
-		
-	def _on_stderr(self, text):
-		"""
-		To be overridden
-		"""
-	
-	def _on_abort(self):
-		"""
-		To be overridden
-		"""
-	
-	def _on_exit(self, condition):
-		"""
-		To be overridden
-		"""
-		
-		
\ No newline at end of file
+    """
+    This runs a command in a child process and polls the output
+    """
+
+    __log = getLogger("Process")
+
+    # intervall of polling stdout of the child process
+    __POLL_INTERVAL = 250
+
+    def execute(self, command):
+        self.__log.debug("execute: %s" % command)
+
+        # run child process
+        self.__process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
+                                        stderr=subprocess.PIPE)
+
+        # unblock pipes
+        fcntl.fcntl(self.__process.stdout, fcntl.F_SETFL, os.O_NONBLOCK)
+        fcntl.fcntl(self.__process.stderr, fcntl.F_SETFL, os.O_NONBLOCK)
+
+        # monitor process and pipes
+        self.__handlers = [ GObject.timeout_add(self.__POLL_INTERVAL, self.__on_stdout),
+                            GObject.timeout_add(self.__POLL_INTERVAL, self.__on_stderr),
+                            GObject.child_watch_add(self.__process.pid, self.__on_exit) ]
+
+    def abort(self):
+        """
+        Abort the running process
+        """
+        if self.__process:
+            for handler in self.__handlers:
+                GObject.source_remove(handler)
+
+            try:
+                os.kill(self.__process.pid, signal.SIGTERM)
+
+                self._on_abort()
+            except OSError, e:
+                self.__log.error("Failed to abort process: %s" % e)
+
+    def __on_stdout(self):
+        try:
+            s = self.__process.stdout.read()
+            if len(s):
+                self._on_stdout(s)
+        except IOError:
+            pass
+        return True
+
+    def __on_stderr(self):
+        try:
+            s = self.__process.stderr.read()
+            if len(s):
+                self._on_stderr(s)
+        except IOError:
+            pass
+        return True
+
+    def __on_exit(self, pid, condition):
+        for handler in self.__handlers:
+            GObject.source_remove(handler)
+
+        # read remaining output
+        self.__on_stdout()
+        self.__on_stderr()
+
+        self._on_exit(condition)
+
+    def _on_stdout(self, text):
+        """
+        To be overridden
+        """
+
+    def _on_stderr(self, text):
+        """
+        To be overridden
+        """
+
+    def _on_abort(self):
+        """
+        To be overridden
+        """
+
+    def _on_exit(self, condition):
+        """
+        To be overridden
+        """
+
diff --git a/latex/tools/views.py b/latex/tools/views.py
index 9bf5367..2008da3 100755
--- a/latex/tools/views.py
+++ b/latex/tools/views.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -34,167 +34,167 @@ from ..issues import Issue, IStructuredIssueHandler
 
 
 class ToolView(BottomView, IStructuredIssueHandler):
-	"""
-	"""
-	
-	_log = getLogger("ToolView")
-	
-	label = "Tools"
-	icon = Gtk.Image.new_from_stock(Gtk.STOCK_CONVERT, Gtk.IconSize.MENU)
-	scope = View.SCOPE_WINDOW
-	
-	_ICON_RUN = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/run.png"))
-	_ICON_FAIL = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/error.png"))
-	_ICON_SUCCESS = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/okay.png"))
-	_ICON_ERROR = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/error.png"))
-	_ICON_WARNING = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/warning.png"))
-	_ICON_ABORT = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/abort.png"))
-	
-	def __init__(self, context):
-		BottomView.__init__(self, context)
-		self._handlers = {}
-		
-	def init(self, context):
-		self._log.debug("init")
-		
-		self._context = context
-		
-		self._scroll = Gtk.ScrolledWindow()
-		self._scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
-		self._scroll.set_shadow_type(Gtk.ShadowType.IN)
-		
-		self._store = Gtk.TreeStore(GdkPixbuf.Pixbuf, str, str, str, object)	# icon, message, file, line, Issue object
-		
-		self._view = Gtk.TreeView(model=self._store)
-		
-		column = Gtk.TreeViewColumn("Job")
-
-		pixbuf_renderer = Gtk.CellRendererPixbuf()
-		column.pack_start(pixbuf_renderer, False)
-		column.add_attribute(pixbuf_renderer, "pixbuf", 0)
-		
-		text_renderer = Gtk.CellRendererText()
-		column.pack_start(text_renderer, True)
-		column.add_attribute(text_renderer, "markup", 1)
-		
-		self._view.append_column(column)
-		self._view.append_column(Gtk.TreeViewColumn("File", Gtk.CellRendererText(), text=2))
-		self._view.append_column(Gtk.TreeViewColumn("Line", Gtk.CellRendererText(), text=3))
-		
-		self._handlers[self._view] = self._view.connect("row-activated", self._on_row_activated)
-		
-		self._scroll.add(self._view)
-		
-		self.pack_start(self._scroll, True, True, 0)
-		
-		# toolbar
-		
-		self._buttonCancel = Gtk.ToolButton(icon_name=Gtk.STOCK_STOP)
-		self._buttonCancel.set_sensitive(False)
-		self._buttonCancel.set_tooltip_text("Abort Job")
-		self._handlers[self._buttonCancel] = self._buttonCancel.connect("clicked", self._on_abort_clicked)
-		
-		self._buttonDetails = Gtk.ToolButton(icon_name=Gtk.STOCK_INFO)
-		self._buttonDetails.set_sensitive(False)
-		self._buttonDetails.set_tooltip_text("Show Detailed Output")
-		self._handlers[self._buttonDetails] = self._buttonDetails.connect("clicked", self._on_details_clicked)
-
-		self._toolbar = Gtk.Toolbar()
-		self._toolbar.set_style(Gtk.ToolbarStyle.ICONS)
-		self._toolbar.set_icon_size(Gtk.IconSize.SMALL_TOOLBAR)		# FIXME: deprecated???
-		self._toolbar.set_orientation(Gtk.Orientation.VERTICAL)
-		self._toolbar.insert(self._buttonCancel, -1)
-		self._toolbar.insert(self._buttonDetails, -1)
-
-		self.pack_start(self._toolbar, False, False, 0)
-
-		# theme like gtk3
-		ctx = self._scroll.get_style_context()
-		ctx.set_junction_sides(Gtk.JunctionSides.RIGHT)
-
-		ctx = self._toolbar.get_style_context()
-		ctx.set_junction_sides(Gtk.JunctionSides.LEFT | Gtk.JunctionSides.RIGHT)
-		ctx.add_class("inline-toolbar")
-
-	
-	def _on_abort_clicked(self, button):
-		self._abort_method.__call__()
-	
-	def _on_details_clicked(self, button):
-		"""
-		The details button has been clicked
-		"""
-	
-	def _on_row_activated(self, view, path, column):
-		issue = self._store.get(self._store.get_iter(path), 4)[0]
-		if issue:
-			self._context.activate_editor(issue.file)
-			if self._context.active_editor:
-				self._context.active_editor.select_lines(issue.start)
-			else:
-				self._log.error("No Editor object for calling select_lines")
-	
-	def clear(self):
-		self.assure_init()
-		self._store.clear()
-	
-	def set_abort_enabled(self, enabled, method):
-		# see issues.IStructuredIssueHandler.set_abort_enabled
-		
-		self._abort_method = method
-		self._buttonCancel.set_sensitive(enabled)
-	
-	def add_partition(self, label, state, parent_partition_id=None):
-		"""
-		Add a new partition
-		
-		@param label: a label used in the UI
-		@return: a unique id for the partition (here a Gtk.TreeIter)
-		"""
-		icon = None
-		if state == "running":
-			icon = self._ICON_RUN
-		elif state == "succeeded":
-			icon = self._ICON_SUCCESS
-		elif state == "failed":
-			icon = self._ICON_FAIL
-		elif state == "aborted":
-			icon = self._ICON_ABORT
-			
-		self._view.expand_all()
-		
-		return self._store.append(parent_partition_id, [icon, label, "", "", None])
-	
-	def set_partition_state(self, partition_id, state):
-		# see IStructuredIssueHandler.set_partition_state
-		
-		icon = None
-		if state == "running":
-			icon = self._ICON_RUN
-		elif state == "succeeded":
-			icon = self._ICON_SUCCESS
-		elif state == "failed":
-			icon = self._ICON_FAIL
-		elif state == "aborted":
-			icon = self._ICON_ABORT
-			
-		self._store.set_value(partition_id, 0, icon)
-	
-	def append_issues(self, partition_id, issues):
-		for issue in issues:
-			icon = None
-			if issue.severity == Issue.SEVERITY_WARNING:
-				icon = self._ICON_WARNING
-			elif issue.severity == Issue.SEVERITY_ERROR:
-				icon = self._ICON_ERROR
-			self._store.append(partition_id, [icon, issue.message, issue.file.basename, str(issue.start), issue])
-			
-			self._log.debug(str(issue))
-			
-		self._view.expand_all()
-	
-	def destroy(self):
-		for obj in self._handlers:
-			obj.disconnect(self._handlers[obj])
-		BottomView.destroy(self)
-	
+    """
+    """
+
+    _log = getLogger("ToolView")
+
+    label = "Tools"
+    icon = Gtk.Image.new_from_stock(Gtk.STOCK_CONVERT, Gtk.IconSize.MENU)
+    scope = View.SCOPE_WINDOW
+
+    _ICON_RUN = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/run.png"))
+    _ICON_FAIL = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/error.png"))
+    _ICON_SUCCESS = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/okay.png"))
+    _ICON_ERROR = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/error.png"))
+    _ICON_WARNING = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/warning.png"))
+    _ICON_ABORT = GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/abort.png"))
+
+    def __init__(self, context):
+        BottomView.__init__(self, context)
+        self._handlers = {}
+
+    def init(self, context):
+        self._log.debug("init")
+
+        self._context = context
+
+        self._scroll = Gtk.ScrolledWindow()
+        self._scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+        self._scroll.set_shadow_type(Gtk.ShadowType.IN)
+
+        self._store = Gtk.TreeStore(GdkPixbuf.Pixbuf, str, str, str, object)    # icon, message, file, line, Issue object
+
+        self._view = Gtk.TreeView(model=self._store)
+
+        column = Gtk.TreeViewColumn("Job")
+
+        pixbuf_renderer = Gtk.CellRendererPixbuf()
+        column.pack_start(pixbuf_renderer, False)
+        column.add_attribute(pixbuf_renderer, "pixbuf", 0)
+
+        text_renderer = Gtk.CellRendererText()
+        column.pack_start(text_renderer, True)
+        column.add_attribute(text_renderer, "markup", 1)
+
+        self._view.append_column(column)
+        self._view.append_column(Gtk.TreeViewColumn("File", Gtk.CellRendererText(), text=2))
+        self._view.append_column(Gtk.TreeViewColumn("Line", Gtk.CellRendererText(), text=3))
+
+        self._handlers[self._view] = self._view.connect("row-activated", self._on_row_activated)
+
+        self._scroll.add(self._view)
+
+        self.pack_start(self._scroll, True, True, 0)
+
+        # toolbar
+
+        self._buttonCancel = Gtk.ToolButton(icon_name=Gtk.STOCK_STOP)
+        self._buttonCancel.set_sensitive(False)
+        self._buttonCancel.set_tooltip_text("Abort Job")
+        self._handlers[self._buttonCancel] = self._buttonCancel.connect("clicked", self._on_abort_clicked)
+
+        self._buttonDetails = Gtk.ToolButton(icon_name=Gtk.STOCK_INFO)
+        self._buttonDetails.set_sensitive(False)
+        self._buttonDetails.set_tooltip_text("Show Detailed Output")
+        self._handlers[self._buttonDetails] = self._buttonDetails.connect("clicked", self._on_details_clicked)
+
+        self._toolbar = Gtk.Toolbar()
+        self._toolbar.set_style(Gtk.ToolbarStyle.ICONS)
+        self._toolbar.set_icon_size(Gtk.IconSize.SMALL_TOOLBAR)        # FIXME: deprecated???
+        self._toolbar.set_orientation(Gtk.Orientation.VERTICAL)
+        self._toolbar.insert(self._buttonCancel, -1)
+        self._toolbar.insert(self._buttonDetails, -1)
+
+        self.pack_start(self._toolbar, False, False, 0)
+
+        # theme like gtk3
+        ctx = self._scroll.get_style_context()
+        ctx.set_junction_sides(Gtk.JunctionSides.RIGHT)
+
+        ctx = self._toolbar.get_style_context()
+        ctx.set_junction_sides(Gtk.JunctionSides.LEFT | Gtk.JunctionSides.RIGHT)
+        ctx.add_class("inline-toolbar")
+
+
+    def _on_abort_clicked(self, button):
+        self._abort_method.__call__()
+
+    def _on_details_clicked(self, button):
+        """
+        The details button has been clicked
+        """
+
+    def _on_row_activated(self, view, path, column):
+        issue = self._store.get(self._store.get_iter(path), 4)[0]
+        if issue:
+            self._context.activate_editor(issue.file)
+            if self._context.active_editor:
+                self._context.active_editor.select_lines(issue.start)
+            else:
+                self._log.error("No Editor object for calling select_lines")
+
+    def clear(self):
+        self.assure_init()
+        self._store.clear()
+
+    def set_abort_enabled(self, enabled, method):
+        # see issues.IStructuredIssueHandler.set_abort_enabled
+
+        self._abort_method = method
+        self._buttonCancel.set_sensitive(enabled)
+
+    def add_partition(self, label, state, parent_partition_id=None):
+        """
+        Add a new partition
+
+        @param label: a label used in the UI
+        @return: a unique id for the partition (here a Gtk.TreeIter)
+        """
+        icon = None
+        if state == "running":
+            icon = self._ICON_RUN
+        elif state == "succeeded":
+            icon = self._ICON_SUCCESS
+        elif state == "failed":
+            icon = self._ICON_FAIL
+        elif state == "aborted":
+            icon = self._ICON_ABORT
+
+        self._view.expand_all()
+
+        return self._store.append(parent_partition_id, [icon, label, "", "", None])
+
+    def set_partition_state(self, partition_id, state):
+        # see IStructuredIssueHandler.set_partition_state
+
+        icon = None
+        if state == "running":
+            icon = self._ICON_RUN
+        elif state == "succeeded":
+            icon = self._ICON_SUCCESS
+        elif state == "failed":
+            icon = self._ICON_FAIL
+        elif state == "aborted":
+            icon = self._ICON_ABORT
+
+        self._store.set_value(partition_id, 0, icon)
+
+    def append_issues(self, partition_id, issues):
+        for issue in issues:
+            icon = None
+            if issue.severity == Issue.SEVERITY_WARNING:
+                icon = self._ICON_WARNING
+            elif issue.severity == Issue.SEVERITY_ERROR:
+                icon = self._ICON_ERROR
+            self._store.append(partition_id, [icon, issue.message, issue.file.basename, str(issue.start), issue])
+
+            self._log.debug(str(issue))
+
+        self._view.expand_all()
+
+    def destroy(self):
+        for obj in self._handlers:
+            obj.disconnect(self._handlers[obj])
+        BottomView.destroy(self)
+
diff --git a/latex/util.py b/latex/util.py
index 77b8119..51953f9 100644
--- a/latex/util.py
+++ b/latex/util.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -29,65 +29,65 @@ import logging
 from gi.repository import GdkPixbuf
 
 def singleton(cls):
-	"""
-	Singleton decorator that works with GObject derived types. The 'recommended'
-	python one - http://wiki.python.org/moin/PythonDecoratorLibrary#Singleton
-	does not (interacts badly with GObjectMeta
-	"""
-	instances = {}
-	def getinstance():
-		if cls not in instances:
-			instances[cls] = cls()
-		return instances[cls]
-	return getinstance
+    """
+    Singleton decorator that works with GObject derived types. The 'recommended'
+    python one - http://wiki.python.org/moin/PythonDecoratorLibrary#Singleton
+    does not (interacts badly with GObjectMeta
+    """
+    instances = {}
+    def getinstance():
+        if cls not in instances:
+            instances[cls] = cls()
+        return instances[cls]
+    return getinstance
 
 def require(arg_name, *allowed_types):
-	"""
-	Type-checking decorator (see http://code.activestate.com/recipes/454322/)
-	
-	Usage:
-	
-	@require("x", int, float)
-	@require("y", float)
-	def foo(x, y):
-		return x+y
-		
-	print foo(1, 2.5)	  	# Prints 3.5.
-	print foo(2.0, 2.5)		# Prints 4.5.
-	print foo("asdf", 2.5) 	# Raises TypeError exception.
-	print foo(1, 2)			# Raises TypeError exception.
-	"""
-	def make_wrapper(f):
-		if hasattr(f, "wrapped_args"):
-			wrapped_args = getattr(f, "wrapped_args")
-		else:
-			code = f.func_code
-			wrapped_args = list(code.co_varnames[:code.co_argcount])
-
-		try:
-			arg_index = wrapped_args.index(arg_name)
-		except ValueError:
-			raise NameError, arg_name
-
-		def wrapper(*args, **kwargs):
-			if len(args) > arg_index:
-				arg = args[arg_index]
-				if not isinstance(arg, allowed_types):
-					type_list = " or ".join(str(allowed_type) for allowed_type in allowed_types)
-					raise TypeError, "Expected '%s' to be %s; was %s." % (arg_name, type_list, type(arg))
-			else:
-				if arg_name in kwargs:
-					arg = kwargs[arg_name]
-					if not isinstance(arg, allowed_types):
-						type_list = " or ".join(str(allowed_type) for allowed_type in allowed_types)
-						raise TypeError, "Expected '%s' to be %s; was %s." % (arg_name, type_list, type(arg))
-
-			return f(*args, **kwargs)
-
-		wrapper.wrapped_args = wrapped_args
-		return wrapper
-
-	return make_wrapper
+    """
+    Type-checking decorator (see http://code.activestate.com/recipes/454322/)
+
+    Usage:
+
+    @require("x", int, float)
+    @require("y", float)
+    def foo(x, y):
+        return x+y
+
+    print foo(1, 2.5)          # Prints 3.5.
+    print foo(2.0, 2.5)        # Prints 4.5.
+    print foo("asdf", 2.5)     # Raises TypeError exception.
+    print foo(1, 2)            # Raises TypeError exception.
+    """
+    def make_wrapper(f):
+        if hasattr(f, "wrapped_args"):
+            wrapped_args = getattr(f, "wrapped_args")
+        else:
+            code = f.func_code
+            wrapped_args = list(code.co_varnames[:code.co_argcount])
+
+        try:
+            arg_index = wrapped_args.index(arg_name)
+        except ValueError:
+            raise NameError, arg_name
+
+        def wrapper(*args, **kwargs):
+            if len(args) > arg_index:
+                arg = args[arg_index]
+                if not isinstance(arg, allowed_types):
+                    type_list = " or ".join(str(allowed_type) for allowed_type in allowed_types)
+                    raise TypeError, "Expected '%s' to be %s; was %s." % (arg_name, type_list, type(arg))
+            else:
+                if arg_name in kwargs:
+                    arg = kwargs[arg_name]
+                    if not isinstance(arg, allowed_types):
+                        type_list = " or ".join(str(allowed_type) for allowed_type in allowed_types)
+                        raise TypeError, "Expected '%s' to be %s; was %s." % (arg_name, type_list, type(arg))
+
+            return f(*args, **kwargs)
+
+        wrapper.wrapped_args = wrapped_args
+        return wrapper
+
+    return make_wrapper
 
 
 from gi.repository import Gtk
@@ -96,111 +96,111 @@ from xml.sax import saxutils
 
 
 class StringReader(object):
-	"""
-	A simple string reader that is able to push back one character
-	"""
-	def __init__(self, string):
-		self._iter = iter(string)
-		self.offset = 0
-		self._pushbackChar = None
-		self._pushbackFlag = False
-	
-	def read(self):
-		if self._pushbackFlag:
-			self._pushbackFlag = False
-			return self._pushbackChar
-		else:
-			self.offset += 1
-			return self._iter.next()
-	
-	def unread(self, char):
-		#assert not self._pushbackFlag
-		
-		self._pushbackChar = char
-		self._pushbackFlag = True
+    """
+    A simple string reader that is able to push back one character
+    """
+    def __init__(self, string):
+        self._iter = iter(string)
+        self.offset = 0
+        self._pushbackChar = None
+        self._pushbackFlag = False
+
+    def read(self):
+        if self._pushbackFlag:
+            self._pushbackFlag = False
+            return self._pushbackChar
+        else:
+            self.offset += 1
+            return self._iter.next()
+
+    def unread(self, char):
+        #assert not self._pushbackFlag
+
+        self._pushbackChar = char
+        self._pushbackFlag = True
 
 
 def escape(string, remove_newlines=True):
-	"""
-	Prepares a string for inclusion in Pango markup and error messages
-	"""
-	s = saxutils.escape(string)
-	s = s.replace("\n", " ")
-	s = s.replace("\"", "&quot;")
-	return s
-	
+    """
+    Prepares a string for inclusion in Pango markup and error messages
+    """
+    s = saxutils.escape(string)
+    s = s.replace("\n", " ")
+    s = s.replace("\"", "&quot;")
+    return s
+
 
 def open_error(message, secondary_message=None):
-	"""
-	Popup an error dialog window
-	"""
-	dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL|Gtk.DialogFlags.DESTROY_WITH_PARENT, 
-							Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, message)
-	if secondary_message:
-		# TODO: why not use markup?
-		dialog.format_secondary_text(secondary_message)
-	dialog.run()
-	dialog.destroy()
+    """
+    Popup an error dialog window
+    """
+    dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL|Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                            Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, message)
+    if secondary_message:
+        # TODO: why not use markup?
+        dialog.format_secondary_text(secondary_message)
+    dialog.run()
+    dialog.destroy()
 
 
 def open_info(message, secondary_message=None):
-	"""
-	Popup an info dialog window
-	"""
-	dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL|Gtk.DialogFlags.DESTROY_WITH_PARENT, 
-							Gtk.MessageType.INFO, Gtk.ButtonsType.OK, message)
-	if secondary_message:
-		dialog.format_secondary_markup(secondary_message)
-	dialog.run()
-	dialog.destroy()
-	
+    """
+    Popup an info dialog window
+    """
+    dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL|Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                            Gtk.MessageType.INFO, Gtk.ButtonsType.OK, message)
+    if secondary_message:
+        dialog.format_secondary_markup(secondary_message)
+    dialog.run()
+    dialog.destroy()
+
 
 def verbose(function):
-	"""
-	'verbose'-decorator. This runs the decorated method in a try-except-block
-	and shows an error dialog on exception.
-	"""
-	def decorated_function(*args, **kw):
-		try:
-			return function(*args, **kw)
-		except Exception, e:
-			stack = traceback.format_exc(limit=10)
-			open_error(str(e), stack)
-	return decorated_function
+    """
+    'verbose'-decorator. This runs the decorated method in a try-except-block
+    and shows an error dialog on exception.
+    """
+    def decorated_function(*args, **kw):
+        try:
+            return function(*args, **kw)
+        except Exception, e:
+            stack = traceback.format_exc(limit=10)
+            open_error(str(e), stack)
+    return decorated_function
 
 
 class GladeInterface(object):
-	"""
-	Utility base class for interfaces loaded from a Glade definition 
-	"""
-	
-	__log = logging.getLogger("GladeInterface")
-	
-	filename = None
-	
-	def __init__(self):
-		self.__tree = None
-	
-	def __get_tree(self):
-		if not self.__tree:
-			self.__tree = Gtk.Builder()
-			self.__tree.add_from_file(self.filename)
-		return self.__tree
-	
-	def find_widget(self, name):
-		"""
-		Find a widget by its name
-		"""
-		widget = self.__get_tree().get_object(name)
-		if widget is None:
-			self.__log.error("Widget '%s' could not be found in interface description '%s'" % (name, self.filename))
-		return widget
-	
-	def connect_signals(self, mapping):
-		"""
-		Auto-connect signals
-		"""
-		self.__get_tree().connect_signals(mapping)
+    """
+    Utility base class for interfaces loaded from a Glade definition
+    """
+
+    __log = logging.getLogger("GladeInterface")
+
+    filename = None
+
+    def __init__(self):
+        self.__tree = None
+
+    def __get_tree(self):
+        if not self.__tree:
+            self.__tree = Gtk.Builder()
+            self.__tree.add_from_file(self.filename)
+        return self.__tree
+
+    def find_widget(self, name):
+        """
+        Find a widget by its name
+        """
+        widget = self.__get_tree().get_object(name)
+        if widget is None:
+            self.__log.error("Widget '%s' could not be found in interface description '%s'" % (name, self.filename))
+        return widget
+
+    def connect_signals(self, mapping):
+        """
+        Auto-connect signals
+        """
+        self.__get_tree().connect_signals(mapping)
 
 
 from uuid import uuid1
@@ -210,44 +210,44 @@ from base import Action
 
 
 class IconAction(Action):
-	"""
-	A utility class for creating actions with a custom icon instead of
-	a gtk stock id.
-	
-	The subclass must provide a field 'icon'.
-	"""
-
-	__stock_id = None
-
-	def __init__(self, *args, **kwargs):
-		self.__icon_factory = kwargs["icon_factory"]
-	
-	@property
-	def icon(self):
-		"""
-		Return a File object for the icon to use
-		"""
-		raise NotImplementedError
-	
-	def __init_stock_id(self):
-		#
-		# generate a new stock id
-		#
-		self.__stock_id = str(uuid1())
-		self.__icon_factory.add(
-				self.__stock_id,
-				Gtk.IconSet.new_from_pixbuf(
-					GdkPixbuf.Pixbuf.new_from_file(self.icon.path)))
-
-	@property
-	def stock_id(self):
-		if self.icon:
-			if not self.__stock_id:
-				self.__init_stock_id()
-			return self.__stock_id
-		else:
-			return None
-	
-	
-	
-	
+    """
+    A utility class for creating actions with a custom icon instead of
+    a gtk stock id.
+
+    The subclass must provide a field 'icon'.
+    """
+
+    __stock_id = None
+
+    def __init__(self, *args, **kwargs):
+        self.__icon_factory = kwargs["icon_factory"]
+
+    @property
+    def icon(self):
+        """
+        Return a File object for the icon to use
+        """
+        raise NotImplementedError
+
+    def __init_stock_id(self):
+        #
+        # generate a new stock id
+        #
+        self.__stock_id = str(uuid1())
+        self.__icon_factory.add(
+                self.__stock_id,
+                Gtk.IconSet.new_from_pixbuf(
+                    GdkPixbuf.Pixbuf.new_from_file(self.icon.path)))
+
+    @property
+    def stock_id(self):
+        if self.icon:
+            if not self.__stock_id:
+                self.__init_stock_id()
+            return self.__stock_id
+        else:
+            return None
+
+
+
+
diff --git a/latex/views.py b/latex/views.py
index 61cafe1..4623d98 100644
--- a/latex/views.py
+++ b/latex/views.py
@@ -11,7 +11,7 @@
 #
 # 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 
+# 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
@@ -33,176 +33,176 @@ from util import escape
 
 
 class IssueView(BottomView):
-	"""
-	"""
-	
-	_log = getLogger("IssueView")
-	
-	label = "Issues"
-	icon = Gtk.Image.new_from_stock(Gtk.STOCK_DIALOG_INFO, Gtk.IconSize.MENU)
-	scope = View.SCOPE_EDITOR
-	
-	def __init__(self, context, editor):
-		BottomView.__init__(self, context)
-		self._editor = editor
-		self._handlers = {}
-	
-	def init(self, context):
-		self._log.debug("init")
-		
-		self._preferences = Preferences()
-		self._preferences.connect("preferences-changed", self._on_preferences_changed)
-		self._show_tasks = self._preferences.get_bool("issues-show-tasks")
-		self._show_warnings = self._preferences.get_bool("issues-show-warnings")
-		
-		self._context = context
-		
-		self._icons = { Issue.SEVERITY_WARNING : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/warning.png")), 
-						Issue.SEVERITY_ERROR : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/error.png")), 
-			   			Issue.SEVERITY_INFO : None,
-			   			Issue.SEVERITY_TASK : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/task.png")) }
-		
-		self._store = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str, object)
-		
-		self._view = Gtk.TreeView(model=self._store)
-		
-		column = Gtk.TreeViewColumn()
-		column.set_title("Message")
-		
-		pixbuf_renderer = Gtk.CellRendererPixbuf()
-		column.pack_start(pixbuf_renderer, False)
-		column.add_attribute(pixbuf_renderer, "pixbuf", 0)
-		
-		text_renderer = Gtk.CellRendererText()
-		column.pack_start(text_renderer, True)
-		column.add_attribute(text_renderer, "markup", 1)
-		
-		self._view.append_column(column)
-		
-		column = Gtk.TreeViewColumn()
-		column.set_title("File")
-		text_renderer2 = Gtk.CellRendererText()
-		column.pack_start(text_renderer2, True)
-		column.add_attribute(text_renderer2, "markup", 2)
-		self._view.insert_column(column, -1)
-		self._handlers[self._view] = self._view.connect("row-activated", self._on_row_activated)
-		
-		self._scr = Gtk.ScrolledWindow()
-		
-		self._scr.add(self._view)
-		self._scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
-		self._scr.set_shadow_type(Gtk.ShadowType.IN)
-		
-		self.pack_start(self._scr, True, True, 0)
-		
-		# toolbar
-		
-		self._button_warnings = Gtk.ToggleToolButton()
-		self._button_warnings.set_tooltip_text("Show/Hide Warnings")
-		image = Gtk.Image()
-		image.set_from_file(find_resource("icons/warning.png"))
-		self._button_warnings.set_icon_widget(image)
-		self._button_warnings.set_active(self._show_warnings)
-		self._handlers[self._button_warnings] = self._button_warnings.connect("toggled", self.__on_warnings_toggled)
-		
-		self._button_tasks = Gtk.ToggleToolButton()
-		self._button_tasks.set_tooltip_text("Show/Hide Tasks")
-		imageTask = Gtk.Image()
-		imageTask.set_from_file(find_resource("icons/task.png"))
-		self._button_tasks.set_icon_widget(imageTask)
-		self._button_tasks.set_active(self._show_tasks)
-		self._handlers[self._button_tasks] = self._button_tasks.connect("toggled", self.__on_tasks_toggled)
-		
-		toolbar = Gtk.Toolbar()
-		toolbar.set_orientation(Gtk.Orientation.VERTICAL)
-		toolbar.set_style(Gtk.ToolbarStyle.ICONS)
-		toolbar.set_icon_size(Gtk.IconSize.MENU)
-		toolbar.insert(self._button_warnings, -1)
-		toolbar.insert(self._button_tasks, -1)
-		
-		self.pack_start(toolbar, False, False, 0)
-
-		# theme like gtk3
-		ctx = self._scr.get_style_context()
-		ctx.set_junction_sides(Gtk.JunctionSides.RIGHT)
-
-		ctx = toolbar.get_style_context()
-		ctx.set_junction_sides(Gtk.JunctionSides.LEFT | Gtk.JunctionSides.RIGHT)
-		ctx.add_class("inline-toolbar")
-		
-		self._issues = []
-		
-		self._log.debug("init finished")
-	
-	def _on_row_activated(self, view, path, column):
-		"""
-		A row has been double-clicked on
-		"""
-		issue = self._store.get(self._store.get_iter(path), 3)[0]
-		
-		self._context.activate_editor(issue.file)
-		
-		#~ # FIXME: this doesn't work correctly
-		#~ if not self._context.active_editor is None:
-			#~ self._context.active_editor.select(issue.start, issue.end)
-		self._editor.select(issue.start, issue.end)
-	
-	def _on_preferences_changed(self, prefs, key, value):
-		if key == "issues-show-warnings" or key == "issues-show-tasks":
-			# update filter
-			self._store.clear()
-			for issue, local in self._issues:
-				self.__append_issue_filtered(issue, local)
-	
-	def __on_tasks_toggled(self, togglebutton):
-		self._show_tasks = togglebutton.get_active()
-		self._preferences.set("issues-show-tasks", self._show_tasks)
-		
-	def __on_warnings_toggled(self, togglebutton):
-		self._show_warnings = togglebutton.get_active()
-		self._preferences.set("issues-show-warnings", self._show_warnings)
-	
-	def clear(self):
-		"""
-		Remove all issues from the view
-		"""
-		self.assure_init()
-		self._store.clear()
-		self._issues = []
-	
-	def append_issue(self, issue, local=True):
-		"""
-		Append a new Issue to the view
-		
-		@param issue: the Issue object
-		@param local: indicates whether the Issue occured in the edited file or not
-		"""
-		self.assure_init()
-		self._issues.append((issue, local))
-		self.__append_issue_filtered(issue, local)
-	
-	def __append_issue_filtered(self, issue, local):
-		if issue.severity == Issue.SEVERITY_WARNING:
-			if self._show_warnings:
-				self.__do_append_issue(issue, local)
-		elif issue.severity == Issue.SEVERITY_TASK:
-			if self._show_tasks:
-				self.__do_append_issue(issue, local)
-		else:
-			self.__do_append_issue(issue, local)
-	
-	def __do_append_issue(self, issue, local):
-		if local:
-			message = issue.message
-			filename = escape(issue.file.basename)
-		else:
-			message = "<span color='%s'>%s</span>" % (self._preferences.get("light-foreground-color"), issue.message)
-			filename = "<span color='%s'>%s</span>" % (self._preferences.get("light-foreground-color"), issue.file.basename)
-		self._store.append([self._icons[issue.severity], message, filename, issue])
-		
-	def destroy(self):
-		del self._editor
-		for obj in self._handlers:
-			obj.disconnect(self._handlers[obj])
-		BottomView.destroy(self)
-		
+    """
+    """
+
+    _log = getLogger("IssueView")
+
+    label = "Issues"
+    icon = Gtk.Image.new_from_stock(Gtk.STOCK_DIALOG_INFO, Gtk.IconSize.MENU)
+    scope = View.SCOPE_EDITOR
+
+    def __init__(self, context, editor):
+        BottomView.__init__(self, context)
+        self._editor = editor
+        self._handlers = {}
+
+    def init(self, context):
+        self._log.debug("init")
+
+        self._preferences = Preferences()
+        self._preferences.connect("preferences-changed", self._on_preferences_changed)
+        self._show_tasks = self._preferences.get_bool("issues-show-tasks")
+        self._show_warnings = self._preferences.get_bool("issues-show-warnings")
+
+        self._context = context
+
+        self._icons = { Issue.SEVERITY_WARNING : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/warning.png")),
+                        Issue.SEVERITY_ERROR : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/error.png")),
+                           Issue.SEVERITY_INFO : None,
+                           Issue.SEVERITY_TASK : GdkPixbuf.Pixbuf.new_from_file(find_resource("icons/task.png")) }
+
+        self._store = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str, object)
+
+        self._view = Gtk.TreeView(model=self._store)
+
+        column = Gtk.TreeViewColumn()
+        column.set_title("Message")
+
+        pixbuf_renderer = Gtk.CellRendererPixbuf()
+        column.pack_start(pixbuf_renderer, False)
+        column.add_attribute(pixbuf_renderer, "pixbuf", 0)
+
+        text_renderer = Gtk.CellRendererText()
+        column.pack_start(text_renderer, True)
+        column.add_attribute(text_renderer, "markup", 1)
+
+        self._view.append_column(column)
+
+        column = Gtk.TreeViewColumn()
+        column.set_title("File")
+        text_renderer2 = Gtk.CellRendererText()
+        column.pack_start(text_renderer2, True)
+        column.add_attribute(text_renderer2, "markup", 2)
+        self._view.insert_column(column, -1)
+        self._handlers[self._view] = self._view.connect("row-activated", self._on_row_activated)
+
+        self._scr = Gtk.ScrolledWindow()
+
+        self._scr.add(self._view)
+        self._scr.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+        self._scr.set_shadow_type(Gtk.ShadowType.IN)
+
+        self.pack_start(self._scr, True, True, 0)
+
+        # toolbar
+
+        self._button_warnings = Gtk.ToggleToolButton()
+        self._button_warnings.set_tooltip_text("Show/Hide Warnings")
+        image = Gtk.Image()
+        image.set_from_file(find_resource("icons/warning.png"))
+        self._button_warnings.set_icon_widget(image)
+        self._button_warnings.set_active(self._show_warnings)
+        self._handlers[self._button_warnings] = self._button_warnings.connect("toggled", self.__on_warnings_toggled)
+
+        self._button_tasks = Gtk.ToggleToolButton()
+        self._button_tasks.set_tooltip_text("Show/Hide Tasks")
+        imageTask = Gtk.Image()
+        imageTask.set_from_file(find_resource("icons/task.png"))
+        self._button_tasks.set_icon_widget(imageTask)
+        self._button_tasks.set_active(self._show_tasks)
+        self._handlers[self._button_tasks] = self._button_tasks.connect("toggled", self.__on_tasks_toggled)
+
+        toolbar = Gtk.Toolbar()
+        toolbar.set_orientation(Gtk.Orientation.VERTICAL)
+        toolbar.set_style(Gtk.ToolbarStyle.ICONS)
+        toolbar.set_icon_size(Gtk.IconSize.MENU)
+        toolbar.insert(self._button_warnings, -1)
+        toolbar.insert(self._button_tasks, -1)
+
+        self.pack_start(toolbar, False, False, 0)
+
+        # theme like gtk3
+        ctx = self._scr.get_style_context()
+        ctx.set_junction_sides(Gtk.JunctionSides.RIGHT)
+
+        ctx = toolbar.get_style_context()
+        ctx.set_junction_sides(Gtk.JunctionSides.LEFT | Gtk.JunctionSides.RIGHT)
+        ctx.add_class("inline-toolbar")
+
+        self._issues = []
+
+        self._log.debug("init finished")
+
+    def _on_row_activated(self, view, path, column):
+        """
+        A row has been double-clicked on
+        """
+        issue = self._store.get(self._store.get_iter(path), 3)[0]
+
+        self._context.activate_editor(issue.file)
+
+        #~ # FIXME: this doesn't work correctly
+        #~ if not self._context.active_editor is None:
+            #~ self._context.active_editor.select(issue.start, issue.end)
+        self._editor.select(issue.start, issue.end)
+
+    def _on_preferences_changed(self, prefs, key, value):
+        if key == "issues-show-warnings" or key == "issues-show-tasks":
+            # update filter
+            self._store.clear()
+            for issue, local in self._issues:
+                self.__append_issue_filtered(issue, local)
+
+    def __on_tasks_toggled(self, togglebutton):
+        self._show_tasks = togglebutton.get_active()
+        self._preferences.set("issues-show-tasks", self._show_tasks)
+
+    def __on_warnings_toggled(self, togglebutton):
+        self._show_warnings = togglebutton.get_active()
+        self._preferences.set("issues-show-warnings", self._show_warnings)
+
+    def clear(self):
+        """
+        Remove all issues from the view
+        """
+        self.assure_init()
+        self._store.clear()
+        self._issues = []
+
+    def append_issue(self, issue, local=True):
+        """
+        Append a new Issue to the view
+
+        @param issue: the Issue object
+        @param local: indicates whether the Issue occured in the edited file or not
+        """
+        self.assure_init()
+        self._issues.append((issue, local))
+        self.__append_issue_filtered(issue, local)
+
+    def __append_issue_filtered(self, issue, local):
+        if issue.severity == Issue.SEVERITY_WARNING:
+            if self._show_warnings:
+                self.__do_append_issue(issue, local)
+        elif issue.severity == Issue.SEVERITY_TASK:
+            if self._show_tasks:
+                self.__do_append_issue(issue, local)
+        else:
+            self.__do_append_issue(issue, local)
+
+    def __do_append_issue(self, issue, local):
+        if local:
+            message = issue.message
+            filename = escape(issue.file.basename)
+        else:
+            message = "<span color='%s'>%s</span>" % (self._preferences.get("light-foreground-color"), issue.message)
+            filename = "<span color='%s'>%s</span>" % (self._preferences.get("light-foreground-color"), issue.file.basename)
+        self._store.append([self._icons[issue.severity], message, filename, issue])
+
+    def destroy(self):
+        del self._editor
+        for obj in self._handlers:
+            obj.disconnect(self._handlers[obj])
+        BottomView.destroy(self)
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]