undo and redo with correct style in noteeditor
svn: r15777
This commit is contained in:
@ -43,7 +43,7 @@ _LOG = logging.getLogger(".widgets.styledtextbuffer")
|
|||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
import gobject
|
import gobject
|
||||||
import gtk
|
import gtk
|
||||||
from gui.widgets.undoablebuffer import UndoableBuffer
|
from gui.widgets.undoablebuffer import UndoableBufferStyled
|
||||||
from pango import WEIGHT_BOLD, STYLE_ITALIC, UNDERLINE_SINGLE
|
from pango import WEIGHT_BOLD, STYLE_ITALIC, UNDERLINE_SINGLE
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
@ -226,7 +226,7 @@ class GtkSpellState(object):
|
|||||||
# StyledTextBuffer class
|
# StyledTextBuffer class
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
class StyledTextBuffer(UndoableBuffer):
|
class StyledTextBuffer(UndoableBufferStyled):
|
||||||
"""An extended TextBuffer for handling StyledText strings.
|
"""An extended TextBuffer for handling StyledText strings.
|
||||||
|
|
||||||
StyledTextBuffer is an interface between GRAMPS' L{StyledText} format
|
StyledTextBuffer is an interface between GRAMPS' L{StyledText} format
|
||||||
|
@ -73,6 +73,8 @@ FORMAT_TOOLBAR = '''
|
|||||||
<toolitem action="%d"/>
|
<toolitem action="%d"/>
|
||||||
<toolitem action="%d"/>
|
<toolitem action="%d"/>
|
||||||
<toolitem action="%d"/>
|
<toolitem action="%d"/>
|
||||||
|
<toolitem action="Undo"/>
|
||||||
|
<toolitem action="Redo"/>
|
||||||
<toolitem action="%d"/>
|
<toolitem action="%d"/>
|
||||||
<toolitem action="%d"/>
|
<toolitem action="%d"/>
|
||||||
<toolitem action="%d"/>
|
<toolitem action="%d"/>
|
||||||
@ -171,6 +173,7 @@ class StyledTextEditor(gtk.TextView):
|
|||||||
"""Setup initial instance variable values."""
|
"""Setup initial instance variable values."""
|
||||||
self.textbuffer = StyledTextBuffer()
|
self.textbuffer = StyledTextBuffer()
|
||||||
self.textbuffer.connect('style-changed', self._on_buffer_style_changed)
|
self.textbuffer.connect('style-changed', self._on_buffer_style_changed)
|
||||||
|
self.textbuffer.connect('changed', self._on_buffer_changed)
|
||||||
gtk.TextView.__init__(self, self.textbuffer)
|
gtk.TextView.__init__(self, self.textbuffer)
|
||||||
|
|
||||||
self.match = None
|
self.match = None
|
||||||
@ -480,6 +483,14 @@ class StyledTextEditor(gtk.TextView):
|
|||||||
# create the action group and insert all the actions
|
# create the action group and insert all the actions
|
||||||
self.action_group = gtk.ActionGroup('Format')
|
self.action_group = gtk.ActionGroup('Format')
|
||||||
self.action_group.add_toggle_actions(format_toggle_actions)
|
self.action_group.add_toggle_actions(format_toggle_actions)
|
||||||
|
self.undo_action = gtk.Action("Undo", _('Undo'), _('Undo'),
|
||||||
|
gtk.STOCK_UNDO)
|
||||||
|
self.undo_action.connect('activate', self.undo)
|
||||||
|
self.redo_action = gtk.Action("Redo", _('Redo'), _('Redo'),
|
||||||
|
gtk.STOCK_REDO)
|
||||||
|
self.redo_action.connect('activate', self.redo)
|
||||||
|
self.action_group.add_action(self.undo_action)
|
||||||
|
self.action_group.add_action(self.redo_action)
|
||||||
self.action_group.add_actions(format_actions)
|
self.action_group.add_actions(format_actions)
|
||||||
self.action_group.add_action(fontface_action)
|
self.action_group.add_action(fontface_action)
|
||||||
self.action_group.add_action(fontsize_action)
|
self.action_group.add_action(fontsize_action)
|
||||||
@ -494,6 +505,8 @@ class StyledTextEditor(gtk.TextView):
|
|||||||
# get the toolbar and set it's style
|
# get the toolbar and set it's style
|
||||||
toolbar = uimanager.get_widget('/ToolBar')
|
toolbar = uimanager.get_widget('/ToolBar')
|
||||||
toolbar.set_style(gtk.TOOLBAR_ICONS)
|
toolbar.set_style(gtk.TOOLBAR_ICONS)
|
||||||
|
self.undo_action.set_sensitive(False)
|
||||||
|
self.redo_action.set_sensitive(False)
|
||||||
|
|
||||||
return toolbar
|
return toolbar
|
||||||
|
|
||||||
@ -648,6 +661,11 @@ class StyledTextEditor(gtk.TextView):
|
|||||||
self.textbuffer.get_iter_at_offset(start),
|
self.textbuffer.get_iter_at_offset(start),
|
||||||
self.textbuffer.get_iter_at_offset(end+1))
|
self.textbuffer.get_iter_at_offset(end+1))
|
||||||
|
|
||||||
|
def _on_buffer_changed(self, buffer):
|
||||||
|
"""synchronize the undo/redo buttons with what is possible"""
|
||||||
|
self.undo_action.set_sensitive(self.textbuffer.can_undo)
|
||||||
|
self.redo_action.set_sensitive(self.textbuffer.can_redo)
|
||||||
|
|
||||||
def _on_buffer_style_changed(self, buffer, changed_styles):
|
def _on_buffer_style_changed(self, buffer, changed_styles):
|
||||||
"""Synchronize actions as the format changes at the buffer's cursor."""
|
"""Synchronize actions as the format changes at the buffer's cursor."""
|
||||||
for style, style_value in changed_styles.iteritems():
|
for style, style_value in changed_styles.iteritems():
|
||||||
@ -745,10 +763,10 @@ class StyledTextEditor(gtk.TextView):
|
|||||||
"""
|
"""
|
||||||
return self.toolbar
|
return self.toolbar
|
||||||
|
|
||||||
def undo(self):
|
def undo(self, obj=None):
|
||||||
self.textbuffer.undo()
|
self.textbuffer.undo()
|
||||||
|
|
||||||
def redo(self):
|
def redo(self, obj=None):
|
||||||
self.textbuffer.redo()
|
self.textbuffer.redo()
|
||||||
|
|
||||||
def uri_dialog(self, uri, callback):
|
def uri_dialog(self, uri, callback):
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
# Copyright (C) 2009 Florian Heinle
|
# Copyright (C) 2009 Florian Heinle
|
||||||
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
|
||||||
|
# Copyright (C) 2010 Benny Malengier
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -31,12 +32,12 @@ gtk textbuffer with undo functionality
|
|||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
|
|
||||||
|
from gen.lib.styledtext import StyledText
|
||||||
|
|
||||||
class UndoableInsert(object):
|
class UndoableInsert(object):
|
||||||
"""something that has been inserted into our textbuffer"""
|
"""something that has been inserted into our textbuffer"""
|
||||||
def __init__(self, text_iter, text, length):
|
def __init__(self, text_iter, text, length, text_buffer):
|
||||||
self.offset = text_iter.get_offset()
|
self.offset = text_iter.get_offset()
|
||||||
# FIXME: GRAMPS change: force to use string rather than
|
|
||||||
# StyledText
|
|
||||||
self.text = str(text)
|
self.text = str(text)
|
||||||
self.length = length
|
self.length = length
|
||||||
if self.length > 1 or self.text in ("\r", "\n", " "):
|
if self.length > 1 or self.text in ("\r", "\n", " "):
|
||||||
@ -44,11 +45,15 @@ class UndoableInsert(object):
|
|||||||
else:
|
else:
|
||||||
self.mergeable = True
|
self.mergeable = True
|
||||||
|
|
||||||
|
class UndoableInsertStyled(UndoableInsert):
|
||||||
|
"""something that has been inserted into our styledtextbuffer"""
|
||||||
|
def __init__(self, text_iter, text, length, text_buffer):
|
||||||
|
UndoableInsert.__init__(self, text_iter, text, length, text_buffer)
|
||||||
|
self.tags = text_buffer.get_text().get_tags()
|
||||||
|
|
||||||
class UndoableDelete(object):
|
class UndoableDelete(object):
|
||||||
"""something that has ben deleted from our textbuffer"""
|
"""something that has been deleted from our textbuffer"""
|
||||||
def __init__(self, text_buffer, start_iter, end_iter):
|
def __init__(self, text_buffer, start_iter, end_iter):
|
||||||
# FIXME: GRAMPS change: force to use string rather than
|
|
||||||
# StyledText
|
|
||||||
self.text = str(text_buffer.get_text(start_iter, end_iter))
|
self.text = str(text_buffer.get_text(start_iter, end_iter))
|
||||||
self.start = start_iter.get_offset()
|
self.start = start_iter.get_offset()
|
||||||
self.end = end_iter.get_offset()
|
self.end = end_iter.get_offset()
|
||||||
@ -64,11 +69,18 @@ class UndoableDelete(object):
|
|||||||
else:
|
else:
|
||||||
self.mergeable = True
|
self.mergeable = True
|
||||||
|
|
||||||
|
class UndoableDeleteStyled(UndoableDelete):
|
||||||
|
def __init__(self, text_buffer, start_iter, end_iter):
|
||||||
|
UndoableDelete.__init__(self, text_buffer, start_iter, end_iter)
|
||||||
|
self.tags = text_buffer.get_text().get_tags()
|
||||||
|
|
||||||
class UndoableBuffer(gtk.TextBuffer):
|
class UndoableBuffer(gtk.TextBuffer):
|
||||||
"""text buffer with added undo capabilities
|
"""text buffer with added undo capabilities
|
||||||
|
|
||||||
designed as a drop-in replacement for gtksourceview,
|
designed as a drop-in replacement for gtksourceview,
|
||||||
at least as far as undo is concerned"""
|
at least as far as undo is concerned"""
|
||||||
|
insertclass = UndoableInsert
|
||||||
|
deleteclass = UndoableDelete
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
@ -113,7 +125,7 @@ class UndoableBuffer(gtk.TextBuffer):
|
|||||||
self.redo_stack = []
|
self.redo_stack = []
|
||||||
if self.not_undoable_action:
|
if self.not_undoable_action:
|
||||||
return
|
return
|
||||||
undo_action = UndoableInsert(text_iter, text, length)
|
undo_action = self.insertclass(text_iter, text, length, textbuffer)
|
||||||
try:
|
try:
|
||||||
prev_insert = self.undo_stack.pop()
|
prev_insert = self.undo_stack.pop()
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@ -159,7 +171,7 @@ class UndoableBuffer(gtk.TextBuffer):
|
|||||||
self.redo_stack = []
|
self.redo_stack = []
|
||||||
if self.not_undoable_action:
|
if self.not_undoable_action:
|
||||||
return
|
return
|
||||||
undo_action = UndoableDelete(text_buffer, start_iter, end_iter)
|
undo_action = self.deleteclass(text_buffer, start_iter, end_iter)
|
||||||
try:
|
try:
|
||||||
prev_delete = self.undo_stack.pop()
|
prev_delete = self.undo_stack.pop()
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@ -219,15 +231,14 @@ class UndoableBuffer(gtk.TextBuffer):
|
|||||||
undo_action.offset + undo_action.length
|
undo_action.offset + undo_action.length
|
||||||
)
|
)
|
||||||
self.delete(start, stop)
|
self.delete(start, stop)
|
||||||
self.place_cursor(start)
|
self.place_cursor(self.get_iter_at_offset(undo_action.offset))
|
||||||
else:
|
else:
|
||||||
start = self.get_iter_at_offset(undo_action.start)
|
start = self.get_iter_at_offset(undo_action.start)
|
||||||
self.insert(start, undo_action.text)
|
self.insert(start, undo_action.text)
|
||||||
stop = self.get_iter_at_offset(undo_action.end)
|
|
||||||
if undo_action.delete_key_used:
|
if undo_action.delete_key_used:
|
||||||
self.place_cursor(start)
|
self.place_cursor(self.get_iter_at_offset(undo_action.start))
|
||||||
else:
|
else:
|
||||||
self.place_cursor(stop)
|
self.place_cursor(self.get_iter_at_offset(undo_action.end))
|
||||||
self.end_not_undoable_action()
|
self.end_not_undoable_action()
|
||||||
self.undo_in_progress = False
|
self.undo_in_progress = False
|
||||||
|
|
||||||
@ -252,6 +263,82 @@ class UndoableBuffer(gtk.TextBuffer):
|
|||||||
start = self.get_iter_at_offset(redo_action.start)
|
start = self.get_iter_at_offset(redo_action.start)
|
||||||
stop = self.get_iter_at_offset(redo_action.end)
|
stop = self.get_iter_at_offset(redo_action.end)
|
||||||
self.delete(start, stop)
|
self.delete(start, stop)
|
||||||
self.place_cursor(start)
|
self.place_cursor(self.get_iter_at_offset(redo_action.start))
|
||||||
|
self.end_not_undoable_action()
|
||||||
|
self.undo_in_progress = False
|
||||||
|
|
||||||
|
class UndoableBufferStyled(UndoableBuffer):
|
||||||
|
"""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 undo(self):
|
||||||
|
"""undo inserts or deletions
|
||||||
|
|
||||||
|
undone actions are being moved to redo stack"""
|
||||||
|
if not self.undo_stack:
|
||||||
|
return
|
||||||
|
self.begin_not_undoable_action()
|
||||||
|
self.undo_in_progress = True
|
||||||
|
undo_action = self.undo_stack.pop()
|
||||||
|
self.redo_stack.append(undo_action)
|
||||||
|
if isinstance(undo_action, UndoableInsert):
|
||||||
|
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()), undo_action.tags)
|
||||||
|
self.set_text(s_text)
|
||||||
|
self.place_cursor(self.get_iter_at_offset(undo_action.offset))
|
||||||
|
else:
|
||||||
|
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()), 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))
|
||||||
|
self.end_not_undoable_action()
|
||||||
|
self.undo_in_progress = False
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
"""redo inserts or deletions
|
||||||
|
|
||||||
|
redone actions are moved to undo stack"""
|
||||||
|
if not self.redo_stack:
|
||||||
|
return
|
||||||
|
self.begin_not_undoable_action()
|
||||||
|
self.undo_in_progress = True
|
||||||
|
redo_action = self.redo_stack.pop()
|
||||||
|
self.undo_stack.append(redo_action)
|
||||||
|
if isinstance(redo_action, UndoableInsert):
|
||||||
|
start = self.get_iter_at_offset(redo_action.offset)
|
||||||
|
self.insert(start, redo_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()), redo_action.tags)
|
||||||
|
self.set_text(s_text)
|
||||||
|
new_cursor_pos = self.get_iter_at_offset(
|
||||||
|
redo_action.offset + redo_action.length
|
||||||
|
)
|
||||||
|
self.place_cursor(new_cursor_pos)
|
||||||
|
else:
|
||||||
|
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()), redo_action.tags)
|
||||||
|
self.set_text(s_text)
|
||||||
|
self.place_cursor(self.get_iter_at_offset(redo_action.start))
|
||||||
self.end_not_undoable_action()
|
self.end_not_undoable_action()
|
||||||
self.undo_in_progress = False
|
self.undo_in_progress = False
|
Reference in New Issue
Block a user