1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import os
20 import math
21
22 import wx
23
24 from timelinelib.canvas.drawing.utils import darken_color
25 from timelinelib.canvas.drawing.utils import get_colour
26 from timelinelib.config.paths import EVENT_ICONS_DIR
27 from timelinelib.features.experimental.experimentalfeatures import EXTENDED_CONTAINER_HEIGHT
28
29
30 HANDLE_SIZE = 4
31 HALF_HANDLE_SIZE = HANDLE_SIZE // 2
32 DATA_INDICATOR_SIZE = 10
33 INNER_PADDING = 3
34 GRAY = (200, 200, 200)
35
36
38
39 - def draw(self, dc, scene, rect, event, view_properties):
50
60
62 self._draw_background(dc, rect, event)
63 self._draw_fuzzy_edges(dc, rect, event)
64 self._draw_locked_edges(dc, rect, event)
65 self._draw_progress_box(dc, rect, event)
66 self._draw_text(dc, rect, event)
67 self._draw_contents_indicator(dc, event, rect)
68 self._draw_locked_edges(dc, rect, event)
69 self._draw_selection_handles(dc, event, rect, selected)
70 self._draw_hyperlink(dc, rect, event)
71
73 dc.SetBrush(wx.Brush(self._get_event_color(event), wx.BRUSHSTYLE_SOLID))
74 dc.SetPen(self._get_pen(dc, event))
75 dc.DrawRectangle(rect)
76
84
90
97
98 - def _draw_contents_indicator(self, dc, event, rect):
99 if event.has_balloon_data():
100 self._draw_balloon_indicator(dc, event, rect)
101
105
107 return self._get_border_pen(event)
108
110 return self._get_border_pen(event, thickness=8)
111
113 return wx.Pen(self._get_border_color(event), thickness, wx.PENSTYLE_SOLID)
114
116 base_color = self._get_event_color(event)
117 darker_color = darken_color(base_color, 0.6)
118 brush = wx.Brush(darker_color, wx.BRUSHSTYLE_SOLID)
119 return brush
120
123
129
131 self._inflate_clipping_region(dc, rect)
132 dc.DrawBitmap(self._get_fuzzy_bitmap(), rect.x - 4, rect.y + 4, True)
133
135 self._inflate_clipping_region(dc, rect)
136 dc.DrawBitmap(self._get_fuzzy_bitmap(), rect.x + rect.width - 8, rect.y + 4, True)
137
138 - def draw_fuzzy(self, dc, event, p1, p2, p3, p4, p5):
139 self._erase_outzide_fuzzy_box(dc, p1, p2, p3)
140 self._erase_outzide_fuzzy_box(dc, p3, p4, p5)
141 self._draw_fuzzy_border(dc, event, p2, p3, p5)
142
144 dc.SetBrush(wx.WHITE_BRUSH)
145 dc.SetPen(wx.WHITE_PEN)
146 dc.DrawPolygon((p1, p2, p3))
147
149 gc = wx.GraphicsContext.Create(dc)
150 path = gc.CreatePath()
151 path.MoveToPoint(p1.x, p1.y)
152 path.AddLineToPoint(p2.x, p2.y)
153 path.AddLineToPoint(p3.x, p3.y)
154 gc.SetPen(self._get_thin_border_pen(event))
155 gc.StrokePath(path)
156
158 self._inflate_clipping_region(dc, rect)
159 dc.DrawBitmap(self._get_lock_bitmap(), rect.x - 7, rect.y + 3, True)
160
162 self._inflate_clipping_region(dc, rect)
163 dc.DrawBitmap(self._get_lock_bitmap(), rect.x + rect.width - 8, rect.y + 3, True)
164
166 if event.get_data("progress"):
167 self._set_progress_color(dc, event)
168 progress_rect = self._get_progress_rect(rect, event)
169 dc.DrawRectangle(progress_rect)
170
174
183
185 """
186 The data contents indicator is a small triangle drawn in the upper
187 right corner of the event rectangle.
188 """
189 corner_x = rect.X + rect.Width
190 points = (
191 wx.Point(corner_x - DATA_INDICATOR_SIZE, rect.Y),
192 wx.Point(corner_x, rect.Y),
193 wx.Point(corner_x, rect.Y + DATA_INDICATOR_SIZE),
194 )
195 dc.SetBrush(self._get_balloon_indicator_brush(event))
196 dc.SetPen(wx.TRANSPARENT_PEN)
197 dc.DrawPolygon(points)
198
199 - def _draw_text(self, dc, rect, event):
200
201 if self._there_is_room_for_the_text(rect):
202 self._draw_the_text(dc, rect, event)
203
205 return self._get_inner_rect(rect).Width > 0
206
207 - def _draw_the_text(self, dc, rect, event):
208 self._set_text_foreground_color(dc, event)
209 if event.is_container() and EXTENDED_CONTAINER_HEIGHT.enabled():
210 EXTENDED_CONTAINER_HEIGHT.draw_container_text_top_adjusted(event.get_text(), dc, rect)
211 else:
212 self._draw_normal_text(dc, rect, event)
213 dc.DestroyClippingRegion()
214
215 - def _draw_normal_text(self, dc, rect, event):
216 self._set_clipping_rect(dc, rect)
217 dc.DrawText(self._get_text(event), self._calc_x_pos(dc, rect, event), self._calc_y_pos(rect))
218
219 - def _get_text(self, event):
224
232
234 dc.SetClippingRegion(self._get_inner_rect(rect))
235
242
247
248 - def _adjust_x_for_centered_text(self, dc, event, inner_rect, text_x):
249 if self.center_text:
250 text_x = self._center_text(dc, event, inner_rect, text_x)
251 return text_x
252
255
257 return self._get_inner_rect(rect).Y
258
259 - def _center_text(self, dc, event, inner_rect, text_x):
260 width, _ = dc.GetTextExtent(self._get_text(event))
261 return max(text_x, text_x + (inner_rect.width - width) // 2)
262
263 - def _set_text_foreground_color(self, dc, event):
268
270
271 def draw_frame_around_event():
272 small_rect = wx.Rect(*rect)
273 small_rect.Deflate(1, 1)
274 border_color = self._get_border_color(event)
275 border_color = darken_color(border_color)
276 pen = wx.Pen(border_color, 1, wx.PENSTYLE_SOLID)
277 dc.SetBrush(wx.TRANSPARENT_BRUSH)
278 dc.SetPen(pen)
279 dc.DrawRectangle(small_rect)
280
281 dc.SetClippingRegion(rect)
282 draw_frame_around_event()
283 self._draw_all_handles(dc, rect, event)
284 dc.DestroyClippingRegion()
285
287
288 def inflate_clipping_region():
289 big_rect = wx.Rect(*rect)
290 big_rect.Inflate(HANDLE_SIZE, HANDLE_SIZE)
291 dc.DestroyClippingRegion()
292 dc.SetClippingRegion(big_rect)
293
294 def set_pen_and_brush():
295 dc.SetBrush(wx.BLACK_BRUSH)
296 dc.SetPen(wx.BLACK_PEN)
297
298 def create_handle_rect():
299 HALF_EVENT_HEIGHT = rect.Height // 2
300 y = rect.Y + HALF_EVENT_HEIGHT - HALF_HANDLE_SIZE
301 x = rect.X - HALF_HANDLE_SIZE + 1
302 return wx.Rect(x, y, HANDLE_SIZE, HANDLE_SIZE)
303
304 def draw_rect(handle_rect, offset):
305 handle_rect.Offset(offset, 0)
306 dc.DrawRectangle(handle_rect)
307
308 def draw_handle_rects(handle_rect):
309 HALF_EVENT_WIDTH = rect.Width // 2
310 EVENT_WIDTH = rect.Width
311 draw_rect(handle_rect, 0)
312 draw_rect(handle_rect, EVENT_WIDTH - 2)
313 if not event.ends_today:
314 draw_rect(handle_rect, -HALF_EVENT_WIDTH)
315
316 inflate_clipping_region()
317 set_pen_and_brush()
318 handle_rect = create_handle_rect()
319 draw_handle_rects(handle_rect)
320
324
326 copy = wx.Rect(*rect)
327 copy.Inflate(10, 0)
328 dc.DestroyClippingRegion()
329 dc.SetClippingRegion(copy)
330
333
336
339
342
350
351 def draw_shape():
352
353 draw_rectangle_shape()
354
355
356 def draw_rectangle_shape():
357 dc.DestroyClippingRegion()
358 dc.SetPen(self._black_solid_pen(1))
359 if event.get_category() is None:
360 dc.SetBrush(wx.Brush(wx.Colour(*event.get_default_color()), wx.BRUSHSTYLE_SOLID))
361 else:
362 dc.SetBrush(wx.Brush(wx.Colour(*event.get_category().get_color()), wx.BRUSHSTYLE_SOLID))
363 dc.DrawRectangle(rect)
364
365 def draw_circle_shape():
366 half_size = rect.width // 2
367 dc.DestroyClippingRegion()
368 dc.SetPen(self._black_solid_pen(1))
369 dc.SetBrush(wx.Brush(wx.Colour(*event.get_default_color()), wx.BRUSHSTYLE_SOLID))
370 dc.DrawCircle(rect.x + half_size, rect.y + half_size, 2 * rect.width // 3)
371
372 def draw_diamond_shape():
373 SIZE = 2
374 x = rect.x
375 y = rect.y
376 half_size = rect.width // 2
377 points = (wx.Point(x - SIZE, y + half_size),
378 wx.Point(x + half_size, y - SIZE),
379 wx.Point(x + rect.width + SIZE, y + half_size),
380 wx.Point(x + half_size, y + rect.width + SIZE))
381 dc.DestroyClippingRegion()
382 dc.SetPen(self._black_solid_pen(1))
383 dc.SetBrush(wx.Brush(wx.Colour(*event.get_default_color()), wx.BUSHSTYLE_SOLID))
384 dc.DrawPolygon(points)
385
386 def draw_label():
387 x_offset = 6
388 y_offset = 2
389 try:
390 label = event.get_text()[0]
391 except IndexError:
392 label = " "
393 dc.DrawText(label, rect.x + x_offset, rect.y + y_offset)
394
395 def draw_move_handle():
396 dc.SetBrush(self._black_solid_brush())
397 handle_rect = create_handle_rect()
398 handle_rect.Offset(rect.Width // 2, 0)
399 dc.DrawRectangle(handle_rect)
400
401 draw_shape()
402 draw_label()
403 if selected:
404 draw_move_handle()
405
407 return wx.Pen(wx.Colour(0, 0, 0), size, wx.PENSTYLE_SOLID)
408
410 return wx.Brush(wx.Colour(0, 0, 0), wx.BRUSHSTYLE_SOLID)
411