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 period = self._event_rect_drawn_as_period(rect)
98 direction = self._get_direction(period, up)
99 evt = self._get_overlapping_event(period, direction, selected_event, rect)
100 return (evt, direction)
101
102 - def center_text(self):
103 return self._appearance.get_center_event_texts()
104
106 for (_, rect) in self.event_data:
107 rect.Inflate(self._outer_padding, self._outer_padding)
108
110 for (evt, rect) in self.event_data:
111 if evt == event:
112 return rect
113 return None
114
117
130
132 event_data = self._get_overlapping_events_list(period, rect)
133 event = self._get_overlapping_event_from_list(event_data, direction,
134 selected_event)
135 return event
136
138 if period:
139 return self._get_list_with_overlapping_period_events(rect)
140 else:
141 return self._get_list_with_overlapping_point_events(rect)
142
144 if direction == FORWARD:
145 return self._get_next_overlapping_event(event_data, selected_event)
146 else:
147 return self._get_prev_overlapping_event(event_data, selected_event)
148
150 selected_event_found = False
151 for (e, _) in event_data:
152 if not selected_event.is_subevent() and e.is_subevent():
153 continue
154 if selected_event_found:
155 return e
156 else:
157 if e == selected_event:
158 selected_event_found = True
159 return None
160
162 prev_event = None
163 for (e, _) in event_data:
164 if not selected_event.is_subevent() and e.is_subevent():
165 continue
166 if e == selected_event:
167 return prev_event
168 prev_event = e
169
171 self.events_from_db = self._db.get_events(self._view_properties.displayed_period)
172 visible_events = self._view_properties.filter_events(self.events_from_db)
173 visible_events = self._place_subevents_after_container(visible_events)
174 return self._calc_event_rects(visible_events)
175
177 """
178 All subevents belonging to a container are placed directly after
179 the container event in the events list.
180 This is necessary because the position of the subevents are
181 dependent on the position of the container. So the container metrics
182 must be calculated first.
183 """
184 result = []
185 for event in events:
186 if event.is_container():
187 result.append(event)
188 result.extend(self._get_container_subevents(event, events))
189 elif not event.is_subevent():
190 result.append(event)
191 return result
192
199
201 self.event_data = self._calc_non_overlapping_event_rects(events)
202 self._deflate_rects(self.event_data)
203 return self.event_data
204
206 self.event_data = []
207 for event in events:
208 rect = self._create_ideal_rect_for_event(event)
209 self._prevent_overlapping_by_adjusting_rect_y(event, rect)
210 self.event_data.append((event, rect))
211 return self.event_data
212
214 for (_, rect) in event_data:
215 rect.Deflate(self._outer_padding, self._outer_padding)
216
225
229
232
235
237 rw, rh = self._calc_width_and_height_for_period_event(event)
238 rx = self._calc_x_pos_for_period_event(event)
239 ry = self._calc_y_pos_for_period_event(event)
240 return self._calc_ideal_wx_rect(rx, ry, rw, rh)
241
243 _, th = self._get_text_size(event.get_text())
244 ew = self._metrics.calc_width(event.get_time_period())
245 min_w = 5 * self._outer_padding
246 rw = max(ew + 2 * self._outer_padding, min_w)
247 rh = th + 2 * self._inner_padding + 2 * self._outer_padding
248 return rw, rh
249
252
254 if event.is_subevent():
255 if event.is_period():
256 return self._get_container_ry(event)
257 else:
258 return self._metrics.half_height - self._baseline_padding
259 else:
260 return self._metrics.half_height + self._baseline_padding
261
267
280
282 return self._calc_ideal_wx_rect(-1, -1, 0, 0)
283
285 tw, th = self._get_text_size(event.get_text())
286 rw = tw + 2 * self._inner_padding + 2 * self._outer_padding
287 rh = th + 2 * self._inner_padding + 2 * self._outer_padding
288 if event.has_data():
289 rw += self._data_indicator_size / 3
290 if event.get_fuzzy() or event.get_locked():
291 rw += th + 2 * self._inner_padding
292 return rw, rh
293
299
301 if event.is_milestone():
302 return self._metrics.half_height - rh / 2
303 else:
304 return self._metrics.half_height - rh - self._baseline_padding
305
306 - def _get_text_size(self, text):
307 if len(text) > 0:
308 return self._get_text_size_fn(text)
309 else:
310 return self._get_text_size_fn(" ")
311
313 return self._appearance.get_never_show_period_events_as_point_events()
314
316
317
318
319 MARGIN = 15
320 if rx < (-MARGIN):
321 move_distance = abs(rx) - MARGIN
322 rx += move_distance
323 rw -= move_distance
324 right_edge_x = rx + rw
325 if right_edge_x > self.width + MARGIN:
326 rw -= right_edge_x - self.width - MARGIN
327 return wx.Rect(rx, ry, rw, rh)
328
330 """Fill the two arrays `minor_strip_data` and `major_strip_data`."""
331
332 def fill(strip_list, strip):
333 """Fill the given list with the given strip."""
334 try:
335 current_start = strip.start(self._view_properties.displayed_period.start_time)
336 while current_start < self._view_properties.displayed_period.end_time:
337 next_start = strip.increment(current_start)
338 strip_list.append(TimePeriod(current_start, next_start))
339 current_start = next_start
340 except:
341
342 pass
343 major_strip_data = []
344 minor_strip_data = []
345 self.major_strip, self.minor_strip = self._db.get_time_type().choose_strip(self._metrics, self._appearance)
346 if hasattr(self.major_strip, 'set_skip_s_in_decade_text'):
347 self.major_strip.set_skip_s_in_decade_text(self._view_properties.get_skip_s_in_decade_text())
348 if hasattr(self.minor_strip, 'set_skip_s_in_decade_text'):
349 self.minor_strip.set_skip_s_in_decade_text(self._view_properties.get_skip_s_in_decade_text())
350 fill(major_strip_data, self.major_strip)
351 fill(minor_strip_data, self.minor_strip)
352 return (minor_strip_data, major_strip_data)
353
355 return self.minor_strip.is_day()
356
359
361 return len(self.events_from_db) - self._count_visible_events()
362
364 num_visible = 0
365 for (_, rect) in self.event_data:
366 if rect.Y < self.height and (rect.Y + rect.Height) > 0:
367 num_visible += 1
368 return num_visible
369
380
385
391
401
403 event_data = self._get_list_with_overlapping_subevents(subevent, event_rect)
404 rect_with_largest_y = None
405 for (_, rect) in event_data:
406 if rect_with_largest_y is None or rect.Y > rect_with_largest_y.Y:
407 rect_with_largest_y = rect
408 return rect_with_largest_y
409
411 event_data = self._get_list_with_overlapping_period_events(event_rect)
412 rect_with_largest_yh = None
413 for (_, rect) in event_data:
414 if rect_with_largest_yh is None or rect.Y + rect.Height > rect_with_largest_yh.Y + rect_with_largest_yh.Height:
415 rect_with_largest_yh = rect
416 return rect_with_largest_yh
417
422
430
435
437 event_data = self._get_list_with_overlapping_point_events(event_rect)
438 rect_with_smallest_y = None
439 for (_, rect) in event_data:
440 if rect_with_smallest_y is None or rect.Y < rect_with_smallest_y.Y:
441 rect_with_smallest_y = rect
442 return rect_with_smallest_y
443
448
450 REMOVE_X_PADDING = 2 + self._outer_padding * 2
451 return (rect2.x + REMOVE_X_PADDING <= rect1.x + rect1.width and
452 rect1.x + REMOVE_X_PADDING <= rect2.x + rect2.width)
453