root/gaphor/trunk/gaphor/diagram/diagramline.py

Revision 2187, 7.5 kB (checked in by arj..@yirdis.nl, 10 months ago)

do only update line's name if line has a subject.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 """
2 Basic functionality for canvas line based items on a diagram.
3 """
4
5 from math import atan2, pi
6
7 import gaphas
8 from gaphor import UML
9 from gaphas.util import text_extents, text_align
10 from diagramitem import DiagramItem
11 from interfaces import IConnect
12
13 from gaphor.diagram.style import get_text_point_at_line, \
14     get_text_point_at_line2, \
15     ALIGN_CENTER, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_TOP
16
17
18 class LineItem(gaphas.Line, DiagramItem):
19     """
20     Base class for diagram lines.
21     """
22
23     def __init__(self, id = None):
24         gaphas.Line.__init__(self)
25         DiagramItem.__init__(self, id)
26         self.fuzziness = 2
27
28     head = property(lambda self: self._handles[0])
29     tail = property(lambda self: self._handles[-1])
30
31
32     def setup_canvas(self):
33         gaphas.Line.setup_canvas(self)
34         self.register_handlers()
35
36
37     def teardown_canvas(self):
38         gaphas.Line.teardown_canvas(self)
39         self.unregister_handlers()
40
41
42     def pre_update(self, context):
43         # first, update stereotype to know its text
44         self.update_stereotype()
45
46         #super(LineItem, self).pre_update(context)
47         gaphas.Line.pre_update(self, context)
48         DiagramItem.pre_update(self, context)
49
50
51     def post_update(self, context):
52         #super(LineItem, self).update(context)
53         gaphas.Line.post_update(self, context)
54         DiagramItem.post_update(self, context)
55
56
57     def draw(self, context):
58         #super(LineItem, self).draw(context)
59         gaphas.Line.draw(self, context)
60         DiagramItem.draw(self, context)
61
62
63     def point(self, x, y):
64         d1 = gaphas.Line.point(self, x, y)
65         d2 = DiagramItem.point(self, x, y)
66         return min(d1, d2)
67
68
69 class DiagramLine(LineItem):
70     """
71     Gaphor lines. This line is serializable and has a popup
72     menu.
73
74     TODO: put serializability and popup in separate adapters.
75     """
76
77     def save (self, save_func):
78         LineItem.save(self, save_func)
79         save_func('matrix', tuple(self.matrix))
80         for prop in ('orthogonal', 'horizontal'):
81             save_func(prop, getattr(self, prop))
82         points = [ ]
83         for h in self.handles():
84             points.append(tuple(map(float, h.pos)))
85         save_func('points', points)
86         c = self.head.connected_to
87         if c:
88             save_func('head-connection', c, reference=True)
89         c = self.tail.connected_to
90         if c:
91             save_func('tail-connection', c, reference=True)
92
93     def load (self, name, value):
94         if name == 'matrix':
95             self.matrix = eval(value)
96         elif name == 'points':
97             points = eval(value)
98             for x in xrange(len(points) - 2):
99                 self.split_segment(0)
100             for i, p in enumerate(points):
101                 self.handles()[i].pos = p
102         elif name == 'orthogonal':
103             self._load_orthogonal = eval(value)
104         elif name in ('head_connection', 'head-connection'):
105             self._load_head_connection = value
106         elif name in ('tail_connection', 'tail-connection'):
107             self._load_tail_connection = value
108         else:
109             LineItem.load(self, name, value)
110
111     def postload(self):
112         # Ohoh, need the IConnect adapters here
113         from zope import component
114         if hasattr(self, '_load_orthogonal'):
115             self.orthogonal = self._load_orthogonal
116             del self._load_orthogonal
117
118         # First update matrix and solve constraints (NE and SW handle are
119         # lazy and are resolved by the constraint solver rather than set
120         # directly.
121         #self.canvas.update_matrix(self)
122         #self.canvas.solver.solve()
123
124         if hasattr(self, '_load_head_connection'):
125             adapter = component.queryMultiAdapter((self._load_head_connection, self), IConnect)
126             assert adapter, 'No IConnect adapter to connect %s to %s' % (self._load_head_connection, self)
127
128             adapter.connect(self.head)
129             del self._load_head_connection
130
131         if hasattr(self, '_load_tail_connection'):
132             adapter = component.queryMultiAdapter((self._load_tail_connection, self), IConnect)
133             assert adapter, 'No IConnect adapter to connect %s to %s' % (self._load_tail_connection, self)
134
135             adapter.connect(self.tail)
136             del self._load_tail_connection
137         LineItem.postload(self)
138
139
140     def _get_middle_segment(self):
141         """
142         Get middle line segment.
143         """
144         handles = self._handles
145         m = len(handles) / 2
146         assert m - 1 >= 0 and m < len(handles)
147         return handles[m - 1], handles[m]
148
149
150     def _get_center_pos(self, inverted=False):
151         """
152         Return position in the centre of middle segment of a line. Angle of
153         the middle segment is also returned.
154         """
155         h0, h1 = self._get_middle_segment()
156         pos = (h0.x + h1.x) / 2, (h0.y + h1.y) / 2
157         angle = atan2(h1.y - h0.y, h1.x - h0.x)
158         if inverted:
159             angle += pi
160         return pos, angle
161
162
163     def text_align(self, extents, align, padding, outside):
164         handles = self._handles
165         halign, valign = align
166
167         if halign == ALIGN_LEFT:
168             p1 = handles[0].pos
169             p2 = handles[-1].pos
170             x, y = get_text_point_at_line(extents, p1, p2, align, padding)
171
172         elif halign == ALIGN_CENTER:
173             h0, h1 = self._get_middle_segment()
174             p1 = h0.pos
175             p2 = h1.pos
176             x, y = get_text_point_at_line2(extents, p1, p2, align, padding)
177         elif halign == ALIGN_RIGHT:
178             p1 = handles[-1].pos
179             p2 = handles[-2].pos
180
181             x, y = get_text_point_at_line(extents, p1, p2, align, padding)
182
183         return x, y
184
185
186
187
188 class NamedLine(DiagramLine):
189
190     __style__ = {
191             'name-align': (ALIGN_CENTER, ALIGN_TOP),
192             'name-padding': (5, 5, 5, 5),
193             'name-outside': True,
194             'name-align-str': None,
195     }
196
197     def __init__(self, id=None):
198         DiagramLine.__init__(self, id)
199         self._name = self.add_text('name', style={
200                     'text-align': self.style.name_align,
201                     'text-padding': self.style.name_padding,
202                     'text-outside': self.style.name_outside,
203                     'text-align-str': self.style.name_align_str,
204                     'text-align-group': 'stereotype',
205                 }, editable=True)
206         self.add_watch(UML.NamedElement.name, self.on_named_element_name)
207
208
209     def on_named_element_name(self, event):
210         if self.subject and (event is None or self.subject is event.element):
211             self._name.text = self.subject.name
212             self.request_update()
213
214
215 class FreeLine(LineItem):
216     """
217     TODO: get rid of this one.
218
219     A line with disabled last handle. This allows to create diagram items,
220     which have one or more additional lines.
221
222     Last handle is usually attached to a point of diagram item. This point
223     is called main point.
224     """
225     def __init__(self, id = None, mpt = None):
226         LineItem.__init__(self, id)
227
228         # expose first handle
229         self._handle = self.handles[0]
230
231         # disable last handle, it should be in main point
232         h = self.handles[-1]
233         h.props.movable = False
234         h.props.connectable = False
235         h.props.visible = False
236
237         self._main_point = mpt
238
239         if self._main_point is None:
240             self._main_point = 0, 0
241
242
243     def on_update(self, affine):
244         self.handles[-1].set_pos_i(*self._main_point)
245         self._handle.set_pos_i(*self._handle.get_pos_i()) # really strange, but we have to do this
246
247         LineItem.on_update(self, affine)
248
249 # vim:sw=4:et
Note: See TracBrowser for help on using the browser.