Package Gnumed :: Package timelinelib :: Package wxgui :: Package frames :: Package mainframe :: Module guicreator |
[frames] | no frames]

# Source Code for Module Gnumed.timelinelib.wxgui.frames.mainframe.guicreator

      1  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
      2  #
      3  # This file is part of Timeline.
      4  #
      5  # Timeline is free software: you can redistribute it and/or modify
      6  # it under the terms of the GNU General Public License as published by
      7  # the Free Software Foundation, either version 3 of the License, or
      8  # (at your option) any later version.
      9  #
     10  # Timeline is distributed in the hope that it will be useful,
     11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  # GNU General Public License for more details.
     14  #
     15  # You should have received a copy of the GNU General Public License
     16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>.
     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
     96 -class GuiCreator(object):
     97
     98 -    def _create_gui(self):
     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
    105 -    def _create_status_bar(self):
    106          self.CreateStatusBar()
    107          self.status_bar_adapter = StatusBarAdapter(self.GetStatusBar())
    108
    109 -    def _create_main_panel(self):
    110          self.main_panel = MainPanel(self, self.config, self)
    111
    124
    125 -    def _set_shortcuts(self):
    126          from timelinelib.config.shortcut import ShortcutController
    127          self.shortcut_controller = ShortcutController(self.config, self.shortcut_items)
    128          self.shortcut_controller.load_config_settings()
    129
    130 -    def _bind_frame_events(self):
    131          self.Bind(wx.EVT_CLOSE, self._window_on_close)
    132
    148
    149 -    def _create_export_menues(self, file_menu):
    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
    174
    180
    185
    190
    197
    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
    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
    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
    385 -    def set_category_on_selected(self):
    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                  # No event selected so do nothing!
    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                  # No event selected so do nothing!
    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
    500
    501 -    def _add_to_controller_requiring_writeable_timeline(self, menu, item_id):
    502          mnu_item = menu.FindItemById(item_id)
    503          self.menu_controller.add_menu_requiring_writable_timeline(mnu_item)
    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
    553
    554 -    def _add_to_controller_requiring_timeline(self, menu, item_id):
    555          mnu_item = menu.FindItemById(item_id)
    556          self.menu_controller.add_menu_requiring_timeline(mnu_item)
    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
    585
    588
    599
    608
    631
    632 -    def _mnu_file_new_on_click(self, event):
    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
    660 -    def _mnu_file_open_on_click(self, event):
    661          self._open_existing_timeline()
    662
    663 -    def mnu_file_save_as_on_click(self, event):
    664          if self.timeline is not None:
    665              self._save_as()
    666
    667 -    def _mnu_file_import_on_click(self, menu):
    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
    674 -    def _mnu_file_exit_on_click(self, evt):
    675          self.Close()
    676
    677 -    def _add_ellipses_to_menuitem(self, wx_id):
    678          plain = wx.GetStockLabel(wx_id, wx.STOCK_WITH_ACCELERATOR | wx.STOCK_WITH_MNEMONIC)
    679          # format of plain 'xxx[\tyyy]', example '&New\tCtrl+N'
    680          tab_index = plain.find("\t")
    681          if tab_index != -1:
    682              return plain[:tab_index] + "..." + plain[tab_index:]
    683          return plain + "..."
    684