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.drawing.utils import Metrics
22 from timelinelib.db.objects import TimePeriod
23
24
25 FORWARD = 1
26 BACKWARD = -1
27
29
30 - def __init__(self, size, db, view_properties, get_text_size_fn, config):
31 self._db = db
32 self._view_properties = view_properties
33 self._get_text_size = get_text_size_fn
34 self._config = config
35 self._outer_padding = 5
36 self._inner_padding = 3
37 self._baseline_padding = 15
38 self._period_threshold = 20
39 self._data_indicator_size = 10
40 self._metrics = Metrics(size, self._db.get_time_type(),
41 self._view_properties.displayed_period,
42 self._view_properties.divider_position)
43 self.width, self.height = size
44 self.divider_y = self._metrics.half_height
45 self.event_data = []
46 self.major_strip = None
47 self.minor_strip = None
48 self.major_strip_data = []
49 self.minor_strip_data = []
50
52 self._outer_padding = outer_padding
53
55 self._inner_padding = inner_padding
56
58 self._baseline_padding = baseline_padding
59
61 self._period_threshold = period_threshold
62
64 self._data_indicator_size = data_indicator_size
65
67 self._calc_event_positions()
68 self._calc_strips()
69
72
76
79
81 time1_x = self._metrics.calc_exact_x(time1)
82 time2_x = self._metrics.calc_exact_x(time2)
83 distance = abs(time1_x - time2_x)
84 return distance
85
88
90 rect = self._get_event_rect(selected_event)
91 period = self._event_rect_drawn_as_period(rect)
92 direction = self._get_direction(period, up)
93 evt = self._get_overlapping_event(period, direction, selected_event, rect)
94 return (evt, direction)
95
97 for (evt, rect) in self.event_data:
98 if evt == event:
99 return rect
100 return None
101
104
106 if up:
107 if period:
108 direction = BACKWARD
109 else:
110 direction = FORWARD
111 else:
112 if period:
113 direction = FORWARD
114 else:
115 direction = BACKWARD
116 return direction
117
119 list = self._get_overlapping_events_list(period, rect)
120 event = self._get_overlapping_event_from_list(list, direction,
121 selected_event)
122 return event
123
125 if period:
126 list = self._get_list_with_overlapping_period_events(rect)
127 else:
128 list = self._get_list_with_overlapping_point_events(rect)
129 return list
130
132 if direction == FORWARD:
133 return self._get_next_overlapping_event(list, selected_event)
134 else:
135 return self._get_prev_overlapping_event(list, selected_event)
136
138 selected_event_found = False
139 for (e,r) in list:
140 if selected_event_found:
141 return e
142 else:
143 if e == selected_event:
144 selected_event_found = True
145 return None
146
148 prev_event = None
149 for (e,r) in list:
150 if e == selected_event:
151 return prev_event
152 prev_event = e
153
155 self.events_from_db = self._db.get_events(self._view_properties.displayed_period)
156 visible_events = self._view_properties.filter_events(self.events_from_db)
157 visible_events = self._place_subevents_last(visible_events)
158 self._calc_rects(visible_events)
159
167
169 self.event_data = []
170 for event in events:
171 rect = self._create_rectangle_for_event(event)
172 self.event_data.append((event, rect))
173 for (event, rect) in self.event_data:
174 rect.Deflate(self._outer_padding, self._outer_padding)
175
177 if self._period_subevent(event):
178 return self._create_rectangle_for_period_subevent(event)
179 else:
180 return self._create_rectangle_for_possibly_overlapping_event(event)
181
184
186 return self._create_ideal_rect_for_event(event)
187
189 rect = self._create_ideal_rect_for_event(event)
190 self._ensure_rect_is_not_far_outisde_screen(rect)
191 self._prevent_overlapping_by_adjusting_rect_y(event, rect)
192 return rect
193
195 if event.ends_today:
196 event.time_period.end_time = self._db.get_time_type().now()
197 if self._display_as_period(event) or event.is_subevent():
198 if self._display_as_period(event):
199 return self._create_ideal_rect_for_period_event(event)
200 else:
201 return self._create_ideal_rect_for_non_period_event(event)
202 else:
203 return self._create_ideal_rect_for_non_period_event(event)
204
206 if event.is_container():
207 event_width = self._calc_min_subevent_threshold_width(event)
208 else:
209 event_width = self._metrics.calc_width(event.time_period)
210 return event_width > self._period_threshold
211
213 min_width = self._metrics.calc_width(container.time_period)
214 for event in container.events:
215 if event.is_period():
216 width = self._calc_subevent_threshold_width(event)
217 if width > 0 and width < min_width:
218 min_width = width
219 return min_width
220
222
223
224 enlarging_factor = 2
225 return enlarging_factor * self._metrics.calc_width(event.time_period)
226
228 tw, th = self._get_text_size(event.text)
229 ew = self._metrics.calc_width(event.time_period)
230 min_w = 5 * self._outer_padding
231 rw = max(ew + 2 * self._outer_padding, min_w)
232 rh = th + 2 * self._inner_padding + 2 * self._outer_padding
233 rx = (self._metrics.calc_x(event.time_period.start_time) -
234 self._outer_padding)
235 ry = self._get_ry(event)
236 rect = wx.Rect(rx, ry, rw, rh)
237 return rect
238
240 if event.is_subevent():
241 if event.is_period():
242 return self._get_container_ry(event)
243 else:
244 return self._metrics.half_height - self._baseline_padding
245 else:
246 return self._metrics.half_height + self._baseline_padding
247
249 for (event, rect) in self.event_data:
250 if event == subevent.container:
251 return rect.y
252 return self._metrics.half_height + self._baseline_padding
253
255 tw, th = self._get_text_size(event.text)
256 rw = tw + 2 * self._inner_padding + 2 * self._outer_padding
257 rh = th + 2 * self._inner_padding + 2 * self._outer_padding
258 if event.has_data():
259 rw += self._data_indicator_size / 3
260 if event.fuzzy or event.locked:
261 rw += th + 2 * self._inner_padding
262 rx = self._metrics.calc_x(event.mean_time()) - rw / 2
263 ry = self._metrics.half_height - rh - self._baseline_padding
264 rect = wx.Rect(rx, ry, rw, rh)
265 return rect
266
268
269
270
271 rx = rect.GetX()
272 rw = rect.GetWidth()
273 MARGIN = 50
274 if rx < -MARGIN:
275 distance_beyond_left_margin = -rx - MARGIN
276 rx += distance_beyond_left_margin
277 rw -= distance_beyond_left_margin
278 right_edge_x = rx + rw
279 if right_edge_x > self._metrics.width + MARGIN:
280 rw -= right_edge_x - self._metrics.width - MARGIN
281 rect.SetX(rx)
282 rect.SetWidth(rw)
283
285 """Fill the two arrays `minor_strip_data` and `major_strip_data`."""
286 def fill(list, strip):
287 """Fill the given list with the given strip."""
288 current_start = strip.start(self._view_properties.displayed_period.start_time)
289 try:
290 while current_start < self._view_properties.displayed_period.end_time:
291 next_start = strip.increment(current_start)
292 list.append(TimePeriod(self._db.get_time_type(), current_start, next_start))
293 current_start = next_start
294 except:
295
296 pass
297 self.major_strip_data = []
298 self.minor_strip_data = []
299 self.major_strip, self.minor_strip = self._db.get_time_type().choose_strip(self._metrics, self._config)
300 fill(self.major_strip_data, self.major_strip)
301 fill(self.minor_strip_data, self.minor_strip)
302
304 return len(self.events_from_db) - self._count_visible_events()
305
307 num_visible = 0
308 for (event, rect) in self.event_data:
309 if rect.Y < self.height and (rect.Y + rect.Height) > 0:
310 num_visible += 1
311 return num_visible
312
314 if self._display_as_period(event):
315 self._adjust_period_rect(event_rect)
316 else:
317 self._adjust_point_rect(event_rect)
318
323
325 list = self._get_list_with_overlapping_period_events(event_rect)
326 rect_with_largest_y = None
327 for (event, rect) in list:
328 if rect_with_largest_y is None or rect.Y > rect_with_largest_y.Y:
329 rect_with_largest_y = rect
330 return rect_with_largest_y
331
333 return [(event, rect) for (event, rect) in self.event_data
334 if (self._rects_overlap(event_rect, rect) and
335 rect.Y >= self.divider_y )]
336
341
343 list = self._get_list_with_overlapping_point_events(event_rect)
344 rect_with_smallest_y = None
345 for (event, rect) in list:
346 if rect_with_smallest_y is None or rect.Y < rect_with_smallest_y.Y:
347 rect_with_smallest_y = rect
348 return rect_with_smallest_y
349
351 return [(event, rect) for (event, rect) in self.event_data
352 if (self._rects_overlap(event_rect, rect) and
353 rect.Y < self.divider_y )]
354
356 return (rect2.x <= rect1.x + rect1.width and
357 rect1.x <= rect2.x + rect2.width)
358