1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """
19 A base class for the mainframe window, responsible for creating the GUI.
20 """
21
22
23 import collections
24 import wx
25
26 from timelinelib.db.utils import safe_locking
27 from timelinelib.meta.about import display_about_dialog
28 from timelinelib.plugin.factory import EVENTBOX_DRAWER
29 from timelinelib.plugin.factory import EXPORTER
30 from timelinelib.plugin import factory
31 from timelinelib.proxies.drawingarea import DrawingAreaProxy
32 from timelinelib.wxgui.components.mainpanel import MainPanel
33 from timelinelib.wxgui.components.statusbaradapter import StatusBarAdapter
34 from timelinelib.wxgui.dialogs.duplicateevent.view import open_duplicate_event_dialog_for_event
35 from timelinelib.wxgui.dialogs.editevent.view import open_create_event_editor
36 from timelinelib.wxgui.dialogs.feedback.view import show_feedback_dialog
37 from timelinelib.wxgui.dialogs.filenew.view import FileNewDialog
38 from timelinelib.wxgui.dialogs.importevents.view import ImportEventsDialog
39 from timelinelib.wxgui.dialogs.milestone.view import open_milestone_editor_for
40 from timelinelib.wxgui.dialogs.preferences.view import PreferencesDialog
41 from timelinelib.wxgui.dialogs.shortcutseditor.view import ShortcutsEditorDialog
42 from timelinelib.wxgui.dialogs.systeminfo.view import show_system_info_dialog
43
44
45 NONE = 0
46 CHECKBOX = 1
47 CHECKED_RB = 2
48 UNCHECKED_RB = 3
49 ID_SIDEBAR = wx.NewId()
50 ID_LEGEND = wx.NewId()
51 ID_BALLOONS = wx.NewId()
52 ID_ZOOMIN = wx.NewId()
53 ID_ZOOMOUT = wx.NewId()
54 ID_VERT_ZOOMIN = wx.NewId()
55 ID_VERT_ZOOMOUT = wx.NewId()
56 ID_CREATE_EVENT = wx.NewId()
57 ID_CREATE_MILESTONE = wx.NewId()
58 ID_PT_EVENT_TO_RIGHT = wx.NewId()
59 ID_EDIT_EVENT = wx.NewId()
60 ID_DUPLICATE_EVENT = wx.NewId()
61 ID_SET_CATEGORY_ON_SELECTED = wx.NewId()
62 ID_MEASURE_DISTANCE = wx.NewId()
63 ID_COMPRESS = wx.NewId()
64 ID_SET_CATEGORY_ON_WITHOUT = wx.NewId()
65 ID_EDIT_CATEGORIES = wx.NewId()
66 ID_EDIT_ERAS = wx.NewId()
67 ID_SET_READONLY = wx.NewId()
68 ID_FIND_FIRST = wx.NewId()
69 ID_FIND_LAST = wx.NewId()
70 ID_FIT_ALL = wx.NewId()
71 ID_EDIT_SHORTCUTS = wx.NewId()
72 ID_TUTORIAL = wx.NewId()
73 ID_NUMTUTORIAL = wx.NewId()
74 ID_FEEDBACK = wx.NewId()
75 ID_CONTACT = wx.NewId()
76 ID_SYSTEM_INFO = wx.NewId()
77 ID_IMPORT = wx.NewId()
78 ID_EXPORT = wx.NewId()
79 ID_EXPORT_ALL = wx.NewId()
80 ID_EXPORT_SVG = wx.NewId()
81 ID_FIND_CATEGORIES = wx.NewId()
82 ID_SELECT_ALL = wx.NewId()
83 ID_RESTORE_TIME_PERIOD = wx.NewId()
84 ID_NEW = wx.ID_NEW
85 ID_FIND = wx.ID_FIND
86 ID_UNDO = wx.NewId()
87 ID_REDO = wx.NewId()
88 ID_PREFERENCES = wx.ID_PREFERENCES
89 ID_HELP = wx.ID_HELP
90 ID_ABOUT = wx.ID_ABOUT
91 ID_SAVEAS = wx.ID_SAVEAS
92 ID_EXIT = wx.ID_EXIT
93 ID_MOVE_EVENT_UP = wx.NewId()
94 ID_MOVE_EVENT_DOWN = wx.NewId()
95 ID_PRESENTATION = wx.NewId()
96 ID_HIDE_DONE = wx.NewId()
97 ID_NAVIGATE = wx.NewId() + 100
98
99
101
103 self.shortcut_items = {}
104 self._create_status_bar()
105 self._create_main_panel()
106 self._create_main_menu_bar()
107 self._bind_frame_events()
108
110 self.CreateStatusBar()
111 self.status_bar_adapter = StatusBarAdapter(self.GetStatusBar())
112
114 self.main_panel = MainPanel(self, self.config, self)
115
117 main_menu_bar = wx.MenuBar()
118 main_menu_bar.Append(self._create_file_menu(), _("&File"))
119 main_menu_bar.Append(self._create_edit_menu(), _("&Edit"))
120 main_menu_bar.Append(self._create_view_menu(), _("&View"))
121 main_menu_bar.Append(self._create_timeline_menu(), _("&Timeline"))
122 main_menu_bar.Append(self._create_navigate_menu(), _("&Navigate"))
123 main_menu_bar.Append(self._create_help_menu(), _("&Help"))
124 self._set_shortcuts()
125 self.SetMenuBar(main_menu_bar)
126 self.update_navigation_menu_items()
127 self.enable_disable_menus()
128
133
135 self.Bind(wx.EVT_CLOSE, self._window_on_close)
136
138 file_menu = wx.Menu()
139 self._create_file_new_menu_item(file_menu)
140 self._create_file_open_menu_item(file_menu)
141 self._create_file_open_recent_menu(file_menu)
142 file_menu.AppendSeparator()
143 self._create_file_save_as_menu(file_menu)
144 file_menu.AppendSeparator()
145 self._create_import_menu_item(file_menu)
146 file_menu.AppendSeparator()
147 self._create_export_menues(file_menu)
148 file_menu.AppendSeparator()
149 self._create_file_exit_menu_item(file_menu)
150 self._file_menu = file_menu
151 return file_menu
152
154
155 def create_click_handler(plugin, main_frame):
156 def event_handler(evt):
157 plugin.run(main_frame)
158 return event_handler
159
160 submenu = wx.Menu()
161 file_menu.Append(wx.ID_ANY, _("Export"), submenu)
162 for plugin in factory.get_plugins(EXPORTER):
163 mnu = submenu.Append(wx.ID_ANY, plugin.display_name(), plugin.display_name())
164 self.menu_controller.add_menu_requiring_timeline(mnu)
165 handler = create_click_handler(plugin, self)
166 self.Bind(wx.EVT_MENU, handler, mnu)
167 method = getattr(plugin, "wxid", None)
168 if callable(method):
169 self.shortcut_items[method()] = mnu
170
172 accel = wx.GetStockLabel(wx.ID_NEW, wx.STOCK_WITH_ACCELERATOR | wx.STOCK_WITH_MNEMONIC)
173 accel = accel.split("\t", 1)[1]
174 file_menu.Append(
175 wx.ID_NEW, _("New...") + "\t" + accel, _("Create a new timeline"))
176 self.shortcut_items[wx.ID_NEW] = file_menu.FindItemById(wx.ID_NEW)
177 self.Bind(wx.EVT_MENU, self._mnu_file_new_on_click, id=wx.ID_NEW)
178
180 file_menu.Append(
181 wx.ID_OPEN, self._add_ellipses_to_menuitem(wx.ID_OPEN),
182 _("Open an existing timeline"))
183 self.Bind(wx.EVT_MENU, self._mnu_file_open_on_click, id=wx.ID_OPEN)
184
186 self.mnu_file_open_recent_submenu = wx.Menu()
187 file_menu.Append(wx.ID_ANY, _("Open &Recent"), self.mnu_file_open_recent_submenu)
188 self.update_open_recent_submenu()
189
194
196 mnu_file_import = file_menu.Append(
197 ID_IMPORT, _("Import events..."), _("Import events..."))
198 self.shortcut_items[ID_IMPORT] = mnu_file_import
199 self.Bind(wx.EVT_MENU, self._mnu_file_import_on_click, mnu_file_import)
200 self.menu_controller.add_menu_requiring_writable_timeline(mnu_file_import)
201
203 file_menu.Append(wx.ID_EXIT, "", _("Exit the program"))
204 self.shortcut_items[wx.ID_EXIT] = file_menu.FindItemById(wx.ID_EXIT)
205 self.Bind(wx.EVT_MENU, self._mnu_file_exit_on_click, id=wx.ID_EXIT)
206
212
213 def find(evt):
214 self.main_panel.show_searchbar(True)
215
216 def find_categories(evt):
217 dialog = create_category_find_dialog()
218 dialog.ShowModal()
219 dialog.Destroy()
220
221 def select_all(evt):
222 self.controller.select_all()
223
224 def preferences(evt):
225 def edit_function():
226 dialog = PreferencesDialog(self, self.config)
227 dialog.ShowModal()
228 dialog.Destroy()
229 safe_locking(self, edit_function)
230
231 def edit_shortcuts(evt):
232
233 def edit_function():
234 dialog = ShortcutsEditorDialog(self, self.shortcut_controller)
235 dialog.ShowModal()
236 dialog.Destroy()
237 safe_locking(self, edit_function)
238 cbx = NONE
239 items_spec = ((wx.ID_FIND, find, None, cbx),
240 (ID_FIND_CATEGORIES, find_categories, _("Find Categories..."), cbx),
241 None,
242 (ID_SELECT_ALL, select_all, _("Select All Events"), cbx),
243 None,
244 (wx.ID_PREFERENCES, preferences, None, cbx),
245 (ID_EDIT_SHORTCUTS, edit_shortcuts, _("Shortcuts..."), cbx))
246 self._edit_menu = self._create_menu(items_spec)
247 self._add_edit_menu_items_to_controller(self._edit_menu)
248 return self._edit_menu
249
255
264
265 def legend(evt):
266 self.config.show_legend = evt.IsChecked()
267
268 def balloons(evt):
269 self.config.balloon_on_hover = evt.IsChecked()
270
271 def zoomin(evt):
272 DrawingAreaProxy(self).zoom_in()
273
274 def zoomout(evt):
275 DrawingAreaProxy(self).zoom_out()
276
277 def vert_zoomin(evt):
278 DrawingAreaProxy(self).VertZoomIn()
279
280 def vert_zoomout(evt):
281 DrawingAreaProxy(self).VertZoomOut()
282
283 def start_slide_show(evt):
284 canvas = self.main_panel.get_timeline_canvas()
285 self.controller.start_slide_show(canvas)
286
287 def hide_events_done(evt):
288 self.config.hide_events_done = evt.IsChecked()
289
290 items_spec = [self._create_view_toolbar_menu_item,
291 (ID_SIDEBAR, sidebar, _("&Sidebar") + "\tCtrl+I", CHECKBOX),
292 (ID_LEGEND, legend, _("&Legend"), CHECKBOX),
293 None,
294 (ID_BALLOONS, balloons, _("&Balloons on hover"), CHECKBOX),
295 None,
296 (ID_ZOOMIN, zoomin, _("Zoom &In") + "\tCtrl++", NONE),
297 (ID_ZOOMOUT, zoomout, _("Zoom &Out") + "\tCtrl+-", NONE),
298 (ID_VERT_ZOOMIN, vert_zoomin, _("Vertical Zoom &In") + "\tAlt++", NONE),
299 (ID_VERT_ZOOMOUT, vert_zoomout, _("Vertical Zoom &Out") + "\tAlt+-", NONE),
300 None,
301 self._create_view_point_event_alignment_menu,
302 None,
303 self._create_event_box_drawers_menu,
304 None,
305 (ID_PRESENTATION, start_slide_show, _("Start slide show") + "...", NONE),
306 None,
307 (ID_HIDE_DONE, hide_events_done, _("&Hide Events done"), CHECKBOX),
308 ]
309 self._view_menu = self._create_menu(items_spec)
310 self._check_view_menu_items(self._view_menu)
311 self._add_view_menu_items_to_controller(self._view_menu)
312 return self._view_menu
313
319
320 def check_item_corresponding_to_config():
321 item.Check(self.config.show_toolbar)
322
323 self.Bind(wx.EVT_MENU, on_click, item)
324 self.config.listen_for_any(check_item_corresponding_to_config)
325 check_item_corresponding_to_config()
326
333 return event_handler
334
335 items = []
336 for plugin in factory.get_plugins(EVENTBOX_DRAWER):
337 if (plugin.display_name() == self.config.get_selected_event_box_drawer()):
338 items.append((wx.ID_ANY, create_click_handler(plugin), plugin.display_name(), CHECKED_RB))
339 else:
340 items.append((wx.ID_ANY, create_click_handler(plugin), plugin.display_name(), UNCHECKED_RB))
341 sub_menu = self._create_menu(items)
342 view_menu.Append(wx.ID_ANY, _("Event appearance"), sub_menu)
343
345 sub_menu = wx.Menu()
346 left_item = sub_menu.Append(wx.ID_ANY, _("Left"), kind=wx.ITEM_RADIO)
347 center_item = sub_menu.Append(wx.ID_ANY, _("Center"), kind=wx.ITEM_RADIO)
348 view_menu.Append(wx.ID_ANY, _("Point event alignment"), sub_menu)
349
350 def on_first_tool_click(event):
351 self.config.draw_point_events_to_right = True
352
353 def on_second_tool_click(event):
354 self.config.draw_point_events_to_right = False
355
356 def check_item_corresponding_to_config():
357 if self.config.draw_point_events_to_right:
358 left_item.Check()
359 else:
360 center_item.Check()
361
362 self.Bind(wx.EVT_MENU, on_first_tool_click, left_item)
363 self.Bind(wx.EVT_MENU, on_second_tool_click, center_item)
364 self.config.listen_for_any(check_item_corresponding_to_config)
365 check_item_corresponding_to_config()
366
368
369 def item(item_id):
370 return view_menu.FindItemById(item_id)
371
372 item(ID_SIDEBAR).Check(self.config.show_sidebar)
373 item(ID_LEGEND).Check(self.config.show_legend)
374 item(ID_BALLOONS).Check(self.config.balloon_on_hover)
375 item(ID_HIDE_DONE).Check(self.config.hide_events_done)
376
388
390
391 def edit_function():
392 self._set_category_to_selected_events()
393
394 safe_locking(self, edit_function)
395
400
401 def edit_event(evt):
402 try:
403 event_id = self.main_panel.get_id_of_first_selected_event()
404 event = self.timeline.find_event_with_id(event_id)
405 except IndexError:
406
407 return
408 self.main_panel.open_event_editor(event)
409
410 def duplicate_event(evt):
411 try:
412 event_id = self.main_panel.get_id_of_first_selected_event()
413 event = self.timeline.find_event_with_id(event_id)
414 except IndexError:
415
416 return
417 open_duplicate_event_dialog_for_event(self, self, self.timeline, event)
418
419 def create_milestone(evt):
420 open_milestone_editor_for(self, self, self.config, self.timeline)
421
422 def set_categoryon_selected(evt):
423
424 def edit_function():
425 self._set_category_to_selected_events()
426 safe_locking(self, edit_function)
427
428 def measure_distance(evt):
429 self._measure_distance_between_events()
430
431 def set_category_on_without(evt):
432 def edit_function():
433 self._set_category()
434 safe_locking(self, edit_function)
435
436 def edit_categories(evt):
437 def edit_function():
438 self._edit_categories()
439 safe_locking(self, edit_function)
440
441 def edit_eras(evt):
442 def edit_function():
443 self._edit_eras()
444 safe_locking(self, edit_function)
445
446 def set_readonly(evt):
447 self.controller.set_timeline_in_readonly_mode()
448
449 def undo(evt):
450 safe_locking(self, self.timeline.undo)
451
452 def redo(evt):
453 safe_locking(self, self.timeline.redo)
454
455 def compress(evt):
456 safe_locking(self, self.timeline.compress)
457
458 def move_up_handler(event):
459 self.main_panel.timeline_panel.move_selected_event_up()
460
461 def move_down_handler(event):
462 self.main_panel.timeline_panel.move_selected_event_down()
463
464 cbx = NONE
465 items_spec = ((ID_CREATE_EVENT, create_event, _("Create &Event..."), cbx),
466 (ID_EDIT_EVENT, edit_event, _("&Edit Selected Event..."), cbx),
467 (ID_DUPLICATE_EVENT, duplicate_event, _("&Duplicate Selected Event..."), cbx),
468 (ID_SET_CATEGORY_ON_SELECTED, set_categoryon_selected, _("Set Category on Selected Events..."), cbx),
469 (ID_MOVE_EVENT_UP, move_up_handler, _("Move event up") + "\tAlt+Up", cbx),
470 (ID_MOVE_EVENT_DOWN, move_down_handler, _("Move event down") + "\tAlt+Down", cbx),
471 None,
472 (ID_CREATE_MILESTONE, create_milestone, _("Create &Milestone..."), cbx),
473 None,
474 (ID_COMPRESS, compress, _("&Compress timeline Events"), cbx),
475 None,
476 (ID_MEASURE_DISTANCE, measure_distance, _("&Measure Distance between two Events..."), cbx),
477 None,
478 (ID_SET_CATEGORY_ON_WITHOUT, set_category_on_without,
479 _("Set Category on events &without category..."), cbx),
480 (ID_EDIT_CATEGORIES, edit_categories, _("Edit &Categories"), cbx),
481 None,
482 (ID_EDIT_ERAS, edit_eras, _("Edit Era's..."), cbx),
483 None,
484 (ID_SET_READONLY, set_readonly, _("&Read Only"), cbx),
485 None,
486 (ID_UNDO, undo, _("&Undo") + "\tCtrl+Z", cbx),
487 (ID_REDO, redo, _("&Redo") + "\tAlt+Z", cbx))
488 self._timeline_menu = self._create_menu(items_spec)
489 self._add_timeline_menu_items_to_controller(self._timeline_menu)
490 return self._timeline_menu
491
493 self._add_to_controller_requiring_writeable_timeline(menu, ID_CREATE_EVENT)
494 self._add_to_controller_requiring_writeable_timeline(menu, ID_EDIT_EVENT)
495 self._add_to_controller_requiring_writeable_timeline(menu, ID_CREATE_MILESTONE)
496 self._add_to_controller_requiring_writeable_timeline(menu, ID_DUPLICATE_EVENT)
497 self._add_to_controller_requiring_writeable_timeline(menu, ID_SET_CATEGORY_ON_SELECTED)
498 self._add_to_controller_requiring_writeable_timeline(menu, ID_MEASURE_DISTANCE)
499 self._add_to_controller_requiring_writeable_timeline(menu, ID_SET_CATEGORY_ON_WITHOUT)
500 self._add_to_controller_requiring_writeable_timeline(menu, ID_EDIT_CATEGORIES)
501 self._add_to_controller_requiring_writeable_timeline(menu, ID_SET_READONLY)
502 self._add_to_controller_requiring_writeable_timeline(menu, ID_EDIT_ERAS)
503 self._add_to_controller_requiring_writeable_timeline(menu, ID_COMPRESS)
504
508
519
520 def find_last(evt):
521 event = self.timeline.get_last_event()
522 if event:
523 end = event.get_end_time()
524 delta = self.main_panel.get_displayed_period_delta()
525 try:
526 start = end - delta
527 except ValueError:
528 start = self.timeline.get_time_type().get_min_time()
529 margin_delta = delta / 24
530 self.main_panel.Navigate(lambda tp: tp.update(start, end, end_delta=margin_delta))
531
532 def restore_time_period(evt):
533 if self.prev_time_period:
534 self.main_panel.Navigate(lambda tp: self.prev_time_period)
535
536 def fit_all(evt):
537 self._fit_all_events()
538
539 cbx = NONE
540 items_spec = (None,
541 (ID_FIND_FIRST, find_first, _("Find &First Event"), cbx),
542 (ID_FIND_LAST, find_last, _("Find &Last Event"), cbx),
543 (ID_FIT_ALL, fit_all, _("Fit &All Events"), cbx),
544 None,
545 (ID_RESTORE_TIME_PERIOD, restore_time_period, _("Go to previous time period"), cbx),)
546 self._navigation_menu_items = []
547 self._navigation_functions_by_menu_item_id = {}
548 self.update_navigation_menu_items()
549 self._navigate_menu = self._create_menu(items_spec)
550 self._add_navigate_menu_items_to_controller(self._navigate_menu)
551 return self._navigate_menu
552
554 self._add_to_controller_requiring_timeline(menu, ID_FIND_FIRST)
555 self._add_to_controller_requiring_timeline(menu, ID_FIND_LAST)
556 self._add_to_controller_requiring_timeline(menu, ID_FIT_ALL)
557
561
566
567 cbx = NONE
568 items_spec = [(wx.ID_HELP, self.help_browser.show_contents_page, _("&Contents") + "\tF1", cbx),
569 None,
570 (ID_TUTORIAL, self.controller.open_gregorian_tutorial_timeline, _("Getting started &tutorial"), cbx),
571 (ID_NUMTUTORIAL, self.controller.open_numeric_tutorial_timeline, _("Getting started numeric &tutorial"), cbx),
572 None,
573 (ID_FEEDBACK, feedback, _("Give &Feedback..."), cbx),
574 (ID_CONTACT, self.help_browser.show_contact_page, _("Co&ntact"), cbx),
575 None,
576 (ID_SYSTEM_INFO, show_system_info_dialog, _("System information"), cbx),
577 None,
578 (wx.ID_ABOUT, display_about_dialog, None, cbx)]
579 self._help_menu = self._create_menu(items_spec)
580 return self._help_menu
581
583 try:
584 menu = self.context_menu
585 except:
586 self.context_menu = self._create_timeline_context_menu()
587 menu = self.context_menu
588 self.PopupMenu(menu)
589 self._create_main_menu_bar()
590
592 self.context_menu = self._create_timeline_context_menu()
593
595 menu = wx.Menu()
596 menu_bar = self.GetMenuBar()
597 menu.Append(wx.ID_ANY, menu_bar.GetMenuLabel(0), self._file_menu)
598 menu.Append(wx.ID_ANY, menu_bar.GetMenuLabel(1), self._edit_menu)
599 menu.Append(wx.ID_ANY, menu_bar.GetMenuLabel(2), self._view_menu)
600 menu.Append(wx.ID_ANY, menu_bar.GetMenuLabel(3), self._timeline_menu)
601 menu.Append(wx.ID_ANY, menu_bar.GetMenuLabel(4), self._navigate_menu)
602 menu.Append(wx.ID_ANY, menu_bar.GetMenuLabel(5), self._help_menu)
603 return menu
604
606 menu = wx.Menu()
607 for item in items_spec:
608 if item is not None:
609 self._create_menu_item(menu, item)
610 else:
611 menu.AppendSeparator()
612 return menu
613
615 if isinstance(item_spec, collections.Callable):
616 item_spec(menu)
617 else:
618 item_id, handler, label, checkbox = item_spec
619 if label is not None:
620 if checkbox == CHECKBOX:
621 item = menu.Append(item_id, label, kind=wx.ITEM_CHECK)
622 elif checkbox == CHECKED_RB:
623 item = menu.Append(item_id, label, kind=wx.ITEM_RADIO)
624 item.Check(True)
625 elif checkbox == UNCHECKED_RB:
626 item = menu.Append(item_id, label, kind=wx.ITEM_RADIO)
627 else:
628 if label is not None:
629 item = menu.Append(item_id, label)
630 else:
631 item = menu.Append(item_id)
632 else:
633 item = menu.Append(item_id)
634 self.shortcut_items[item_id] = menu.FindItemById(item_id)
635 self.Bind(wx.EVT_MENU, handler, item)
636
638 items = [
639 {
640 "text": _("Gregorian"),
641 "description": _("This creates a timeline using the standard calendar."),
642 "create_fn": self._create_new_timeline,
643 },
644 {
645 "text": _("Numeric"),
646 "description": _("This creates a timeline that has numbers on the x-axis instead of dates."),
647 "create_fn": self._create_new_numeric_timeline,
648 },
649 {
650 "text": _("Directory"),
651 "description": _("This creates a timeline where the modification date of files in a directory are shown as events."),
652 "create_fn": self._create_new_dir_timeline,
653 },
654 {
655 "text": _("Bosparanian"),
656 "description": _("This creates a timeline using the fictuous Bosparanian calendar from the German pen-and-paper RPG \"The Dark Eye\" (\"Das schwarze Auge\", DSA)."),
657 "create_fn": self._create_new_bosparanian_timeline,
658 },
659 {
660 "text": _("Pharaonic"),
661 "description": _("This creates a timeline using the ancient egypt pharaonic calendar"),
662 "create_fn": self._create_new_pharaonic_timeline,
663 },
664 {
665 "text": _("Coptic"),
666 "description": _("This creates a timeline using the coptic calendar"),
667 "create_fn": self._create_new_coptic_timeline,
668 },
669 ]
670 dialog = FileNewDialog(self, items)
671 if dialog.ShowModal() == wx.ID_OK:
672 dialog.GetSelection()["create_fn"]()
673 dialog.Destroy()
674
676 self._open_existing_timeline()
677
679 if self.timeline is not None:
680 self._save_as()
681
683 def open_import_dialog():
684 dialog = ImportEventsDialog(self.timeline, self)
685 dialog.ShowModal()
686 dialog.Destroy()
687 safe_locking(self, open_import_dialog)
688
691
693 plain = wx.GetStockLabel(wx_id, wx.STOCK_WITH_ACCELERATOR | wx.STOCK_WITH_MNEMONIC)
694
695 tab_index = plain.find("\t")
696 if tab_index != -1:
697 return plain[:tab_index] + "..." + plain[tab_index:]
698 return plain + "..."
699