Package Gnumed :: Package timelinelib :: Package canvas :: Package eventboxdrawers :: Module defaulteventboxdrawer
[frames] | no frames]

Source Code for Module Gnumed.timelinelib.canvas.eventboxdrawers.defaulteventboxdrawer

  1  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg 
  2  # 
  3  # This file is part of Timeline. 
  4  # 
  5  # Timeline is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # Timeline is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>. 
 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  # Space inside event box to text (pixels) 
 34  GRAY = (200, 200, 200) 
 35   
 36   
37 -class DefaultEventBoxDrawer(object):
38
39 - def draw(self, dc, scene, rect, event, view_properties):
40 self.scene = scene 41 self.view_properties = view_properties 42 selected = view_properties.is_selected(event) 43 self.center_text = scene.center_text() 44 if event.is_milestone(): 45 self._draw_milestone_event(dc, rect, scene, event, selected) 46 elif scene.never_show_period_events_as_point_events() and rect.y < scene.divider_y and event.is_period(): 47 self._draw_period_event_as_symbol_below_divider_line(dc, scene, event) 48 else: 49 self._draw_event_box(dc, rect, event, selected)
50
51 - def _draw_period_event_as_symbol_below_divider_line(self, dc, scene, event):
52 dc.DestroyClippingRegion() 53 x = scene.x_pos_for_time(event.mean_time()) 54 y0 = scene.divider_y 55 y1 = y0 + 10 56 dc.SetBrush(self._black_solid_brush()) 57 dc.SetPen(self._black_solid_pen(1)) 58 dc.DrawLine(x, y0, x, y1) 59 dc.DrawCircle(x, y1, 2)
60
61 - def _draw_event_box(self, dc, rect, event, selected):
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
72 - def _draw_background(self, dc, rect, event):
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
77 - def _get_pen(self, dc, event):
78 pen = self._get_thin_border_pen(event) 79 if self.view_properties.is_highlighted(event): 80 if self.view_properties.get_highlight_count(event) % 2 == 0: 81 dc.DestroyClippingRegion() 82 pen = self._get_thick_border_pen(event) 83 return pen
84
85 - def _draw_fuzzy_edges(self, dc, rect, event):
86 if event.get_fuzzy(): 87 self._draw_fuzzy_start(dc, rect, event) 88 if not event.get_ends_today(): 89 self._draw_fuzzy_end(dc, rect, event)
90
91 - def _draw_locked_edges(self, dc, rect, event):
92 if event.get_ends_today(): 93 self._draw_locked_end(dc, event, rect) 94 if event.get_locked(): 95 self._draw_locked_start(dc, event, rect) 96 self._draw_locked_end(dc, event, rect)
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
102 - def _draw_selection_handles(self, dc, event, rect, selected):
103 if not event.locked and selected: 104 self._draw_handles(dc, event, rect)
105
106 - def _get_thin_border_pen(self, event):
107 return self._get_border_pen(event)
108
109 - def _get_thick_border_pen(self, event):
110 return self._get_border_pen(event, thickness=8)
111
112 - def _get_border_pen(self, event, thickness=1):
113 return wx.Pen(self._get_border_color(event), thickness, wx.PENSTYLE_SOLID)
114
115 - def _get_balloon_indicator_brush(self, event):
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
121 - def _get_border_color(self, event):
122 return darken_color(self._get_event_color(event))
123
124 - def _get_event_color(self, event):
125 try: 126 return event.get_category().color 127 except: 128 return event.get_default_color()
129
130 - def _draw_fuzzy_start(self, dc, rect, event):
131 self._inflate_clipping_region(dc, rect) 132 dc.DrawBitmap(self._get_fuzzy_bitmap(), rect.x - 4, rect.y + 4, True)
133
134 - def _draw_fuzzy_end(self, dc, rect, event):
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
143 - def _erase_outzide_fuzzy_box(self, dc, p1, p2, p3):
144 dc.SetBrush(wx.WHITE_BRUSH) 145 dc.SetPen(wx.WHITE_PEN) 146 dc.DrawPolygon((p1, p2, p3))
147
148 - def _draw_fuzzy_border(self, dc, event, p1, p2, p3):
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
157 - def _draw_locked_start(self, dc, event, rect):
158 self._inflate_clipping_region(dc, rect) 159 dc.DrawBitmap(self._get_lock_bitmap(), rect.x - 7, rect.y + 3, True)
160
161 - def _draw_locked_end(self, dc, event, rect):
162 self._inflate_clipping_region(dc, rect) 163 dc.DrawBitmap(self._get_lock_bitmap(), rect.x + rect.width - 8, rect.y + 3, True)
164
165 - def _draw_progress_box(self, dc, rect, event):
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
171 - def _set_progress_color(self, dc, event):
172 progress_color = event.get_progress_color() 173 dc.SetBrush(wx.Brush(wx.Colour(progress_color[0], progress_color[1], progress_color[2])))
174
175 - def _get_progress_rect(self, event_rect, event):
176 HEIGHT_FACTOR = 0.35 177 h = event_rect.height * HEIGHT_FACTOR 178 y = event_rect.y + (event_rect.height - h) 179 rw, _ = self.scene._calc_width_and_height_for_period_event(event) 180 rx = self.scene._calc_x_pos_for_period_event(event) 181 w = rw * event.get_data("progress") / 100.0 182 return wx.Rect(int(rx), int(y), int(w), int(h))
183
184 - def _draw_balloon_indicator(self, dc, event, rect):
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 # Ensure that we can't draw content outside inner rectangle 201 if self._there_is_room_for_the_text(rect): 202 self._draw_the_text(dc, rect, event)
203
204 - def _there_is_room_for_the_text(self, rect):
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):
220 if event.get_progress() == 100 and self.view_properties.get_display_checkmark_on_events_done(): 221 return "\u2714" + event.get_text() 222 else: 223 return event.get_text()
224
225 - def _get_inner_rect(self, rect):
226 # Under windows with wx 3.0 the following lined doesn't work !!! 227 # return wx.Rect(*rect).Deflate(INNER_PADDING, INNER_PADDING) 228 # The x-coordinate is corrupted 229 r = wx.Rect(*rect) 230 r.Deflate(INNER_PADDING, INNER_PADDING) 231 return r
232
233 - def _set_clipping_rect(self, dc, rect):
234 dc.SetClippingRegion(self._get_inner_rect(rect))
235
236 - def _calc_x_pos(self, dc, rect, event):
237 inner_rect = self._get_inner_rect(rect) 238 text_x = inner_rect.X 239 text_x = self._adjust_x_for_edge_icons(event, rect, text_x) 240 text_x = self._adjust_x_for_centered_text(dc, event, inner_rect, text_x) 241 return text_x
242
243 - def _adjust_x_for_edge_icons(self, event, rect, text_x):
244 if self._event_has_edge_icons(event): 245 text_x += rect.Height // 2 246 return text_x
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
253 - def _event_has_edge_icons(self, event):
254 return event.get_fuzzy() or event.get_locked()
255
256 - def _calc_y_pos(self, rect):
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
269 - def _draw_handles(self, dc, event, rect):
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
286 - def _draw_all_handles(self, dc, rect, event):
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
325 - def _inflate_clipping_region(self, dc, rect):
326 copy = wx.Rect(*rect) 327 copy.Inflate(10, 0) 328 dc.DestroyClippingRegion() 329 dc.SetClippingRegion(copy)
330 333
334 - def _get_lock_bitmap(self):
335 return self._get_bitmap(self.view_properties.get_locked_icon())
336
337 - def _get_fuzzy_bitmap(self):
338 return self._get_bitmap(self.view_properties.get_fuzzy_icon())
339
340 - def _get_bitmap(self, name):
341 return wx.Bitmap(os.path.join(EVENT_ICONS_DIR, name))
342
343 - def _draw_milestone_event(self, dc, rect, scene, event, selected):
344 345 def create_handle_rect(): 346 HALF_EVENT_HEIGHT = rect.Height // 2 347 y = rect.Y + HALF_EVENT_HEIGHT - HALF_HANDLE_SIZE 348 x = rect.X - HALF_HANDLE_SIZE + 1 349 return wx.Rect(x, y, HANDLE_SIZE, HANDLE_SIZE)
350 351 def draw_shape(): 352 # draw_diamond_shape() 353 draw_rectangle_shape() 354 # draw_circle_shape() 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
406 - def _black_solid_pen(self, size):
407 return wx.Pen(wx.Colour(0, 0, 0), size, wx.PENSTYLE_SOLID)
408
409 - def _black_solid_brush(self):
410 return wx.Brush(wx.Colour(0, 0, 0), wx.BRUSHSTYLE_SOLID)
411