root/gaphor/tags/gaphor-0.12.0/gaphor/diagram/diagramline.py

Revision 2013, 6.5 kB (checked in by wrobe..@pld-linux.org, 1 year ago)

- starting to introduce new algorithm for align text at center of a line

with support for top/bottom align

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