Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/tutorius/actions.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/tutorius/actions.py')
-rw-r--r--src/tutorius/actions.py178
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()
+