1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import os.path
20
21 import wx
22
23 from timelinelib.application import TimelineApplication
24 from timelinelib.config.dotfile import read_config
25 from timelinelib.config.paths import ICONS_DIR
26 from timelinelib.db.exceptions import TimelineIOError
27 from timelinelib.db import db_open
28 from timelinelib.db.objects import TimePeriod
29 from timelinelib.export.bitmap import export_to_image
30 from timelinelib.meta.about import APPLICATION_NAME
31 from timelinelib.meta.about import display_about_dialog
32 from timelinelib.meta.version import DEV
33 from timelinelib.utils import ex_msg
34 from timelinelib.wxgui.components.cattree import CategoriesTree
35 from timelinelib.wxgui.components.hyperlinkbutton import HyperlinkButton
36 from timelinelib.wxgui.components.search import SearchBar
37 from timelinelib.wxgui.components.timelineview import DrawingAreaPanel
38 from timelinelib.wxgui.dialogs.categorieseditor import CategoriesEditor
39 from timelinelib.wxgui.dialogs.duplicateevent import open_duplicate_event_dialog_for_event
40 from timelinelib.wxgui.dialogs.eventeditor import open_create_event_editor
41 from timelinelib.wxgui.dialogs.helpbrowser import HelpBrowser
42 from timelinelib.wxgui.dialogs.playframe import PlayFrame
43 from timelinelib.wxgui.dialogs.preferences import PreferencesDialog
44 from timelinelib.wxgui.dialogs.textdisplay import TextDisplayDialog
45 from timelinelib.wxgui.dialogs.timeeditor import TimeEditorDialog
46 from timelinelib.wxgui.utils import _ask_question
47 from timelinelib.wxgui.utils import _display_error_message
48 from timelinelib.wxgui.utils import WildcardHelper
49 import timelinelib.printing as printing
50 import timelinelib.wxgui.utils as gui_utils
51
52
53 -class MainFrame(wx.Frame):
54
55 - def __init__(self, application_arguments):
56 self.config = read_config(application_arguments.get_config_file_path())
57
58 wx.Frame.__init__(self, None, size=self.config.get_window_size(),
59 pos=self.config.get_window_pos(),
60 style=wx.DEFAULT_FRAME_STYLE, name="main_frame")
61
62
63 self.locale = wx.Locale(wx.LANGUAGE_DEFAULT)
64
65 self.help_browser = HelpBrowser(self)
66 self.controller = TimelineApplication(self, db_open, self.config)
67 self.menu_controller = MenuController()
68
69 self._set_initial_values_to_member_variables()
70 self._create_print_data()
71 self._create_gui()
72
73 self.Maximize(self.config.get_window_maximized())
74 self.SetTitle(APPLICATION_NAME)
75 self.SetIcons(self._load_icon_bundle())
76
77 self.main_panel.show_welcome_panel()
78 self.enable_disable_menus()
79
80 self.controller.on_started(application_arguments)
81 self._create_and_start_timer()
82
84 return self.config.week_start == "monday"
85
87 self.timer = wx.Timer(self)
88 self.Bind(wx.EVT_TIMER, self._timer_tick, self.timer)
89 self.timer.Start(10000)
90 self.alert_dialog_open = False
91
93 self.timeline = None
94 self.timeline_wildcard_helper = WildcardHelper(
95 _("Timeline files"), ["timeline", "ics"])
96 self.images_svg_wildcard_helper = WildcardHelper(
97 _("SVG files"), ["svg"])
98
100 self.printData = wx.PrintData()
101 self.printData.SetPaperId(wx.PAPER_A4)
102 self.printData.SetPrintMode(wx.PRINT_MODE_PRINTER)
103 self.printData.SetOrientation(wx.LANDSCAPE)
104
105 - def _create_gui(self):
106 self._create_status_bar()
107 self._create_main_panel()
108 self._create_main_menu_bar()
109 self._bind_frame_events()
110
112 self.CreateStatusBar()
113 self.status_bar_adapter = StatusBarAdapter(self.GetStatusBar())
114
116 self.main_panel = MainPanel(self, self.config, self)
117
119 main_menu_bar = wx.MenuBar()
120 self._create_file_menu(main_menu_bar)
121 self._create_edit_menu(main_menu_bar)
122 self._create_view_menu(main_menu_bar)
123 self._create_timeline_menu(main_menu_bar)
124 self._create_navigate_menu(main_menu_bar)
125 self._create_help_menu(main_menu_bar)
126 self.SetMenuBar(main_menu_bar)
127
128 - def _create_file_menu(self, main_menu_bar):
129 file_menu = wx.Menu()
130 self._create_file_new_menu(file_menu)
131 self._create_file_open_menu_item(file_menu)
132 self._create_file_open_recent_menu(file_menu)
133 file_menu.AppendSeparator()
134 self._create_file_page_setup_menu_item(file_menu)
135 self._create_file_print_preview_menu_item(file_menu)
136 self._create_file_print_menu_item(file_menu)
137 file_menu.AppendSeparator()
138 self._create_file_export_to_image_menu_item(file_menu)
139 self._create_file_export_to_svg_menu_item(file_menu)
140 file_menu.AppendSeparator()
141 if DEV:
142 self._create_file_play(file_menu)
143 file_menu.AppendSeparator()
144 self._create_file_exit_menu_item(file_menu)
145 main_menu_bar.Append(file_menu, _("&File"))
146
147 - def _create_file_new_menu(self, file_menu):
148 file_new_menu = wx.Menu()
149 self._create_file_new_timeline_menu_item(file_new_menu)
150 self._create_file_new_dir_timeline_menu_item(file_new_menu)
151 file_menu.AppendMenu(wx.ID_ANY, _("New"), file_new_menu, _("Create a new timeline"))
152
154 accel = wx.GetStockLabel(wx.ID_NEW, wx.STOCK_WITH_ACCELERATOR|wx.STOCK_WITH_MNEMONIC)
155 accel = accel.split("\t", 1)[1]
156 file_new_menu.Append(
157 wx.ID_NEW, _("File Timeline...") + "\t" + accel, _("File Timeline..."))
158 self.Bind(wx.EVT_MENU, self._mnu_file_new_on_click, id=wx.ID_NEW)
159
160 - def _mnu_file_new_on_click(self, event):
161 self._create_new_timeline()
162
164 wildcard = self.timeline_wildcard_helper.wildcard_string()
165 dialog = wx.FileDialog(self, message=_("Create Timeline"),
166 wildcard=wildcard, style=wx.FD_SAVE)
167 if dialog.ShowModal() == wx.ID_OK:
168 self._save_current_timeline_data()
169 path = self.timeline_wildcard_helper.get_path(dialog)
170 if os.path.exists(path):
171 msg_first_part = _("The specified timeline already exists.")
172 msg_second_part = _("Opening timeline instead of creating new.")
173 wx.MessageBox("%s\n\n%s" % (msg_first_part, msg_second_part),
174 _("Information"),
175 wx.OK|wx.ICON_INFORMATION, self)
176 self.open_timeline(path)
177 dialog.Destroy()
178
180 mnu_file_new_dir = file_new_menu.Append(
181 wx.ID_ANY, _("Directory Timeline..."), _("Directory Timeline..."))
182 self.Bind(wx.EVT_MENU, self._mnu_file_new_dir_on_click, mnu_file_new_dir)
183
185 self._create_new_dir_timeline()
186
188 dialog = wx.DirDialog(self, message=_("Create Timeline"))
189 if dialog.ShowModal() == wx.ID_OK:
190 self._save_current_timeline_data()
191 self.open_timeline(dialog.GetPath())
192 dialog.Destroy()
193
195 file_menu.Append(
196 wx.ID_OPEN, self._add_ellipses_to_menuitem(wx.ID_OPEN),
197 _("Open an existing timeline"))
198 self.Bind(wx.EVT_MENU, self._mnu_file_open_on_click, id=wx.ID_OPEN)
199
201 self._open_existing_timeline()
202
204 dir = ""
205 if self.timeline is not None:
206 dir = os.path.dirname(self.timeline.path)
207 wildcard = self.timeline_wildcard_helper.wildcard_string()
208 dialog = wx.FileDialog(self, message=_("Open Timeline"),
209 defaultDir=dir,
210 wildcard=wildcard, style=wx.FD_OPEN)
211 if dialog.ShowModal() == wx.ID_OK:
212 self._save_current_timeline_data()
213 self.open_timeline(dialog.GetPath())
214 dialog.Destroy()
215
217 self.mnu_file_open_recent_submenu = wx.Menu()
218 file_menu.AppendMenu(wx.ID_ANY, _("Open &Recent"), self.mnu_file_open_recent_submenu)
219 self._update_open_recent_submenu()
220
222 mnu_file_print_setup = file_menu.Append(
223 wx.ID_PRINT_SETUP, _("Page Set&up..."), _("Setup page for printing"))
224 self.menu_controller.add_menu_requiring_timeline(mnu_file_print_setup)
225 self.Bind(wx.EVT_MENU, self._mnu_file_print_setup_on_click, id=wx.ID_PRINT_SETUP)
226
229
231 mnu_file_print_preview = file_menu.Append(
232 wx.ID_PREVIEW, "", _("Print Preview"))
233 self.menu_controller.add_menu_requiring_timeline(mnu_file_print_preview)
234 self.Bind(wx.EVT_MENU, self._mnu_file_print_preview_on_click, id=wx.ID_PREVIEW)
235
238
240 mnu_file_print = file_menu.Append(
241 wx.ID_PRINT, self._add_ellipses_to_menuitem(wx.ID_PRINT), _("Print"))
242 self.menu_controller.add_menu_requiring_timeline(mnu_file_print)
243 self.Bind(wx.EVT_MENU, self._mnu_file_print_on_click, id=wx.ID_PRINT)
244
247
249 mnu_file_export = file_menu.Append(
250 wx.ID_ANY, _("&Export to Image..."), _("Export the current view to a PNG image"))
251 self.menu_controller.add_menu_requiring_timeline(mnu_file_export)
252 self.Bind(wx.EVT_MENU, self._mnu_file_export_on_click, mnu_file_export)
253
256
258 mnu_file_export_svg = file_menu.Append(
259 wx.ID_ANY, _("&Export to SVG..."), _("Export the current view to a SVG image"))
260 self.menu_controller.add_menu_requiring_timeline(mnu_file_export_svg)
261 self.Bind(wx.EVT_MENU, self._mnu_file_export_svg_on_click, mnu_file_export_svg)
262
264 self._export_to_svg_image()
265
267 if not self._has_pysvg_module():
268 _display_error_message(_("Could not find pysvg Python package. It is needed to export to SVG. See the Timeline website or the INSTALL file for instructions how to install it."), self)
269 return
270 import timelinelib.export.svg as svgexport
271 wildcard = self.images_svg_wildcard_helper.wildcard_string()
272 dialog = wx.FileDialog(self, message=_("Export to SVG"),
273 wildcard=wildcard, style=wx.FD_SAVE)
274 if dialog.ShowModal() == wx.ID_OK:
275 path = self.images_svg_wildcard_helper.get_path(dialog)
276 overwrite_question = _("File '%s' exists. Overwrite?") % path
277 if (not os.path.exists(path) or
278 _ask_question(overwrite_question, self) == wx.YES):
279 svgexport.export(
280 path,
281 self.main_panel.drawing_area.get_drawer().scene,
282 self.main_panel.drawing_area.get_view_properties())
283 dialog.Destroy()
284
286 try:
287 import pysvg
288 return True
289 except ImportError:
290 return False
291
292 - def _create_file_play(self, file_menu):
293 mnu_play = file_menu.Append(
294 wx.ID_ANY, _("Play timeline"), _("Play timeline as movie"))
295 self.menu_controller.add_menu_requiring_timeline(mnu_play)
296 self.Bind(wx.EVT_MENU, self._mnu_play_on_click, mnu_play)
297
298 - def _mnu_play_on_click(self, file_menu):
299 self.controller.on_play_clicked()
300
302 file_menu.Append(wx.ID_EXIT, "", _("Exit the program"))
303 self.Bind(wx.EVT_MENU, self._mnu_file_exit_on_click, id=wx.ID_EXIT)
304
307
308 - def _create_edit_menu(self, main_menu_bar):
309 edit_menu = wx.Menu()
310 self._create_edit_find_menu_item(edit_menu)
311 edit_menu.AppendSeparator()
312 self._create_edit_preferences_menu_item(edit_menu)
313 main_menu_bar.Append(edit_menu, _("&Edit"))
314
316 find_menu_item = edit_menu.Append(wx.ID_FIND)
317 self.Bind(wx.EVT_MENU, self._mnu_edit_find_on_click, find_menu_item)
318 self.menu_controller.add_menu_requiring_timeline(find_menu_item)
319
321 self.main_panel.show_searchbar(True)
322
324 preferences_item = edit_menu.Append(wx.ID_PREFERENCES)
325 self.Bind(wx.EVT_MENU, self._mnu_edit_preferences_on_click, preferences_item)
326
328 dialog = PreferencesDialog(self, self.config)
329 dialog.ShowModal()
330 dialog.Destroy()
331
332 - def _create_view_menu(self, main_menu_bar):
333 view_menu = wx.Menu()
334 self._create_view_sidebar_menu_item(view_menu)
335 self._create_view_legend_menu_item(view_menu)
336 view_menu.AppendSeparator()
337 self._create_view_balloons_menu_item(view_menu)
338 main_menu_bar.Append(view_menu, _("&View"))
339
341 view_sidebar_item = view_menu.Append(
342 wx.ID_ANY, _("&Sidebar\tCtrl+I"), kind=wx.ITEM_CHECK)
343 self.Bind(wx.EVT_MENU, self._mnu_view_sidebar_on_click, view_sidebar_item)
344 self.menu_controller.add_menu_requiring_visible_timeline_view(view_sidebar_item)
345 view_sidebar_item.Check(self.config.get_show_sidebar())
346
353
355 view_legend_item = view_menu.Append(
356 wx.ID_ANY, _("&Legend"), kind=wx.ITEM_CHECK)
357 self.Bind(wx.EVT_MENU, self._mnu_view_legend_on_click, view_legend_item)
358 self.menu_controller.add_menu_requiring_timeline(view_legend_item)
359 view_legend_item.Check(self.config.get_show_legend())
360
362 self.config.set_show_legend(evt.IsChecked())
363 self.main_panel.drawing_area.show_hide_legend(evt.IsChecked())
364
366 view_balloons_item = view_menu.Append(
367 wx.ID_ANY, _("&Balloons on hover"), kind=wx.ITEM_CHECK)
368 self.Bind(wx.EVT_MENU, self._mnu_view_balloons_on_click, view_balloons_item)
369 view_balloons_item.Check(self.config.get_balloon_on_hover())
370
372 self.config.set_balloon_on_hover(evt.IsChecked())
373 self.main_panel.drawing_area.balloon_visibility_changed(evt.IsChecked())
374
375 - def _create_timeline_menu(self, main_menu_bar):
376 timeline_menu = wx.Menu()
377 self._create_timeline_create_event_menu_item(timeline_menu)
378 self._create_timeline_duplicate_event_menu_item(timeline_menu)
379 self._create_timeline_measure_distance_between_events_menu_item(timeline_menu)
380 self._create_timeline_edit_categories(timeline_menu)
381 main_menu_bar.Append(timeline_menu, _("&Timeline"))
382
384 create_event_item = timeline_menu.Append(
385 wx.ID_ANY, _("Create &Event..."), _("Create a new event"))
386 self.Bind(wx.EVT_MENU, self._mnu_timeline_create_event_on_click, create_event_item)
387 self.menu_controller.add_menu_requiring_writable_timeline(create_event_item)
388
392
394 self.mnu_timeline_duplicate_event = timeline_menu.Append(
395 wx.ID_ANY, _("&Duplicate Selected Event..."), _("Duplicate the Selected Event"))
396 self.Bind(wx.EVT_MENU, self._mnu_timeline_duplicate_event_on_click,
397 self.mnu_timeline_duplicate_event)
398 self.menu_controller.add_menu_requiring_writable_timeline(self.mnu_timeline_duplicate_event)
399
401 try:
402 drawing_area = self.main_panel.drawing_area
403 id = drawing_area.get_view_properties().get_selected_event_ids()[0]
404 event = self.timeline.find_event_with_id(id)
405 except IndexError, e:
406
407 return
408 open_duplicate_event_dialog_for_event(
409 self,
410 self.timeline,
411 self.handle_db_error,
412 event)
413
415 self.mnu_timeline_measure_distance_between_events = timeline_menu.Append(
416 wx.ID_ANY, _("&Measure Distance between two Events..."),
417 _("Measure the Distance between two Events"))
418 self.Bind(wx.EVT_MENU, self._mnu_timeline_measure_distance_between_events_on_click,
419 self.mnu_timeline_measure_distance_between_events)
420 self.menu_controller.add_menu_requiring_writable_timeline(self.mnu_timeline_measure_distance_between_events)
421
423 self._measure_distance_between_events()
424
426 event1, event2 = self._get_selected_events()
427 distance = self._calc_events_distance(event1, event2)
428 self._display_distance(distance)
429
431 view_properties = self.main_panel.drawing_area.get_view_properties()
432 event_id_1 = view_properties.selected_event_ids[0]
433 event_id_2 = view_properties.selected_event_ids[1]
434 event1 = self.timeline.find_event_with_id(event_id_1)
435 event2 = self.timeline.find_event_with_id(event_id_2)
436 return event1, event2
437
438 - def _calc_events_distance(self,event1, event2):
439 if event1.time_period.start_time <= event2.time_period.start_time:
440 distance = (event2.time_period.start_time -
441 event1.time_period.end_time)
442 else:
443 distance = (event1.time_period.start_time -
444 event2.time_period.end_time)
445 return distance
446
447 - def _display_distance(self, distance):
448 header = _("Distance between selected events")
449 distance_text = self.timeline.get_time_type().format_delta(distance)
450 if distance_text == "0":
451 distance_text = _("Events are overlapping or distance is 0")
452 self._display_text(header, distance_text)
453
454 - def _display_text(self, header, text):
455 dialog = wx.MessageDialog(self, text, header,
456 wx.OK | wx.ICON_INFORMATION)
457 dialog.ShowModal()
458 dialog.Destroy()
459
461 edit_categories_item = timeline_menu.Append(
462 wx.ID_ANY, _("Edit &Categories"), _("Edit categories"))
463 self.Bind(wx.EVT_MENU, self._mnu_timeline_edit_categories_on_click,
464 edit_categories_item)
465 self.menu_controller.add_menu_requiring_writable_timeline(edit_categories_item)
466
469
470 - def edit_categories(self):
471 def create_categories_editor():
472 return CategoriesEditor(self, self.timeline)
473 gui_utils.show_modal(create_categories_editor, self.handle_db_error)
474
475 - def _create_navigate_menu(self, main_menu_bar):
476 navigate_menu = wx.Menu()
477 self._navigation_menu_items = []
478 self._navigation_functions_by_menu_item_id = {}
479 self._update_navigation_menu_items()
480 navigate_menu.AppendSeparator()
481 self._create_navigate_find_first_event_menu_item(navigate_menu)
482 self._create_navigate_find_last_event_menu_item(navigate_menu)
483 self._create_navigate_fit_all_events_menu_item(navigate_menu)
484 main_menu_bar.Append(navigate_menu, _("&Navigate"))
485 self.mnu_navigate = navigate_menu
486
488 find_first = navigate_menu.Append(wx.ID_ANY, _("Find First Event"))
489 self.Bind(wx.EVT_MENU, self._mnu_navigate_find_first_on_click, find_first)
490 self.menu_controller.add_menu_requiring_timeline(find_first)
491
493 event = self.timeline.get_first_event()
494 if event:
495 start = event.time_period.start_time
496 delta = self.main_panel.drawing_area.get_view_properties().displayed_period.delta()
497 end = start + delta
498 margin_delta = self.timeline.get_time_type().margin_delta(delta)
499 self._navigate_timeline(lambda tp: tp.update(start, end, -margin_delta))
500
502 find_last = navigate_menu.Append(wx.ID_ANY, _("Find Last Event"))
503 self.Bind(wx.EVT_MENU, self._mnu_navigate_find_last_on_click, find_last)
504 self.menu_controller.add_menu_requiring_timeline(find_last)
505
507 event = self.timeline.get_last_event()
508 if event:
509 end = event.time_period.end_time
510 delta = self.main_panel.drawing_area.get_view_properties().displayed_period.delta()
511 start = end - delta
512 margin_delta = self.timeline.get_time_type().margin_delta(delta)
513 self._navigate_timeline(lambda tp: tp.update(start, end, end_delta=margin_delta))
514
516 fit_all_events = navigate_menu.Append(wx.ID_ANY, _("Fit All Events"))
517 self.Bind(wx.EVT_MENU, self._mnu_navigate_fit_all_events_on_click, fit_all_events)
518 self.menu_controller.add_menu_requiring_timeline(fit_all_events)
519
521 self._fit_all_events()
522
523 - def _fit_all_events(self):
524 all_period = self._period_for_all_visible_events()
525 if all_period == None:
526 return
527 if all_period.is_period():
528 all_period = all_period.zoom(-1)
529 self._navigate_timeline(lambda tp: tp.update(all_period.start_time, all_period.end_time))
530 else:
531 self._navigate_timeline(lambda tp: tp.center(all_period.mean_time()))
532
534 try:
535 visible_events = self._all_visible_events()
536 if len(visible_events) > 0:
537 time_type = self.timeline.get_time_type()
538 start = self._first_time(visible_events)
539 end = self._last_time(visible_events)
540 return TimePeriod(time_type, start, end)
541 else:
542 return None
543 except ValueError, ex:
544 _display_error_message(ex.message)
545 return None
546
548 view_properties = self.main_panel.drawing_area.get_view_properties()
549 all_events = self.timeline.get_all_events()
550 visible_events = view_properties.filter_events(all_events)
551 return visible_events
552
553 - def _first_time(self, events):
554 start_time = lambda event: event.time_period.start_time
555 return start_time(min(events, key=start_time))
556
557 - def _last_time(self, events):
558 end_time = lambda event: event.time_period.end_time
559 return end_time(max(events, key=end_time))
560
561 - def _create_help_menu(self, main_menu_bar):
562 help_menu = wx.Menu()
563 self._create_help_contents_menu_item(help_menu)
564 help_menu.AppendSeparator()
565 self._create_help_tutorial_menu_item(help_menu)
566 self._create_help_contact_menu_item(help_menu)
567 help_menu.AppendSeparator()
568 self._create_help_about_menu_item(help_menu)
569 main_menu_bar.Append(help_menu, _("&Help"))
570
572 contents_item = help_menu.Append(wx.ID_HELP, _("&Contents\tF1"))
573 self.Bind(wx.EVT_MENU, self._mnu_help_contents_on_click, contents_item)
574
576 self.help_browser.show_page("contents")
577
579 tutorial_item = help_menu.Append(wx.ID_ANY, _("Getting started tutorial"))
580 self.Bind(wx.EVT_MENU, self._mnu_help_tutorial_on_click, tutorial_item)
581
583 self.open_timeline(":tutorial:")
584
586 contact_item = help_menu.Append(wx.ID_ANY, _("Contact"))
587 self.Bind(wx.EVT_MENU, self._mnu_help_contact_on_click, contact_item)
588
591
593 about_item = help_menu.Append(wx.ID_ABOUT)
594 self.Bind(wx.EVT_MENU, self._mnu_help_about_on_click, about_item)
595
598
600 self.Bind(wx.EVT_CLOSE, self._window_on_close)
601
602 - def _window_on_close(self, event):
603 self._save_current_timeline_data()
604 self._save_application_config()
605 self.Destroy()
606
608 self.config.set_window_size(self.GetSize())
609 self.config.set_window_pos(self.GetPosition())
610 self.config.set_window_maximized(self.IsMaximized())
611 self.config.set_sidebar_width(self.main_panel.get_sidebar_width())
612 try:
613 self.config.write()
614 except IOError, ex:
615 friendly = _("Unable to write configuration file.")
616 msg = "%s\n\n%s" % (friendly, ex_msg(ex))
617 _display_error_message(msg, self)
618
619 - def open_timeline(self, input_file):
620 self.controller.open_timeline(input_file)
621 self._update_navigation_menu_items()
622
623 - def handle_db_error(self, error):
624 _display_error_message(ex_msg(error), self)
625 self._switch_to_error_view(error)
626
627 - def _switch_to_error_view(self, error):
628 self.controller.set_no_timeline()
629 self.main_panel.error_panel.populate(error)
630 self.main_panel.show_error_panel()
631 self.enable_disable_menus()
632
633 - def _display_timeline(self, timeline):
634 self.timeline = timeline
635 self.menu_controller.on_timeline_change(timeline)
636 if timeline == None:
637
638
639 self.main_panel.cattree.initialize_from_timeline_view(None)
640 self.main_panel.searchbar.set_view(None)
641 self.main_panel.drawing_area.set_timeline(self.timeline)
642 self.status_bar_adapter.set_read_only_text("")
643 if timeline == None:
644 self.main_panel.show_welcome_panel()
645 self.SetTitle(APPLICATION_NAME)
646 else:
647 self.main_panel.cattree.initialize_from_timeline_view(self.main_panel.drawing_area)
648 self.main_panel.searchbar.set_view(self.main_panel.drawing_area)
649 self.main_panel.show_timeline_panel()
650 self.SetTitle("%s (%s) - %s" % (
651 os.path.basename(self.timeline.path),
652 os.path.dirname(os.path.abspath(self.timeline.path)),
653 APPLICATION_NAME))
654 if timeline.is_read_only():
655 self.status_bar_adapter.set_read_only_text(_("read-only"))
656
658 plain = wx.GetStockLabel(id,
659 wx.STOCK_WITH_ACCELERATOR|wx.STOCK_WITH_MNEMONIC)
660
661 tab_index = plain.find("\t")
662 if tab_index != -1:
663 return plain[:tab_index] + "..." + plain[tab_index:]
664 return plain + "..."
665
667 self._clear_navigation_menu_items()
668 if self.timeline:
669 self._create_navigation_menu_items()
670
672 while self._navigation_menu_items:
673 self.mnu_navigate.RemoveItem(self._navigation_menu_items.pop())
674 self._navigation_functions_by_menu_item_id.clear()
675
677 item_data = self.timeline.get_time_type().get_navigation_functions()
678 pos = 0
679 for (itemstr, fn) in item_data:
680 if itemstr == "SEP":
681 item = self.mnu_navigate.InsertSeparator(pos)
682 else:
683 item = self.mnu_navigate.Insert(pos, wx.ID_ANY, itemstr)
684 self._navigation_functions_by_menu_item_id[item.GetId()] = fn
685 self.Bind(wx.EVT_MENU, self._navigation_menu_item_on_click, item)
686 self._navigation_menu_items.append(item)
687 pos += 1
688
690 fn = self._navigation_functions_by_menu_item_id[evt.GetId()]
691 fn(self, self._get_time_period(), self._navigate_timeline)
692
694
695 for item in self.mnu_file_open_recent_submenu.GetMenuItems():
696 self.mnu_file_open_recent_submenu.DeleteItem(item)
697
698 self.open_recent_map = {}
699 for path in self.config.get_recently_opened():
700 name = "%s (%s)" % (
701 os.path.basename(path),
702 os.path.dirname(os.path.abspath(path)))
703 item = self.mnu_file_open_recent_submenu.Append(wx.ID_ANY, name)
704 self.open_recent_map[item.GetId()] = path
705 self.Bind(wx.EVT_MENU, self._mnu_file_open_recent_item_on_click,
706 item)
707
709 path = self.open_recent_map[event.GetId()]
710 self.open_timeline_if_exists(path)
711
713 if os.path.exists(path):
714 self.open_timeline(path)
715 else:
716 _display_error_message(_("File '%s' does not exist.") % path, self)
717
719 self.menu_controller.enable_disable_menus(self.main_panel.timeline_panel_visible())
720 self._enable_disable_duplicate_event_menu()
721 self._enable_disable_measure_distance_between_two_events_menu()
722 self._enable_disable_searchbar()
723
725 view_properties = self.main_panel.drawing_area.get_view_properties()
726 one_event_selected = len(view_properties.selected_event_ids) == 1
727 self.mnu_timeline_duplicate_event.Enable(one_event_selected)
728
730 view_properties = self.main_panel.drawing_area.get_view_properties()
731 two_events_selected = len(view_properties.selected_event_ids) == 2
732 self.mnu_timeline_measure_distance_between_events.Enable(two_events_selected)
733
735 if self.timeline == None:
736 self.main_panel.show_searchbar(False)
737
739 if self.timeline:
740 try:
741 self.timeline.save_view_properties(self.main_panel.drawing_area.get_view_properties())
742 except TimelineIOError, e:
743 self.handle_db_error(e)
744
746 bundle = wx.IconBundle()
747 for size in ["16", "32", "48"]:
748 iconpath = os.path.join(ICONS_DIR, "%s.png" % size)
749 icon = wx.IconFromBitmap(wx.BitmapFromImage(wx.Image(iconpath)))
750 bundle.AddIcon(icon)
751 return bundle
752
753 - def _navigate_timeline(self, navigation_fn):
754 return self.main_panel.drawing_area.navigate_timeline(navigation_fn)
755
757 return self.main_panel.drawing_area.get_time_period()
758
759 - def display_time_editor_dialog(self, time_type, initial_time,
760 handle_new_time_fn, title):
761 dialog = TimeEditorDialog(self, self.config, time_type, initial_time, title)
762 if dialog.ShowModal() == wx.ID_OK:
763 handle_new_time_fn(dialog.time)
764 dialog.Destroy()
765
766 - def open_play_frame(self, timeline):
767 dialog = PlayFrame(timeline, self.config)
768 dialog.ShowModal()
769 dialog.Destroy()
770
771 - def _timer_tick(self, evt):
772 self._handle_event_alerts()
773
775 if self.timeline is None:
776 return
777 if self.alert_dialog_open:
778 return
779 self._display_events_alerts()
780 self.alert_dialog_open = False
781
783 self.alert_dialog_open = True
784 all_events = self.timeline.get_all_events()
785 AlertController().display_events_alerts(all_events, self.timeline.get_time_type())
786
787
789
791 self.time_type = time_type
792 for event in all_events:
793 alert = event.get_data("alert")
794 if alert is not None:
795 if self._time_has_expired(alert[0]):
796 self._display_and_delete_event_alert(event, alert)
797
801
802 - def _alert_time_as_text(self, alert):
803 return "%s" % alert[0]
804
806 return time <= self.time_type.now()
807
808
814
816 text1 = "Trigger time: %s\n\n" % alert[0]
817 text2 = "Event: %s\n\n" % event.get_label()
818 text = "%s%s%s" % (text1, text2, alert[1])
819 return text
820
821
823
825 self.current_timeline = None
826 self.menus_requiring_timeline = []
827 self.menus_requiring_writable_timeline = []
828 self.menus_requiring_visible_timeline_view = []
829
832
834 self.menus_requiring_writable_timeline.append(menu)
835
837 self.menus_requiring_timeline.append(menu)
838
840 self.menus_requiring_visible_timeline_view.append(menu)
841
843 for menu in self.menus_requiring_writable_timeline:
844 self._enable_disable_menu_requiring_writable_timeline(menu)
845 for menu in self.menus_requiring_timeline:
846 self._enable_disable_menu_requiring_timeline(menu)
847 for menu in self.menus_requiring_visible_timeline_view:
848 self._enable_disable_menu_requiring_visible_timeline_view(menu, timeline_view_visible)
849
851 if self.current_timeline == None:
852 menu.Enable(False)
853 elif self.current_timeline.is_read_only():
854 menu.Enable(False)
855 else:
856 menu.Enable(True)
857
859 menu.Enable(self.current_timeline != None)
860
862 has_timeline = self.current_timeline != None
863 menu.Enable(has_timeline and timeline_view_visible)
864
865
866 -class MainPanel(wx.Panel):
867 """
868 Panel that covers the whole client area of MainFrame.
869
870 Displays one of the following panels:
871
872 * The welcome panel (show_welcome_panel)
873 * A splitter with sidebar and DrawingAreaPanel (show_timeline_panel)
874 * The error panel (show_error_panel)
875
876 Also displays the search bar.
877 """
878
879 - def __init__(self, parent, config, main_frame):
880 wx.Panel.__init__(self, parent)
881 self.config = config
882 self.main_frame = main_frame
883 self._create_gui()
884
885 self.cattree = self.timeline_panel.sidebar.cattree
886 self.drawing_area = self.timeline_panel.drawing_area
887 self.show_sidebar = self.timeline_panel.show_sidebar
888 self.hide_sidebar = self.timeline_panel.hide_sidebar
889 self.get_sidebar_width = self.timeline_panel.get_sidebar_width
890
892 return self.timeline_panel.IsShown()
893
895 self._show_panel(self.welcome_panel)
896
898 self._show_panel(self.timeline_panel)
899
901 self._show_panel(self.error_panel)
902
903 - def show_searchbar(self, show=True):
904 self.searchbar.Show(show)
905 if show == True:
906 self.searchbar.search.SetFocus()
907 self.GetSizer().Layout()
908
909 - def _create_gui(self):
910
911 def search_close():
912 self.show_searchbar(False)
913 self.searchbar = SearchBar(self, search_close)
914 self.searchbar.Show(False)
915
916 self.welcome_panel = WelcomePanel(self, self.main_frame)
917 self.timeline_panel = TimelinePanel(
918 self, self.config, self.main_frame.handle_db_error,
919 self.main_frame.status_bar_adapter, self.main_frame)
920 self.error_panel = ErrorPanel(self, self.main_frame)
921
922 self.sizerOuter = wx.BoxSizer(wx.VERTICAL)
923 self.sizer = wx.BoxSizer(wx.HORIZONTAL)
924 self.sizer.Add(self.welcome_panel, flag=wx.GROW, proportion=1)
925 self.sizer.Add(self.timeline_panel, flag=wx.GROW, proportion=1)
926 self.sizer.Add(self.error_panel, flag=wx.GROW, proportion=1)
927 self.sizerOuter.Add(self.sizer, flag=wx.GROW, proportion=1)
928 self.sizerOuter.Add(self.searchbar, flag=wx.GROW, proportion=0)
929 self.SetSizer(self.sizerOuter)
930
931 - def _show_panel(self, panel):
932
933 for panel_to_hide in [self.welcome_panel, self.timeline_panel,
934 self.error_panel]:
935 panel_to_hide.Show(False)
936
937 panel.Show(True)
938 self.sizerOuter.Layout()
939 panel.activated()
940
941
943
944 - def __init__(self, parent, main_frame):
945 wx.Panel.__init__(self, parent)
946 self.main_frame = main_frame
947 self._create_gui()
948
950 vsizer = wx.BoxSizer(wx.VERTICAL)
951
952 t1 = wx.StaticText(self, label=_("No timeline opened."))
953 vsizer.Add(t1, flag=wx.ALIGN_CENTER_HORIZONTAL)
954
955 vsizer.AddSpacer(20)
956
957 t2 = wx.StaticText(self, label=_("First time using Timeline?"))
958 vsizer.Add(t2, flag=wx.ALIGN_CENTER_HORIZONTAL)
959
960 btn_tutorial = HyperlinkButton(self, _("Getting started tutorial"))
961 self.Bind(wx.EVT_HYPERLINK, self._btn_tutorial_on_click, btn_tutorial)
962 vsizer.Add(btn_tutorial, flag=wx.ALIGN_CENTER_HORIZONTAL)
963
964 hsizer = wx.BoxSizer(wx.HORIZONTAL)
965 hsizer.Add(vsizer, flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, proportion=1)
966 self.SetSizer(hsizer)
967
970
973
974
976
977 - def __init__(self, parent, config, handle_db_error, status_bar_adapter,
978 main_frame):
986
988 self._create_divider_line_slider()
989 self._create_splitter()
990 self._layout_components()
991
993 self.divider_line_slider = wx.Slider(
994 self, value=50, size=(20, -1), style=wx.SL_LEFT|wx.SL_VERTICAL)
995
997 self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
998 self.splitter.SetMinimumPaneSize(50)
999 self.Bind(
1000 wx.EVT_SPLITTER_SASH_POS_CHANGED,
1001 self._splitter_on_splitter_sash_pos_changed, self.splitter)
1002 self._create_sidebar()
1003 self._create_drawing_area()
1004 self.splitter.Initialize(self.drawing_area)
1005
1007 if self.IsShown():
1008 self.sidebar_width = self.splitter.GetSashPosition()
1009
1012
1014 self.drawing_area = DrawingAreaPanel(
1015 self.splitter,
1016 self.status_bar_adapter,
1017 self.divider_line_slider,
1018 self.handle_db_error,
1019 self.config,
1020 self.main_frame)
1021
1023 sizer = wx.BoxSizer(wx.HORIZONTAL)
1024 sizer.Add(self.splitter, proportion=1, flag=wx.EXPAND)
1025 sizer.Add(self.divider_line_slider, proportion=0, flag=wx.EXPAND)
1026 self.SetSizer(sizer)
1027
1030
1035
1038
1042
1043
1045
1046 - def __init__(self, parent, main_frame):
1047 wx.Panel.__init__(self, parent)
1048 self.main_frame = main_frame
1049 self._create_gui()
1050
1052 self.txt_error.SetLabel(ex_msg(error))
1053
1055 vsizer = wx.BoxSizer(wx.VERTICAL)
1056
1057 self.txt_error = wx.StaticText(self, label="")
1058 vsizer.Add(self.txt_error, flag=wx.ALIGN_CENTER_HORIZONTAL)
1059
1060 vsizer.AddSpacer(20)
1061
1062 txt_help = wx.StaticText(self, label=_("Relevant help topics:"))
1063 vsizer.Add(txt_help, flag=wx.ALIGN_CENTER_HORIZONTAL)
1064
1065 btn_contact = HyperlinkButton(self, _("Contact"))
1066 self.Bind(wx.EVT_HYPERLINK, self._btn_contact_on_click, btn_contact)
1067 vsizer.Add(btn_contact, flag=wx.ALIGN_CENTER_HORIZONTAL)
1068
1069 hsizer = wx.BoxSizer(wx.HORIZONTAL)
1070 hsizer.Add(vsizer, flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, proportion=1)
1071 self.SetSizer(hsizer)
1072
1075
1078
1079
1097
1098
1117