Source code for gramps.gui.widgets.undoablestyledbuffer

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009  Florian Heinle
# Copyright (C) 2010  Doug Blank <doug.blank@gmail.com>
# Copyright (C) 2010  Benny Malengier
# Copyright (C) 2014  Vassilii Khachaturov
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

""" 
gtk textbuffer with undo functionality 
"""

__all__ = ["UndoableStyledBuffer"]

from contextlib import contextmanager

from gi.repository import Gtk

from gramps.gen.lib.styledtext import StyledText
from .undoablebuffer import UndoableInsert, UndoableDelete
from .styledtextbuffer import StyledTextBuffer

[docs]class UndoableInsertStyled(UndoableInsert): """something that has been inserted into our styledtextbuffer""" def __init__(self, text_iter, text, length, text_buffer): #we obtain the buffer before the text has been inserted UndoableInsert.__init__(self, text_iter, text, length, text_buffer) self.tags = text_buffer.get_text(text_buffer.get_start_iter(), text_buffer.get_end_iter(), True).get_tags() self.tagsafter = None
[docs]class UndoableDeleteStyled(UndoableDelete): def __init__(self, text_buffer, start_iter, end_iter): #we obtain the buffer before the text has been deleted UndoableDelete.__init__(self, text_buffer, start_iter, end_iter) self.tags = text_buffer.get_text(text_buffer.get_start_iter(), text_buffer.get_end_iter(), True).get_tags()
[docs]class UndoableApplyStyle(): """a style has been applied to our textbuffer""" def __init__(self, text_buffer, tag, start, end): self.offset = text_buffer.get_iter_at_mark(text_buffer.get_insert()).get_offset() self.mergeable = False self.tags = text_buffer.get_text(text_buffer.get_start_iter(), text_buffer.get_end_iter(), True).get_tags() # self.tags_after = None self.offset_after = None
[docs] def set_after(self, tags, offset): self.tags_after = tags self.offset_after = offset
[docs]class UndoableStyledBuffer(StyledTextBuffer): """text buffer with added undo capabilities for styledtextbuffer designed as a drop-in replacement for gtksourceview, at least as far as undo is concerned""" insertclass = UndoableInsertStyled deleteclass = UndoableDeleteStyled def __init__(self): StyledTextBuffer.__init__(self) self.connect('apply-tag', self.on_tag_insert_undoable) self.connect_after('apply-tag', self.on_tag_afterinsert_undoable) @contextmanager
[docs] def undo_disabled(self): """ Assures that not_undoable_action is False during the context. Usage example (see gramps/gui/widgets/styledtexteditor.py):: with self.buffer.undo_disabled(): ... # heavy stuff like spell checking """ oldflag = self.not_undoable_action self.not_undoable_action = True try: yield except: raise finally: self.not_undoable_action = oldflag
[docs] def on_tag_insert_undoable(self, buffer, tag, start, end): if not self.undo_in_progress: self._empty_redo_stack() if self.not_undoable_action: return if end.get_offset() - start.get_offset() == 1: #only store this 1 character tag if in a different place if self.undo_stack and isinstance(self.undo_stack[-1], UndoableInsertStyled) and self.undo_stack[-1].offset + \ self.undo_stack[-1].length == end.get_offset(): return undo_action = UndoableApplyStyle(buffer, tag, start, end) self.undo_stack.append(undo_action)
[docs] def on_tag_afterinsert_undoable(self, buffer, tag, start, end): if self.not_undoable_action: return if not self.undo_stack or not isinstance(self.undo_stack[-1], UndoableApplyStyle): return self.undo_stack[-1].set_after(buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True).get_tags(), buffer.get_iter_at_mark(buffer.get_insert()).get_offset())
def _undo_insert(self, undo_action): start = self.get_iter_at_offset(undo_action.offset) stop = self.get_iter_at_offset( undo_action.offset + undo_action.length ) self.delete(start, stop) #the text is correct again, now we create correct styled text s_text = StyledText(Gtk.TextBuffer.get_text(self, self.get_start_iter(), self.get_end_iter(), True), undo_action.tags) self.set_text(s_text) self.place_cursor(self.get_iter_at_offset(undo_action.offset)) def _undo_delete(self, undo_action): start = self.get_iter_at_offset(undo_action.start) self.insert(start, undo_action.text) #the text is correct again, now we create correct styled text s_text = StyledText(Gtk.TextBuffer.get_text(self, self.get_start_iter(), self.get_end_iter(), True), undo_action.tags) self.set_text(s_text) if undo_action.delete_key_used: self.place_cursor(self.get_iter_at_offset(undo_action.start)) else: self.place_cursor(self.get_iter_at_offset(undo_action.end)) def _redo_insert(self, redo_action): s_text = StyledText(Gtk.TextBuffer.get_text(self, self.get_start_iter(), self.get_end_iter(), True), redo_action.tags) self.set_text(s_text) start = self.get_iter_at_offset(redo_action.offset) self.insert(start, redo_action.text) new_cursor_pos = self.get_iter_at_offset( redo_action.offset + redo_action.length ) self.place_cursor(new_cursor_pos) def _redo_delete(self, redo_action): start = self.get_iter_at_offset(redo_action.start) stop = self.get_iter_at_offset(redo_action.end) self.delete(start, stop) #the text is correct again, now we create correct styled text #s_text = StyledText(Gtk.TextBuffer.get_text(self, # self.get_start_iter(), self.get_end_iter(), True), redo_action.tags) #self.set_text(s_text) self.place_cursor(self.get_iter_at_offset(redo_action.start)) def _handle_undo(self, undo_action): """ undo of apply of style """ s_text = StyledText(Gtk.TextBuffer.get_text(self, self.get_start_iter(), self.get_end_iter(), True), undo_action.tags) self.set_text(s_text) self.place_cursor(self.get_iter_at_offset(undo_action.offset)) def _handle_redo(self, redo_action): """ redo of apply of style """ s_text = StyledText(Gtk.TextBuffer.get_text(self, self.get_start_iter(), self.get_end_iter(), True), redo_action.tags_after) self.set_text(s_text) self.place_cursor(self.get_iter_at_offset(redo_action.offset_after))