Gnumed.timelinelib.wxgui.frames.mainframe.guicreator

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  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_ERAS = wx.NewId()
     66  ID_SET_READONLY = wx.NewId()
     67  ID_FIND_FIRST = wx.NewId()
     68  ID_FIND_LAST = wx.NewId()
     69  ID_FIT_ALL = wx.NewId()
     70  ID_EDIT_SHORTCUTS = wx.NewId()
     71  ID_TUTORIAL = wx.NewId()
     72  ID_NUMTUTORIAL = wx.NewId()
     73  ID_FEEDBACK = wx.NewId()
     74  ID_CONTACT = wx.NewId()
     75  ID_SYSTEM_INFO = wx.NewId()
     76  ID_IMPORT = wx.NewId()
     77  ID_EXPORT = wx.NewId()
     78  ID_EXPORT_ALL = wx.NewId()
     79  ID_EXPORT_SVG = wx.NewId()
     80  ID_FIND_CATEGORIES = wx.NewId()
     81  ID_SELECT_ALL = wx.NewId()
     82  ID_RESTORE_TIME_PERIOD = wx.NewId()
     83  ID_NEW = wx.ID_NEW
     84  ID_FIND = wx.ID_FIND
     85  ID_UNDO = wx.NewId()
     86  ID_REDO = wx.NewId()
     87  ID_PREFERENCES = wx.ID_PREFERENCES
     88  ID_HELP = wx.ID_HELP
     89  ID_ABOUT = wx.ID_ABOUT
     90  ID_SAVEAS = wx.ID_SAVEAS
     91  ID_EXIT = wx.ID_EXIT
     92  ID_MOVE_EVENT_UP = wx.NewId()
     93  ID_MOVE_EVENT_DOWN = wx.NewId()
     94  ID_PRESENTATION = wx.NewId()
     95  ID_HIDE_DONE = wx.NewId()
     96  ID_NAVIGATE = wx.NewId() + 100
     97
     98
     99 -class GuiCreator(object):
    100
    101 -    def _create_gui(self):
    102          self.shortcut_items = {}
    103          self._create_status_bar()
    104          self._create_main_panel()
    105          self._create_main_menu_bar()
    106          self._bind_frame_events()
    107
    108 -    def _create_status_bar(self):
    109          self.CreateStatusBar()
    110          self.status_bar_adapter = StatusBarAdapter(self.GetStatusBar())
    111
    112 -    def _create_main_panel(self):
    113          self.main_panel = MainPanel(self, self.config, self)
    114
    127
    128 -    def _set_shortcuts(self):
    129          from timelinelib.config.shortcut import ShortcutController
    130          self.shortcut_controller = ShortcutController(self.config, self.shortcut_items)
    131          self.shortcut_controller.load_config_settings()
    132
    133 -    def _bind_frame_events(self):
    134          self.Bind(wx.EVT_CLOSE, self._window_on_close)
    135
    151
    152 -    def _create_export_menues(self, file_menu):
    153
    154          def create_click_handler(plugin, main_frame):
    155              def event_handler(evt):
    156                  plugin.run(main_frame)
    157              return event_handler
    158
    159          submenu = wx.Menu()
    160          file_menu.Append(wx.ID_ANY, _("Export"), submenu)
    161          for plugin in factory.get_plugins(EXPORTER):
    162              mnu = submenu.Append(wx.ID_ANY, plugin.display_name(), plugin.display_name())
    163              self.menu_controller.add_menu_requiring_timeline(mnu)
    164              handler = create_click_handler(plugin, self)
    165              self.Bind(wx.EVT_MENU, handler, mnu)
    166              method = getattr(plugin, "wxid", None)
    167              if callable(method):
    168                  self.shortcut_items[method()] = mnu
    169
    177
    183
    188
    193
    200
    205
    211
    212          def find(evt):
    213              self.main_panel.show_searchbar(True)
    214
    215          def find_categories(evt):
    216              dialog = create_category_find_dialog()
    217              dialog.ShowModal()
    218              dialog.Destroy()
    219
    220          def select_all(evt):
    221              self.controller.select_all()
    222
    223          def preferences(evt):
    224              def edit_function():
    225                  dialog = PreferencesDialog(self, self.config)
    226                  dialog.ShowModal()
    227                  dialog.Destroy()
    228              safe_locking(self, edit_function)
    229
    230          def edit_shortcuts(evt):
    231
    232              def edit_function():
    233                  dialog = ShortcutsEditorDialog(self, self.shortcut_controller)
    234                  dialog.ShowModal()
    235                  dialog.Destroy()
    236              safe_locking(self, edit_function)
    237          cbx = NONE
    238          items_spec = ((wx.ID_FIND, find, None, cbx),
    239                        (ID_FIND_CATEGORIES, find_categories, _("Find Categories..."), cbx),
    240                        None,
    241                        (ID_SELECT_ALL, select_all, _("Select All Events"), cbx),
    242                        None,
    243                        (wx.ID_PREFERENCES, preferences, None, cbx),
    244                        (ID_EDIT_SHORTCUTS, edit_shortcuts, _("Shortcuts..."), cbx))
    245          self._edit_menu = self._create_menu(items_spec)
    246          self._add_edit_menu_items_to_controller(self._edit_menu)
    247          return self._edit_menu
    248
    254
    263
    264          def legend(evt):
    265              self.config.show_legend = evt.IsChecked()
    266
    267          def balloons(evt):
    268              self.config.balloon_on_hover = evt.IsChecked()
    269
    270          def zoomin(evt):
    271              DrawingAreaProxy(self).zoom_in()
    272
    273          def zoomout(evt):
    274              DrawingAreaProxy(self).zoom_out()
    275
    276          def vert_zoomin(evt):
    277              DrawingAreaProxy(self).vertical_zoom_in()
    278
    279          def vert_zoomout(evt):
    280              DrawingAreaProxy(self).vertical_zoom_out()
    281
    282          def start_slide_show(evt):
    283              canvas = self.main_panel.get_timeline_canvas()
    284              self.controller.start_slide_show(canvas)
    285
    286          def hide_events_done(evt):
    287              self.config.hide_events_done = evt.IsChecked()
    288
    289          items_spec = [self._create_view_toolbar_menu_item,
    290                        (ID_SIDEBAR, sidebar, _("&Sidebar") + "\tCtrl+I", CHECKBOX),
    291                        (ID_LEGEND, legend, _("&Legend"), CHECKBOX),
    292                        None,
    293                        (ID_BALLOONS, balloons, _("&Balloons on hover"), CHECKBOX),
    294                        None,
    295                        (ID_ZOOMIN, zoomin, _("Zoom &In") + "\tCtrl++", NONE),
    296                        (ID_ZOOMOUT, zoomout, _("Zoom &Out") + "\tCtrl+-", NONE),
    297                        (ID_VERT_ZOOMIN, vert_zoomin, _("Vertical Zoom &In") + "\tAlt++", NONE),
    298                        (ID_VERT_ZOOMOUT, vert_zoomout, _("Vertical Zoom &Out") + "\tAlt+-", NONE),
    299                        None,
    300                        self._create_view_point_event_alignment_menu,
    301                        None,
    302                        self._create_event_box_drawers_menu,
    303                        None,
    304                        (ID_PRESENTATION, start_slide_show, _("Start slide show") + "...", NONE),
    305                        None,
    306                        (ID_HIDE_DONE, hide_events_done, _("&Hide Events done"), CHECKBOX),
    307                        ]
    308          self._view_menu = self._create_menu(items_spec)
    309          self._check_view_menu_items(self._view_menu)
    310          self._add_view_menu_items_to_controller(self._view_menu)
    311          return self._view_menu
    312
    318
    319          def check_item_corresponding_to_config():
    320              item.Check(self.config.show_toolbar)
    321
    322          self.Bind(wx.EVT_MENU, on_click, item)
    323          self.config.listen_for_any(check_item_corresponding_to_config)
    324          check_item_corresponding_to_config()
    325
    332              return event_handler
    333
    334          items = []
    335          for plugin in factory.get_plugins(EVENTBOX_DRAWER):
    336              if plugin.display_name() == self.config.get_selected_event_box_drawer():
    337                  items.append((wx.ID_ANY, create_click_handler(plugin), plugin.display_name(), CHECKED_RB))
    338              else:
    339                  items.append((wx.ID_ANY, create_click_handler(plugin), plugin.display_name(), UNCHECKED_RB))
    340          sub_menu = self._create_menu(items)
    341          view_menu.Append(wx.ID_ANY, _("Event appearance"), sub_menu)
    342
    351
    352          def on_second_tool_click(event):
    353              self.config.draw_point_events_to_right = False
    354
    355          def check_item_corresponding_to_config():
    356              if self.config.draw_point_events_to_right:
    357                  left_item.Check()
    358              else:
    359                  center_item.Check()
    360
    361          self.Bind(wx.EVT_MENU, on_first_tool_click, left_item)
    362          self.Bind(wx.EVT_MENU, on_second_tool_click, center_item)
    363          self.config.listen_for_any(check_item_corresponding_to_config)
    364          check_item_corresponding_to_config()
    365
    370
    371          item(ID_SIDEBAR).Check(self.config.show_sidebar)
    372          item(ID_LEGEND).Check(self.config.show_legend)
    373          item(ID_BALLOONS).Check(self.config.balloon_on_hover)
    374          item(ID_HIDE_DONE).Check(self.config.hide_events_done)
    375
    387
    388 -    def set_category_on_selected(self):
    389
    390          def edit_function():
    391              self._set_category_to_selected_events()
    392
    393          safe_locking(self, edit_function)
    394
    399
    400          def edit_event(evt):
    401              try:
    402                  event_id = self.main_panel.get_id_of_first_selected_event()
    403                  event = self.timeline.find_event_with_id(event_id)
    404              except IndexError:
    405                  # No event selected so do nothing!
    406                  return
    407              self.main_panel.open_event_editor(event)
    408
    409          def duplicate_event(evt):
    410              try:
    411                  event_id = self.main_panel.get_id_of_first_selected_event()
    412                  event = self.timeline.find_event_with_id(event_id)
    413              except IndexError:
    414                  # No event selected so do nothing!
    415                  return
    416              open_duplicate_event_dialog_for_event(self, self, self.timeline, event)
    417
    418          def create_milestone(evt):
    419              open_milestone_editor_for(self, self, self.config, self.timeline)
    420
    421          def set_categoryon_selected(evt):
    422
    423              def edit_function():
    424                  self._set_category_to_selected_events()
    425              safe_locking(self, edit_function)
    426
    427          def measure_distance(evt):
    428              self._measure_distance_between_events()
    429
    430          def set_category_on_without(evt):
    431              def edit_function():
    432                  self._set_category()
    433              safe_locking(self, edit_function)
    434
    435          def edit_eras(evt):
    436              def edit_function():
    437                  self._edit_eras()
    438              safe_locking(self, edit_function)
    439
    440          def set_readonly(evt):
    441              self.controller.set_timeline_in_readonly_mode()
    442
    443          def undo(evt):
    444              safe_locking(self, self.timeline.undo)
    445
    446          def redo(evt):
    447              safe_locking(self, self.timeline.redo)
    448
    449          def compress(evt):
    450              safe_locking(self, self.timeline.compress)
    451
    452          def move_up_handler(event):
    453              self.main_panel.timeline_panel.move_selected_event_up()
    454
    455          def move_down_handler(event):
    456              self.main_panel.timeline_panel.move_selected_event_down()
    457
    458          cbx = NONE
    459          items_spec = ((ID_CREATE_EVENT, create_event, _("Create &Event..."), cbx),
    460                        (ID_EDIT_EVENT, edit_event, _("&Edit Selected Event..."), cbx),
    461                        (ID_DUPLICATE_EVENT, duplicate_event, _("&Duplicate Selected Event..."), cbx),
    462                        (ID_SET_CATEGORY_ON_SELECTED, set_categoryon_selected, _("Set Category on Selected Events..."), cbx),
    463                        (ID_MOVE_EVENT_UP, move_up_handler, _("Move event up") + "\tAlt+Up", cbx),
    464                        (ID_MOVE_EVENT_DOWN, move_down_handler, _("Move event down") + "\tAlt+Down", cbx),
    465                        None,
    466                        (ID_CREATE_MILESTONE, create_milestone, _("Create &Milestone..."), cbx),
    467                        None,
    468                        (ID_COMPRESS, compress, _("&Compress timeline Events"), cbx),
    469                        None,
    470                        (ID_MEASURE_DISTANCE, measure_distance, _("&Measure Distance between two Events..."), cbx),
    471                        None,
    472                        (ID_SET_CATEGORY_ON_WITHOUT, set_category_on_without,
    473                         _("Set Category on events &without category..."), cbx),
    474                        None,
    475                        (ID_EDIT_ERAS, edit_eras, _("Edit Era's..."), cbx),
    476                        None,
    477                        (ID_SET_READONLY, set_readonly, _("&Read Only"), cbx),
    478                        None,
    479                        (ID_UNDO, undo, _("&Undo") + "\tCtrl+Z", cbx),
    480                        (ID_REDO, redo, _("&Redo") + "\tAlt+Z", cbx))
    481          self._timeline_menu = self._create_menu(items_spec)
    482          self._add_timeline_menu_items_to_controller(self._timeline_menu)
    483          return self._timeline_menu
    484
    496
    497 -    def _add_to_controller_requiring_writeable_timeline(self, menu, item_id):
    498          mnu_item = menu.FindItemById(item_id)
    499          self.menu_controller.add_menu_requiring_writable_timeline(mnu_item)
    500
    511
    512          def find_last(evt):
    513              event = self.timeline.get_last_event()
    514              if event:
    515                  end = event.get_end_time()
    516                  delta = self.main_panel.get_displayed_period_delta()
    517                  try:
    518                      start = end - delta
    519                  except ValueError:
    520                      start = self.timeline.get_time_type().get_min_time()
    521                  margin_delta = delta / 24
    522                  self.main_panel.Navigate(lambda tp: tp.update(start, end, end_delta=margin_delta))
    523
    524          def restore_time_period(evt):
    525              if self.prev_time_period:
    526                  self.main_panel.Navigate(lambda tp: self.prev_time_period)
    527
    528          def fit_all(evt):
    529              self._fit_all_events()
    530
    531          cbx = NONE
    532          items_spec = (None,
    533                        (ID_FIND_FIRST, find_first, _("Find &First Event"), cbx),
    534                        (ID_FIND_LAST, find_last, _("Find &Last Event"), cbx),
    535                        (ID_FIT_ALL, fit_all, _("Fit &All Events"), cbx),
    536                        None,
    537                        (ID_RESTORE_TIME_PERIOD, restore_time_period, _("Go to previous time period"), cbx),)
    538          self._navigation_menu_items = []
    539          self._navigation_functions_by_menu_item_id = {}
    540          self.update_navigation_menu_items()
    541          self._navigate_menu = self._create_menu(items_spec)
    542          self._add_navigate_menu_items_to_controller(self._navigate_menu)
    543          return self._navigate_menu
    544
    549
    550 -    def _add_to_controller_requiring_timeline(self, menu, item_id):
    551          mnu_item = menu.FindItemById(item_id)
    552          self.menu_controller.add_menu_requiring_timeline(mnu_item)
    553
    558
    559          cbx = NONE
    560          items_spec = [(wx.ID_HELP, self.help_browser.show_contents_page, _("&Contents") + "\tF1", cbx),
    561                        None,
    562                        (ID_TUTORIAL, self.controller.open_gregorian_tutorial_timeline, _("Getting started &tutorial"), cbx),
    563                        (ID_NUMTUTORIAL, self.controller.open_numeric_tutorial_timeline, _("Getting started numeric &tutorial"), cbx),
    564                        None,
    565                        (ID_FEEDBACK, feedback, _("Give &Feedback..."), cbx),
    566                        (ID_CONTACT, self.help_browser.show_contact_page, _("Co&ntact"), cbx),
    567                        None,
    568                        (ID_SYSTEM_INFO, show_system_info_dialog, _("System information"), cbx),
    569                        None,
    570                        (wx.ID_ABOUT, display_about_dialog, None, cbx)]
    571          self._help_menu = self._create_menu(items_spec)
    572          return self._help_menu
    573
    582
    605
    606 -    def _mnu_file_new_on_click(self, event):
    607          items = [
    608              {
    609                  "text": _("Gregorian"),
    610                  "description": _("This creates a timeline using the standard calendar."),
    611                  "create_fn": self._create_new_timeline,
    612              },
    613              {
    614                  "text": _("Numeric"),
    615                  "description": _("This creates a timeline that has numbers on the x-axis instead of dates."),
    616                  "create_fn": self._create_new_numeric_timeline,
    617              },
    618              {
    619                  "text": _("Directory"),
    620                  "description": _("This creates a timeline where the modification date of files in a directory are shown as events."),
    621                  "create_fn": self._create_new_dir_timeline,
    622              },
    623              {
    624                  "text": _("Bosparanian"),
    625                  "description": _("This creates a timeline using the fictuous Bosparanian calendar from the German pen-and-paper RPG \"The Dark Eye\" (\"Das schwarze Auge\", DSA)."),
    626                  "create_fn": self._create_new_bosparanian_timeline,
    627              },
    628              {
    629                  "text": _("Pharaonic"),
    630                  "description": _("This creates a timeline using the ancient egypt pharaonic calendar"),
    631                  "create_fn": self._create_new_pharaonic_timeline,
    632              },
    633              {
    634                  "text": _("Coptic"),
    635                  "description": _("This creates a timeline using the coptic calendar"),
    636                  "create_fn": self._create_new_coptic_timeline,
    637              },
    638          ]
    639          dialog = FileNewDialog(self, items)
    640          if dialog.ShowModal() == wx.ID_OK:
    641              dialog.GetSelection()["create_fn"]()
    642          dialog.Destroy()
    643
    644 -    def _mnu_file_open_on_click(self, event):
    645          self._open_existing_timeline()
    646
    647 -    def mnu_file_save_as_on_click(self, event):
    648          if self.timeline is not None:
    649              self._save_as()
    650
    651 -    def _mnu_file_import_on_click(self, menu):
    652          def open_import_dialog():
    653              dialog = ImportEventsDialog(self.timeline, self)
    654              dialog.ShowModal()
    655              dialog.Destroy()
    656          safe_locking(self, open_import_dialog)
    657
    658 -    def _mnu_file_exit_on_click(self, evt):
    659          self.Close()
    660
    661 -    def _add_ellipses_to_menuitem(self, wx_id):
    662          plain = wx.GetStockLabel(wx_id, wx.STOCK_WITH_ACCELERATOR | wx.STOCK_WITH_MNEMONIC)
    663          # format of plain 'xxx[\tyyy]', example '&New\tCtrl+N'
    664          tab_index = plain.find("\t")
    665          if tab_index != -1:
    666              return plain[:tab_index] + "..." + plain[tab_index:]
    667          return plain + "..."
    668