1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import wx
20
21 from timelinelib.canvas.drawing.utils import Metrics
22 from timelinelib.canvas.data import TimePeriod
23
24
25 FORWARD = 1
26 BACKWARD = -1
27
28
30
31 - def __init__(self, size, db, view_properties, get_text_size_fn, appearance):
32 self._db = db
33 self._view_properties = view_properties
34 self._get_text_size_fn = get_text_size_fn
35 self._appearance = appearance
36 self._outer_padding = 5
37 self._inner_padding = 3
38 self._baseline_padding = 15
39 self._period_threshold = 20
40 self._data_indicator_size = 10
41 self._metrics = Metrics(size, self._db.get_time_type(),
42 self._view_properties.displayed_period,
43 self._view_properties.divider_position)
44 self.width, self.height = size
45 self.divider_y = self._metrics.half_height
46 self.event_data = []
47 self.major_strip = None
48 self.minor_strip = None
49 self.major_strip_data = []
50 self.minor_strip_data = []
51
53 self._outer_padding = outer_padding
54
56 self._inner_padding = inner_padding
57
59 self._baseline_padding = baseline_padding
60
62 self._period_threshold = period_threshold
63
65 self._data_indicator_size = data_indicator_size
66
68 """
69 Creating a scene means that pixel sizes and positions are calculated
70 for events and strips.
71 """
72 self.event_data = self._calc_event_sizes_and_positions()
73 self.minor_strip_data, self.major_strip_data = self._calc_strips_sizes_and_positions()
74
77
81
84
86 time1_x = self._metrics.calc_exact_x(time1)
87 time2_x = self._metrics.calc_exact_x(time2)
88 distance = abs(time1_x - time2_x)
89 return distance
90
93
95 self._inflate_event_rects_to_get_right_dimensions_for_overlap_calculations()
96 rect = self._get_event_rect(selected_event)
97
98
99
100
101
102
103
104 if rect is None:
105 return (None, 1)
106 else:
107 period = self._event_rect_drawn_as_period(rect)
108 direction = self._get_direction(period, up)
109 evt = self._get_overlapping_event(period, direction, selected_event, rect)
110 return (evt, direction)
111
112 - def center_text(self):
113 return self._appearance.get_center_event_texts()
114
116 for (_, rect) in self.event_data:
117 rect.Inflate(self._outer_padding, self._outer_padding)
118
120 for (evt, rect) in self.event_data:
121 if evt == event:
122 return rect
123 return None
124
127
140
142 event_data = self._get_overlapping_events_list(period, rect)
143 event = self._get_overlapping_event_from_list(event_data, direction,
144 selected_event)
145 return event
146
148 if period:
149 return self._get_list_with_overlapping_period_events(rect)
150 else:
151 return self._get_list_with_overlapping_point_events(rect)
152
154 if direction == FORWARD:
155 return self._get_next_overlapping_event(event_data, selected_event)
156 else:
157 return self._get_prev_overlapping_event(event_data, selected_event)
158
160 selected_event_found = False
161 for (e, _) in event_data:
162 if not selected_event.is_subevent() and e.is_subevent():
163 continue
164 if selected_event_found:
165 return e
166 else:
167 if e == selected_event:
168 selected_event_found = True
169 return None
170
172 prev_event = None
173 for (e, _) in event_data:
174 if not selected_event.is_subevent() and e.is_subevent():
175 continue
176 if e == selected_event:
177 return prev_event
178 prev_event = e
179
181 self.events_from_db = self._db.get_events(self._view_properties.displayed_period)
182 visible_events = self._view_properties.filter_events(self.events_from_db)
183 visible_events = self._place_subevents_after_container(visible_events)
184 return self._calc_event_rects(visible_events)
185
187 """
188 All subevents belonging to a container are placed directly after
189 the container event in the events list.
190 This is necessary because the position of the subevents are
191 dependent on the position of the container. So the container metrics
192 must be calculated first.
193 """
194 result = []
195 for event in events:
196 if event.is_container():
197 result.append(event)
198 result.extend(self._get_container_subevents(event, events))
199 elif not event.is_subevent():
200 result.append(event)
201 return result
202
209
211 self.event_data = self._calc_non_overlapping_event_rects(events)
212 self._deflate_rects(self.event_data)
213 return self.event_data
214
216 self.event_data = []
217 for event in events:
218 rect = self._create_ideal_rect_for_event(event)
219 self._prevent_overlapping_by_adjusting_rect_y(event, rect)
220 self.event_data.append((event, rect))
221 return self.event_data
222
224 for (_, rect) in event_data:
225 rect.Deflate(self._outer_padding, self._outer_padding)
226
235
239
242
245
247 rw, rh = self._calc_width_and_height_for_period_event(event)
248 rx = self._calc_x_pos_for_period_event(event)
249 ry = self._calc_y_pos_for_period_event(event)
250 return self._calc_ideal_wx_rect(rx, ry, rw, rh)
251
253 _, th = self._get_text_size(event.get_text())
254 ew = self._metrics.calc_width(event.get_time_period())
255 min_w = 5 * self._outer_padding
256 rw = max(ew + 2 * self._outer_padding, min_w)
257 rh = th + 2 * self._inner_padding + 2 * self._outer_padding
258 return rw, rh
259
262
264 if event.is_subevent():
265 if event.is_period():
266 return self._get_container_ry(event)
267 else:
268 return self._metrics.half_height - self._baseline_padding
269 else:
270 return self._metrics.half_height + self._baseline_padding
271
277
290
292 return self._calc_ideal_wx_rect(-1, -1, 0, 0)
293
295 tw, th = self._get_text_size(event.get_text())
296 rw = tw + 2 * self._inner_padding + 2 * self._outer_padding
297 rh = th + 2 * self._inner_padding + 2 * self._outer_padding
298 if event.has_data():
299 rw += self._data_indicator_size // 3
300 if event.get_fuzzy() or event.get_locked():
301 rw += th + 2 * self._inner_padding
302 return rw, rh
303
309
311 if event.is_milestone():
312 return self._metrics.half_height - rh // 2
313 else:
314 return self._metrics.half_height - rh - self._baseline_padding
315
316 - def _get_text_size(self, text):
317 if len(text) > 0:
318 return self._get_text_size_fn(text)
319 else:
320 return self._get_text_size_fn(" ")
321
323 return self._appearance.get_never_show_period_events_as_point_events()
324
326
327
328
329 MARGIN = 15
330 if rx < (-MARGIN):
331 move_distance = abs(rx) - MARGIN
332 rx += move_distance
333 rw -= move_distance
334 right_edge_x = rx + rw
335 if right_edge_x > self.width + MARGIN:
336 rw -= right_edge_x - self.width - MARGIN
337 return wx.Rect(rx, ry, rw, rh)
338
340 """Fill the two arrays `minor_strip_data` and `major_strip_data`."""
341
342 def fill(strip_list, strip):
343 """Fill the given list with the given strip."""
344 try:
345 current_start = strip.start(self._view_properties.displayed_period.start_time)
346 while current_start < self._view_properties.displayed_period.end_time:
347 next_start = strip.increment(current_start)
348 strip_list.append(TimePeriod(current_start, next_start))
349 current_start = next_start
350 except:
351
352 pass
353 major_strip_data = []
354 minor_strip_data = []
355 self.major_strip, self.minor_strip = self._db.get_time_type().choose_strip(self._metrics, self._appearance)
356 if hasattr(self.major_strip, 'set_skip_s_in_decade_text'):
357 self.major_strip.set_skip_s_in_decade_text(self._view_properties.get_skip_s_in_decade_text())
358 if hasattr(self.minor_strip, 'set_skip_s_in_decade_text'):
359 self.minor_strip.set_skip_s_in_decade_text(self._view_properties.get_skip_s_in_decade_text())
360 fill(major_strip_data, self.major_strip)
361 fill(minor_strip_data, self.minor_strip)
362 return (minor_strip_data, major_strip_data)
363
365 return self.minor_strip.is_day()
366
369
371 return len(self.events_from_db) - self._count_visible_events()
372
374 num_visible = 0
375 for (_, rect) in self.event_data:
376 if rect.Y < self.height and (rect.Y + rect.Height) > 0:
377 num_visible += 1
378 return num_visible
379
390
395
401
411
413 event_data = self._get_list_with_overlapping_subevents(subevent, event_rect)
414 rect_with_largest_y = None
415 for (_, rect) in event_data:
416 if rect_with_largest_y is None or rect.Y > rect_with_largest_y.Y:
417 rect_with_largest_y = rect
418 return rect_with_largest_y
419
421 event_data = self._get_list_with_overlapping_period_events(event_rect)
422 rect_with_largest_yh = None
423 for (_, rect) in event_data:
424 if rect_with_largest_yh is None or rect.Y + rect.Height > rect_with_largest_yh.Y + rect_with_largest_yh.Height:
425 rect_with_largest_yh = rect
426 return rect_with_largest_yh
427
432
440
445
447 event_data = self._get_list_with_overlapping_point_events(event_rect)
448 rect_with_smallest_y = None
449 for (_, rect) in event_data:
450 if rect_with_smallest_y is None or rect.Y < rect_with_smallest_y.Y:
451 rect_with_smallest_y = rect
452 return rect_with_smallest_y
453
458
460 REMOVE_X_PADDING = 2 + self._outer_padding * 2
461 return (rect2.x + REMOVE_X_PADDING <= rect1.x + rect1.width and
462 rect1.x + REMOVE_X_PADDING <= rect2.x + rect2.width)
463