# 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 the DragBox class, representing a gtk.Layout like container which supports a "children dragging mode". """ import gtk class DragBox(gtk.EventBox): """ A DragBox represents a gtk container whose child object can be arbitrarily placed, like for a gtk.Layout. However DragBox implements a child "dragging" mode which allows the user to freely move immediate children of the DragBox. This can be toggled by the dragMode property (default to False) Default behavior is to keep children at minimal size, as returned by a call to gtk.Widget.get_size(). """ def __init__(self): gtk.EventBox.__init__(self) self.set_above_child(False) self.connect("motion-notify-event", self._on_mouse_evt) self.connect("button-press-event", self._on_mouse_evt) self.connect("button-release-event", self._on_mouse_evt) self._drag_mode = False self._dragging = None # the object being dragged, or None self._last_mouse_pos = None # last known mouse position (win. relative) self._layout = gtk.Layout() self.add(self._layout) def attach(self, widget, xPos=0, yPos=0, size=None, resizable=False): """ Adds the child to the EventBox hierarchy. widget is expected to be a displayable gtk.Widget and will respond to the dragMode by being dragable. """ # TODO handle child and set size self._layout.put(child_widget=widget, x=xPos, y=yPos) def show(self): gtk.EventBox.show(self) self._layout.show() def hide(self): gtk.EventBox.hide(self) self._layout.hide() def _on_mouse_evt(self, widget, event, data=None): """ Callback registered on mouse events. _on_mouse_evt does the actual child selection and moving """ if not self._drag_mode: return x, y = event.get_coords() if self._dragging is not None and event.type is gtk.gdk.MOTION_NOTIFY: child = self._dragging.get_allocation() self._layout.move(self._dragging, int(child.x+x-self._last_mouse_pos[0]), int(child.y+y-self._last_mouse_pos[1])) self._last_mouse_pos = event.get_coords() # FIXME scale layout or constraint dragging to DragBox elif self._dragging is None and event.type is gtk.gdk.BUTTON_PRESS: # iterate in revese as last drawn child is over the others for child in reversed(self._layout.get_children()): rect = child.get_allocation() if x >= rect.x and x < rect.x+rect.width \ and y >= rect.y and y < rect.y+rect.height: self._dragging = child # Common interface paradigm is that selecting an item # goes to top of stack it so we do just that. self._layout.remove(child) self._layout.put(child, rect.x, rect.y) break self._last_mouse_pos = event.get_coords() # TODO add conditional restack child elif self._dragging is not None \ and event.type is gtk.gdk.BUTTON_RELEASE: self._dragging = None def _set_drag_mode(self, value): if (not self._drag_mode) and value: self.set_above_child(True) elif self._drag_mode and (not value): self.set_above_child(False) self._drag_mode = value def _get_drag_mode(self): return self._drag_mode dragMode = property(fset=_set_drag_mode, fget=_get_drag_mode, doc="Defines whether widgets in DragBox can be mouse dragged.") # TODO set child properties (list_child_properties)