diff options
Diffstat (limited to 'src/tutorius/actions.py')
-rw-r--r-- | src/tutorius/actions.py | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/tutorius/actions.py b/src/tutorius/actions.py new file mode 100644 index 0000000..bb15459 --- /dev/null +++ b/src/tutorius/actions.py @@ -0,0 +1,178 @@ +# Copyright (C) 2009, Tutorius.org +# +# 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 St, Fifth Floor, Boston, MA 02110-1301 USA +""" +This module defines Actions that can be done and undone on a state +""" +import gtk + +from gettext import gettext as _ + +from sugar.graphics import icon + +from . import addon +from .services import ObjectStore +from .properties import * + +class DragWrapper(object): + """Wrapper to allow gtk widgets to be dragged around""" + def __init__(self, widget, position, draggable=False): + """ + Creates a wrapper to allow gtk widgets to be mouse dragged, if the + parent container supports the move() method, like a gtk.Layout. + @param widget the widget to enhance with drag capability + @param position the widget's position. Will translate the widget if needed + @param draggable wether to enable the drag functionality now + """ + self._widget = widget + self._eventbox = None + self._drag_on = False # whether dragging is enabled + self._rel_pos = (0,0) # mouse pos relative to widget + self._handles = [] # event handlers + self._dragging = False # whether a drag is in progress + self.position = position # position of the widget + + self.draggable = draggable + + def _pressed_cb(self, widget, evt): + """Callback for start of drag event""" + self._eventbox.grab_add() + self._dragging = True + self._rel_pos = evt.get_coords() + + def _moved_cb(self, widget, evt): + """Callback for mouse drag events""" + if not self._dragging: + return + + # Focus on a widget before dragging another would + # create addititonal move event, making the widget jump unexpectedly. + # Solution found was to process those focus events before dragging. + if gtk.events_pending(): + return + + xrel, yrel = self._rel_pos + xparent, yparent = evt.get_coords() + xparent, yparent = widget.translate_coordinates(widget.parent, + xparent, yparent) + self.position = (xparent-xrel, yparent-yrel) + self._widget.parent.move(self._eventbox, *self.position) + self._widget.parent.move(self._widget, *self.position) + self._widget.parent.queue_draw() + + def _released_cb(self, *args): + """Callback for end of drag (mouse release).""" + self._eventbox.grab_remove() + self._dragging = False + + def _drag_end(self, *args): + """Callback for end of drag (stolen focus).""" + self._dragging = False + + def set_draggable(self, value): + """Setter for the draggable property""" + if bool(value) ^ bool(self._drag_on): + if value: + self._eventbox = gtk.EventBox() + self._eventbox.show() + self._eventbox.set_visible_window(False) + size = self._widget.size_request() + self._eventbox.set_size_request(*size) + self._widget.parent.put(self._eventbox, *self.position) + self._handles.append(self._eventbox.connect( + "button-press-event", self._pressed_cb)) + self._handles.append(self._eventbox.connect( + "button-release-event", self._released_cb)) + self._handles.append(self._eventbox.connect( + "motion-notify-event", self._moved_cb)) + self._handles.append(self._eventbox.connect( + "grab-broken-event", self._drag_end)) + else: + while len(self._handles): + handle = self._handles.pop() + self._eventbox.disconnect(handle) + self._eventbox.parent.remove(self._eventbox) + self._eventbox.destroy() + self._eventbox = None + self._drag_on = value + + def get_draggable(self): + """Getter for the draggable property""" + return self._drag_on + + draggable = property(fset=set_draggable, fget=get_draggable, \ + doc="Property to enable the draggable behaviour of the widget") + + def set_widget(self, widget): + """Setter for the widget property""" + if self._dragging or self._drag_on: + raise Exception("Can't change widget while dragging is enabled.") + + assert hasattr(widget, "parent"), "wrapped widget should have a parent" + parent = widget.parent + assert hasattr(parent, "move"), "container of widget need move method" + self._widget = widget + + def get_widget(self): + """Getter for the widget property""" + return self._widget + + widget = property(fset=set_widget, fget=get_widget) + +class Action(TPropContainer): + """Base class for Actions""" + def __init__(self): + TPropContainer.__init__(self) + self.position = (0,0) + self._drag = None + + def do(self, **kwargs): + """ + Perform the action + """ + raise NotImplementedError("Not implemented") + + def undo(self): + """ + Revert anything the action has changed + """ + pass #Should raise NotImplemented? + + def enter_editmode(self, **kwargs): + """ + Enters edit mode. The action should display itself in some way, + without affecting the currently running application. The default is + a small box with the action icon. + """ + meta = addon.get_addon_meta(type(self).__name__) + + actionicon = icon.Icon(icon_name=meta['icon'], + icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR) + # Eventbox create a visible window for the icon, so it clips correctly + self.__edit_img = gtk.EventBox() + self.__edit_img.set_visible_window(True) + self.__edit_img.add(actionicon) + + x, y = self.position + + ObjectStore().activity._overlayer.put(self.__edit_img, x, y) + self.__edit_img.show_all() + self._drag = DragWrapper(self.__edit_img, self.position, True) + + def exit_editmode(self, **kwargs): + x, y = self._drag.position + self.position = [int(x), int(y)] + self.__edit_img.destroy() + |