Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sugar-toolkit/src/sugar/tutorius/dragbox.py
blob: d84e4ee1402a7359ed1e48f9bf282b461f20a848 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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)