File: groupingtools.py

package info (click to toggle)
gaphor 0.13.0-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 3,692 kB
  • ctags: 2,971
  • sloc: python: 19,981; xml: 247; makefile: 54; sh: 40
file content (193 lines) | stat: -rw-r--r-- 6,373 bytes parent folder | download | duplicates (2)
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
from zope import component

import gtk

from gaphor.diagram.interfaces import IGroup
from gaphor.ui.diagramtools import PlacementTool
from gaphas.tool import ItemTool

# cursor to indicate grouping
IN_CURSOR = gtk.gdk.Cursor(gtk.gdk.DIAMOND_CROSS)

# cursor to indicate ungrouping
OUT_CURSOR = gtk.gdk.Cursor(gtk.gdk.SIZING)

class GroupPlacementTool(PlacementTool):
    """
    Try to group items when placing them on diagram.
    """

    def __init__(self, item_factory, after_handler=None, handle_index=-1):
        super(GroupPlacementTool, self).__init__(item_factory,
                after_handler,
                handle_index)

        self._parent = None


    def on_button_press(self, context, event):
        """
        If new item was placed onto diagram, then try to group it with
        parent using grouping adapter.
        """

        # first get parent
        self._parent = None
        if event.button == 1:
            context.ungrab()
            view = context.view
            self._parent = view.get_item_at_point(event.x, event.y)

        # now, place the new item
        placed = PlacementTool.on_button_press(self, context, event)

        # if there is a parent, then try to group item and parent
        if placed and self._parent:
            view = context.view
            item = view.focused_item
            
            adapter = component.queryMultiAdapter((self._parent, item), IGroup)
            if adapter and adapter.can_contain():
                adapter.group()

        return placed


    def on_motion_notify(self, context, event):
        """
        Change parent item to dropzone state if it can accept diagram item
        object to be created.
        """
        view = context.view

        if view.focused_item:
            view.unselect_item(view.focused_item)
            view.focused_item = None

        try:
            parent = view.get_item_at_point(event.x, event.y)
        except KeyError:
            # No bounding box yet.
            return

        if parent:
            adapter = component.queryMultiAdapter((parent, self._factory.item_class), IGroup)
            if adapter and adapter.pre_can_contain():
                view.dropzone_item = parent
                view.window.set_cursor(IN_CURSOR)
            else:
                view.dropzone_item = None
                view.window.set_cursor(None)
        else:
            view.dropzone_item = None
            view.window.set_cursor(None)


    def _create_item(self, context, x, y):
        """
        Create diagram item and place it within parent's boundaries.
        """
        # if no parent, then create with parent placement tool
        if not self._parent:
            return super(GroupPlacementTool, self)._create_item(context, x, y)

        item = self._factory(self._parent)
        
        view = context.view
        # get item position through parent world
        x, y = view.canvas.get_matrix_c2i(self._parent).transform_point(x, y)
        item.matrix.translate(x, y)

        view.dropzone_item = None
        view.window.set_cursor(None)

        return item


class GroupItemTool(ItemTool):
    """
    Group diagram item by dropping it on another item.

    Works only for one selected item, now.
    """

    def on_motion_notify(self, context, event):
        """
        Indicate possibility of grouping/ungrouping of selected item.
        """
        super(GroupItemTool, self).on_motion_notify(context, event)
        view = context.view

        if event.state & gtk.gdk.BUTTON_PRESS_MASK and len(view.selected_items) == 1:
            item = list(view.selected_items)[0]
            parent = view.canvas.get_parent(item)

            over = view.get_item_at_point(event.x, event.y, selected=False)
            assert over is not item

            if over is parent: # do nothing when item is over parent
                view.dropzone_item = None
                view.window.set_cursor(None)
                return

            if parent and not over:  # are we going to remove from parent?
                adapter = component.queryMultiAdapter((parent, item.__class__), IGroup)
                if adapter and adapter.pre_can_contain():
                    view.window.set_cursor(OUT_CURSOR)
                    view.dropzone_item = parent

            if over:       # are we going to add to parent?
                adapter = component.queryMultiAdapter((over, item.__class__), IGroup)
                if adapter and adapter.pre_can_contain():
                    view.dropzone_item = over
                    view.window.set_cursor(IN_CURSOR)


    def on_button_release(self, context, event):
        """
        Group item if it is dropped on parent's item. Ungroup item if it is
        moved out of its parent boundaries. Method also moves item from old
        parent to new one (regrouping).
        """
        super(GroupItemTool, self).on_button_release(context, event)
        view = context.view
        if event.button == 1 and len(view.selected_items) == 1:
            item = list(view.selected_items)[0]
            parent = view.canvas.get_parent(item)
            over = view.get_item_at_point(event.x, event.y, selected=False)
            assert over is not item

            if over is parent:
                return

            if parent: # remove from parent
                adapter = component.queryMultiAdapter((parent, item), IGroup)
                if adapter and adapter.can_contain():
                    adapter.ungroup()

                    canvas = view.canvas
                    canvas.reparent(item, None)

                    # reset item's position
                    px, py = canvas.get_matrix_c2i(parent).transform_point(0, 0)
                    item.matrix.translate(-px, -py)


            if over: # add to over (over becomes parent)
                adapter = component.queryMultiAdapter((over, item), IGroup)
                if adapter and adapter.can_contain():
                    adapter.group()

                    canvas = view.canvas
                    canvas.reparent(item, over)

                    # reset item's position
                    x, y = canvas.get_matrix_i2c(over).transform_point(0, 0)
                    item.matrix.translate(-x, -y)


        view.dropzone_item = None
        view.window.set_cursor(None)


# vim:sw=4:et:ai