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.data.exceptions import TimelineIOError
22 from timelinelib.canvas.drawing.utils import darken_color
23 from timelinelib.db.utils import safe_locking
24 from timelinelib import DEBUG_ENABLED
25 from timelinelib.monitoring import Monitoring
26 from timelinelib.general.observer import Observable
27 from timelinelib.repositories.categories import CategoriesFacade
28 from timelinelib.wxgui.components.font import Font
29 from timelinelib.wxgui.dialogs.editcategory.view import EditCategoryDialog
30 import timelinelib.wxgui.utils as gui_utils
31
32
34
35 - def __init__(self, parent, name=None, size=(100, 100)):
51
56
61
64
67
70
72 wx.BufferedPaintDC(self, self.buffer_image, wx.BUFFER_VIRTUAL_AREA)
73
76
78 def edit_function():
79 self._edit_category()
80 safe_locking(self.parent, edit_function)
81
90
92 def edit_function():
93 self._store_hit_info(event)
94 for (menu_item, should_be_enabled_fn) in self.context_menu_items:
95 menu_item.Enable(should_be_enabled_fn(self.last_hit_info))
96 self.PopupMenu(self.context_menu)
97 safe_locking(self.parent, edit_function)
98
100 self._edit_category()
101
104
109
113
117
121
125
129
133
137
141
145
150
152 (x, y) = self.CalcUnscrolledPosition(event.GetX(), event.GetY())
153 self.last_hit_info = self.model.hit(x, y)
154
161
163 width, height = self.GetVirtualSize()
164 self.buffer_image = wx.Bitmap(width, height)
165 memdc = wx.BufferedDC(None, self.buffer_image)
166 memdc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID))
167 memdc.Clear()
168 self.monitoring.timer_start()
169 self.renderer.render(memdc)
170 self.monitoring.timer_end()
171 if DEBUG_ENABLED:
172 (width, height) = self.GetSize()
173 redraw_time = self.monitoring.timer_elapsed_ms
174 self.monitoring.count_category_redraw()
175 memdc.SetTextForeground((255, 0, 0))
176 memdc.SetFont(Font(10, weight=wx.FONTWEIGHT_BOLD))
177 memdc.DrawText("Redraw count: %d" % self.monitoring._category_redraw_count, 10, height - 35)
178 memdc.DrawText("Last redraw time: %.3f ms" % redraw_time, 10, height - 20)
179 del memdc
180
184
186 def add_item(name, callback, should_be_enabled_fn):
187 item = wx.MenuItem(self.context_menu, wx.ID_ANY, name)
188 self.context_menu.Append(item)
189 self.Bind(wx.EVT_MENU, callback, item)
190 self.context_menu_items.append((item, should_be_enabled_fn))
191 return item
192 self.context_menu_items = []
193 self.context_menu = wx.Menu()
194 add_item(
195 _("Edit..."),
196 self._on_menu_edit,
197 lambda hit_info: hit_info.has_category())
198 add_item(
199 _("Add..."),
200 self._on_menu_add,
201 lambda hit_info: self._has_timeline_view())
202 add_item(
203 _("Delete"),
204 self._on_menu_delete,
205 lambda hit_info: hit_info.has_category())
206 self.context_menu.AppendSeparator()
207 add_item(
208 _("Check All"),
209 self._on_menu_check_all,
210 lambda hit_info: self._has_timeline_view())
211 add_item(
212 _("Check children"),
213 self._on_menu_check_children,
214 lambda hit_info: hit_info.has_category())
215 add_item(
216 _("Check all children"),
217 self._on_menu_check_all_children,
218 lambda hit_info: hit_info.has_category())
219 add_item(
220 _("Check all parents"),
221 self._on_menu_check_parents,
222 lambda hit_info: hit_info.has_category())
223 add_item(
224 _("Check parents for checked children"),
225 self._on_menu_check_parents_for_checked_children,
226 lambda hit_info: self._has_timeline_view())
227 self.context_menu.AppendSeparator()
228 add_item(
229 _("Uncheck All"),
230 self._on_menu_uncheck_all,
231 lambda hit_info: self._has_timeline_view())
232 add_item(
233 _("Uncheck children"),
234 self._on_menu_uncheck_children,
235 lambda hit_info: hit_info.has_category())
236 add_item(
237 _("Uncheck all children"),
238 self._on_menu_uncheck_all_children,
239 lambda hit_info: hit_info.has_category())
240 add_item(
241 _("Uncheck all parents"),
242 self._on_menu_uncheck_parents,
243 lambda hit_info: hit_info.has_category())
244
245
247
248 INNER_PADDING = 2
249 TRIANGLE_SIZE = 8
250
252 self.window = window
253 self.model = model
254
256 self.dc = dc
257 self._render_items(self.model.items)
258 del self.dc
259
261 self.dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT))
262 for item in items:
263 self._render_item(item)
264
266 if item["has_children"]:
267 self._render_arrow(item)
268 self._render_checkbox(item)
269 self._render_name(item)
270 self._render_color_box(item)
271
273 self.dc.SetBrush(wx.Brush(wx.Colour(100, 100, 100), wx.BRUSHSTYLE_SOLID))
274 self.dc.SetPen(wx.Pen(wx.Colour(100, 100, 100), 0, wx.PENSTYLE_SOLID))
275 offset = self.TRIANGLE_SIZE // 2
276 center_x = item["x"] + 2 * self.INNER_PADDING + offset
277 center_y = item["y"] + self.model.ITEM_HEIGHT_PX // 2 - 1
278 if item["expanded"]:
279 open_polygon = [
280 wx.Point(center_x - offset, center_y - offset),
281 wx.Point(center_x + offset, center_y - offset),
282 wx.Point(center_x, center_y + offset),
283 ]
284 self.dc.DrawPolygon(open_polygon)
285 else:
286 closed_polygon = [
287 wx.Point(center_x - offset, center_y - offset),
288 wx.Point(center_x - offset, center_y + offset),
289 wx.Point(center_x + offset, center_y),
290 ]
291 self.dc.DrawPolygon(closed_polygon)
292
303
305 (w, h) = (17, 17)
306 bouning_rect = wx.Rect(item["x"] + self.model.INDENT_PX,
307 item["y"] + (self.model.ITEM_HEIGHT_PX - h) // 2,
308 w,
309 h)
310 if item["visible"]:
311 flag = wx.CONTROL_CHECKED
312 else:
313 flag = 0
314 renderer = wx.RendererNative.Get()
315 renderer.DrawCheckBox(self.window, self.dc, bouning_rect, flag)
316
327
328
330
331 ITEM_HEIGHT_PX = 22
332 INDENT_PX = 15
333
341
344
346 self.view_width = view_width
347 self.view_height = view_height
348 self._update_items()
349
357
358 - def hit(self, x, y):
367
375
382
386
390
392 self.items = []
393 self.y = 0
394 self._update_from_tree(self._list_to_tree(self._get_categories()))
395 self._notify(None)
396
402
408
410 for (category, child_tree) in category_tree:
411 expanded = category.get_id() not in self.collapsed_category_ids
412 self.items.append({
413 "id": category.get_id(),
414 "name": category.get_name(),
415 "color": category.get_color(),
416 "visible": self._is_category_visible(category),
417 "x": indent_level * self.INDENT_PX,
418 "y": self.y,
419 "width": self.view_width - indent_level * self.INDENT_PX,
420 "expanded": expanded,
421 "has_children": len(child_tree) > 0,
422 "actually_visible": self._is_event_with_category_visible(category),
423 "category": category,
424 })
425 self.y += self.ITEM_HEIGHT_PX
426 if expanded:
427 self._update_from_tree(child_tree, indent_level + 1)
428
431
434
435
437
438 - def __init__(self, categories, category, is_on_arrow, is_on_checkbox):
443
445 return self._category is not None
446
448 return self._category
449
452
455
457 return self._categories.get_parents(self._category)
458
461
463 return self._is_on_arrow
464
466 return self._is_on_checkbox
467
468
473
474
479
480
482 delete_warning = _("Are you sure you want to "
483 "delete category '%s'?") % cat.name
484 if cat.parent is None:
485 update_warning = _("Events belonging to '%s' will no longer "
486 "belong to a category.") % cat.name
487 else:
488 update_warning = _("Events belonging to '%s' will now belong "
489 "to '%s'.") % (cat.name, cat.parent.name)
490 question = "%s\n\n%s" % (delete_warning, update_warning)
491 if gui_utils._ask_question(question, parent_ctrl) == wx.YES:
492 db.delete_category(cat)
493