[gedit-latex] Move editor class to from base/__init__.py to base/editor.py
- From: Ignacio Casal Quinteiro <icq src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gedit-latex] Move editor class to from base/__init__.py to base/editor.py
- Date: Thu, 7 Jul 2011 16:22:30 +0000 (UTC)
commit a5597fa471935517d8949aea00ea09f74e8be12a
Author: Ignacio Casal Quinteiro <icq gnome org>
Date: Thu Jul 7 18:18:09 2011 +0200
Move editor class to from base/__init__.py to base/editor.py
latex/base/Makefile.am | 1 +
latex/base/__init__.py | 590 ----------------------------------------------
latex/base/editor.py | 611 ++++++++++++++++++++++++++++++++++++++++++++++++
latex/bibtex/editor.py | 2 +-
latex/latex/editor.py | 3 +-
5 files changed, 615 insertions(+), 592 deletions(-)
---
diff --git a/latex/base/Makefile.am b/latex/base/Makefile.am
index 0d2d640..f943dfb 100644
--- a/latex/base/Makefile.am
+++ b/latex/base/Makefile.am
@@ -5,6 +5,7 @@ plugin_PYTHON = \
completion.py \
config.py \
decorators.py \
+ editor.py \
file.py \
__init__.py \
job.py \
diff --git a/latex/base/__init__.py b/latex/base/__init__.py
index 26adec5..e1431d6 100644
--- a/latex/base/__init__.py
+++ b/latex/base/__init__.py
@@ -221,596 +221,6 @@ class Proposal(object):
return cmp(self.label.lower(), other.label.lower())
-import re
-from uuid import uuid1
-import time
-
-from .completion import CompletionDistributor
-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)
-
-
class WindowContext(object):
"""
The WindowContext is passed to Editors and is used to
diff --git a/latex/base/editor.py b/latex/base/editor.py
new file mode 100644
index 0000000..c6fce1d
--- /dev/null
+++ b/latex/base/editor.py
@@ -0,0 +1,611 @@
+# -*- coding: utf-8 -*-
+
+# This file is part of the Gedit LaTeX Plugin
+#
+# Copyright (C) 2010 Michael Zeising
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public Licence as published by the Free Software
+# Foundation; either version 2 of the Licence, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public 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
+
+import re
+from uuid import uuid1
+import time
+
+from gi.repository import GObject, Gtk, Gdk
+from .completion import CompletionDistributor
+from .templates import TemplateDelegate
+from logging import getLogger
+
+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)
+
+# ex:ts=4:et:
diff --git a/latex/bibtex/editor.py b/latex/bibtex/editor.py
index fba2ab5..0fe7663 100644
--- a/latex/bibtex/editor.py
+++ b/latex/bibtex/editor.py
@@ -24,7 +24,7 @@ bibtex.editor
from logging import getLogger
-from ..base import Editor
+from ..base.editor import Editor
from ..preferences import Preferences
from ..issues import Issue, IIssueHandler, MockIssueHandler
from ..util import verbose
diff --git a/latex/latex/editor.py b/latex/latex/editor.py
index 6a9b9e8..7f7a497 100644
--- a/latex/latex/editor.py
+++ b/latex/latex/editor.py
@@ -30,7 +30,8 @@ from logging import getLogger
if BENCHMARK: import time
-from ..base import Editor, File
+from ..base.editor import Editor
+from ..base.file import File
from completion import LaTeXCompletionHandler
from ..issues import Issue, IIssueHandler
from ..util import verbose, open_error
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]