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