Gnumed.timelinelib.wxgui.frames.mainframe.mainframe

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

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

      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 os.path
     20
     21  import wx.lib.newevent
     22
     23  from timelinelib.canvas.data import TimePeriod
     24  from timelinelib.config.dotfile import read_config
     25  from timelinelib.config.paths import ICONS_DIR
     26  from timelinelib.config.paths import LOCALE_DIR
     27  from timelinelib.dataexport.timelinexml import export_db_to_timeline_xml
     28  from timelinelib.db import db_open
     29  from timelinelib.features.experimental.experimentalfeatures import ExperimentalFeatures
     30  from timelinelib.meta.about import APPLICATION_NAME
     31  from timelinelib.calendar.bosparanian.timetype import BosparanianTimeType
     32  from timelinelib.calendar.num.timetype import NumTimeType
     33  from timelinelib.utils import ex_msg
     34  from timelinelib.wxgui.dialogs.changenowdate.view import ChangeNowDateDialog
     35  from timelinelib.wxgui.dialogs.eraseditor.view import ErasEditorDialog
     36  from timelinelib.wxgui.dialogs.setcategory.view import SetCategoryDialog
     37  from timelinelib.wxgui.dialogs.timeeditor.view import TimeEditorDialog
     38  from timelinelib.wxgui.frames.helpbrowserframe.helpbrowserframe import HelpBrowserFrame
     39  from timelinelib.wxgui.frames.mainframe.mainframecontroller import LockedException
     40  from timelinelib.wxgui.frames.mainframe.mainframecontroller import MainFrameController
     41  from timelinelib.wxgui.timer import TimelineTimer
     42  from timelinelib.wxgui.utils import display_error_message
     43  from timelinelib.wxgui.utils import display_information_message
     44  from timelinelib.wxgui.utils import WildcardHelper
     45  import timelinelib.wxgui.frames.mainframe.guicreator as guic
     46  from timelinelib.wxgui.frames.mainframe.controllerapi import MainFrameApiUsedByController
     47  from timelinelib.wxgui.frames.mainframe.alertcontroller import AlertController
     48  from timelinelib.wxgui.frames.mainframe.menucontroller import MenuController
     49  from timelinelib.calendar.coptic.timetype import CopticTimeType
     50  from timelinelib.calendar.pharaonic.timetype import PharaonicTimeType
     51
     52  CatsViewChangedEvent, EVT_CATS_VIEW_CHANGED = wx.lib.newevent.NewCommandEvent()
     53
     54
     55 -class MainFrame(wx.Frame, guic.GuiCreator, MainFrameApiUsedByController):
     56
     57 -    def __init__(self, application_arguments):
     58          self.config = read_config(application_arguments.get_config_file_path())
     59          wx.Frame.__init__(self, None, size=self.config.get_window_size(),
     60                            pos=self.config.get_window_pos(),
     61                            style=wx.DEFAULT_FRAME_STYLE, name="main_frame")
     62          self.Bind(EVT_CATS_VIEW_CHANGED, self._on_cats_view_changed)
     63          # To enable translations of wx stock items.
     64          self.locale = wx.Locale(wx.LANGUAGE_DEFAULT)
     65          self.locale.AddCatalogLookupPathPrefix(LOCALE_DIR)
     66          self.locale.AddCatalog("wxstd")
     67          self.help_browser = HelpBrowserFrame(self)
     68          self.controller = MainFrameController(self, db_open, self.config)
     69          self.menu_controller = MenuController()
     70          self._set_initial_values_to_member_variables()
     71          self._create_print_data()
     72          self._set_experimental_features()
     73          self._create_gui()
     74          self.Maximize(self.config.window_maximized)
     75          self.SetTitle(APPLICATION_NAME)
     76          self.SetIcons(self._load_icon_bundle())
     77          self.main_panel.show_welcome_panel()
     78          self.enable_disable_menus()
     79          self.controller.on_started(application_arguments)
     80          self._create_and_start_timer()
     81          self.prev_time_period = None
     82
     83 -    def DisplayErrorMessage(self, message):
     84          display_error_message(message, parent=self)
     85
     86 -    def DisplayStatus(self, message):
     87          self.status_bar_adapter.set_text(message)
     88
     89      # API:s used by time types
     90 -    def week_starts_on_monday(self):
     91          return self.controller.week_starts_on_monday()
     92
     93 -    def display_time_editor_dialog(self, time_type, initial_time,
     94                                     handle_new_time_fn, title):
     95          dialog = TimeEditorDialog(self, self.config, time_type, initial_time, title)
     96          dialog.ShowModal()
     97          result = dialog.GetReturnCode()
     98          if result == wx.ID_OK:
     99              handle_new_time_fn(dialog.GetTime())
    100          dialog.Destroy()
    101
    102 -    def display_now_date_editor_dialog(self, handle_new_time_fn, title):
    103          dialog = ChangeNowDateDialog(self, self.config, self.timeline, handle_new_time_fn, title)
    104          dialog.Show()
    105
    106 -    def save_time_period(self):
    107          self.prev_time_period = self.main_panel.get_time_period()
    108
    109      # Concurrent editing
    110 -    def ok_to_edit(self):
    111          try:
    112              return self.controller.ok_to_edit()
    113          except LockedException:
    114              return False
    115
    116 -    def get_lockfile_path(self):
    117          return self.controller._get_lockpath()
    118
    119 -    def edit_ends(self):
    120          self.controller.edit_ends()
    121
    122 -    def get_view_properties(self, ):
    123          return self.main_panel.get_view_properties()
    124
    125 -    def _on_cats_view_changed(self, evt):
    126          self.main_panel.get_view_properties().change_view_cats_individually(evt.is_checked)
    127
    128      # Creation process methods
    129 -    def _set_initial_values_to_member_variables(self):
    130          self.timeline = None
    131          self.timeline_wildcard_helper = WildcardHelper(
    132              _("Timeline files"), ["timeline", "ics"])
    133          self.images_svg_wildcard_helper = WildcardHelper(
    134              _("SVG files"), ["svg"])
    135
    136 -    def _create_print_data(self):
    137          self.printData = wx.PrintData()
    138          self.printData.SetPaperId(wx.PAPER_A4)
    139          self.printData.SetPrintMode(wx.PRINT_MODE_PRINTER)
    140          self.printData.SetOrientation(wx.LANDSCAPE)
    141
    142 -    def _create_and_start_timer(self):
    143          self.alert_dialog_open = False
    144          self.timer = TimelineTimer(self)
    145          self.timer.register(self._timer_tick)
    146          self.timer.start(10000)
    147
    148 -    def _timer_tick(self, evt):
    149          self._handle_event_alerts()
    150
    151 -    def _set_experimental_features(self):
    152          ExperimentalFeatures().set_active_state_on_all_features_from_config_string(self.config.experimental_features)
    153
    154 -    def _load_icon_bundle(self):
    155          bundle = wx.IconBundle()
    156          for size in ["16", "32", "48"]:
    157              iconpath = os.path.join(ICONS_DIR, "%s.png" % size)
    158              icon = wx.Icon(wx.Bitmap(wx.Image(iconpath)))
    159              bundle.AddIcon(icon)
    160          return bundle
    161
    162      # File Menu action handlers
    163 -    def _create_new_timeline(self, timetype=None):
    164          path = self._get_file_path()
    165          if path is not None:
    166              self.controller.open_timeline(path, timetype)
    167
    168 -    def _create_new_bosparanian_timeline(self):
    169          self._create_new_timeline(BosparanianTimeType())
    170
    171 -    def _create_new_coptic_timeline(self):
    172          self._create_new_timeline(CopticTimeType())
    173
    174 -    def _create_new_pharaonic_timeline(self):
    175          self._create_new_timeline(PharaonicTimeType())
    176
    177 -    def _create_new_numeric_timeline(self):
    178          self._create_new_timeline(NumTimeType())
    179
    180 -    def _get_file_path(self):
    181          path = None
    182          wildcard = self.timeline_wildcard_helper.wildcard_string()
    183          dialog = wx.FileDialog(self, message=_("Create Timeline"),
    184                                 wildcard=wildcard, style=wx.FD_SAVE)
    185          if dialog.ShowModal() == wx.ID_OK:
    186              path = self.timeline_wildcard_helper.get_path(dialog)
    187              if os.path.exists(path):
    188                  msg_first_part = _("The specified timeline already exists.")
    189                  msg_second_part = _("Opening timeline instead of creating new.")
    190                  wx.MessageBox("%s\n\n%s" % (msg_first_part, msg_second_part),
    191                                _("Information"),
    192                                wx.OK | wx.ICON_INFORMATION, self)
    193          dialog.Destroy()
    194          return path
    195
    196 -    def _create_new_dir_timeline(self):
    197          dialog = wx.DirDialog(self, message=_("Create Timeline"))
    198          if dialog.ShowModal() == wx.ID_OK:
    199              self.controller.open_timeline(dialog.GetPath())
    200          dialog.Destroy()
    201
    202 -    def _open_existing_timeline(self):
    203          directory = ""
    204          if self.timeline is not None:
    205              directory = os.path.dirname(self.timeline.path)
    206          wildcard = self.timeline_wildcard_helper.wildcard_string()
    207          dialog = wx.FileDialog(self, message=_("Open Timeline"),
    208                                 defaultDir=directory,
    209                                 wildcard=wildcard, style=wx.FD_OPEN)
    210          if dialog.ShowModal() == wx.ID_OK:
    211              self.controller.open_timeline(dialog.GetPath())
    212          dialog.Destroy()
    213
    214 -    def _save_as(self):
    215          new_timeline_path = self._get_new_timeline_path_from_user()
    216          self._save_timeline_to_new_path(new_timeline_path)
    217
    218 -    def _get_new_timeline_path_from_user(self):
    219          defaultDir = os.path.dirname(self.timeline.path)
    220          wildcard_helper = WildcardHelper(_("Timeline files"), ["timeline"])
    221          wildcard = wildcard_helper.wildcard_string()
    222          style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
    223          message = _("Save Timeline As")
    224          dialog = wx.FileDialog(self, message=message, defaultDir=defaultDir,
    225                                 wildcard=wildcard, style=style)
    226          if dialog.ShowModal() == wx.ID_OK:
    227              new_timeline_path = wildcard_helper.get_path(dialog)
    228          else:
    229              new_timeline_path = None
    230          dialog.Destroy()
    231          return new_timeline_path
    232
    233 -    def _save_timeline_to_new_path(self, new_timeline_path):
    234          if new_timeline_path is not None:
    235              assert new_timeline_path.endswith(".timeline")
    236              export_db_to_timeline_xml(self.timeline, new_timeline_path)
    237              self.controller.open_timeline(new_timeline_path)
    238
    239 -    def _window_on_close(self, event):
    240          self.timer.stop()
    241          self._save_application_config()
    242          try:
    243              if self.ok_to_edit():
    244                  self.save_current_timeline_data()
    245          finally:
    246              self.edit_ends()
    247          self.Destroy()
    248
    249 -    def _save_application_config(self):
    250          self.config.set_window_size(self.GetSize())
    251          self.config.set_window_pos(self.GetPosition())
    252          self.config.window_maximized = self.IsMaximized()
    253          self.config.sidebar_width = self.main_panel.get_sidebar_width()
    254          try:
    255              self.config.write()
    256          except IOError as ex:
    257              friendly = _("Unable to write configuration file.")
    258              msg = "%s\n\n%s" % (friendly, ex_msg(ex))
    259              display_error_message(msg, self)
    260
    261 -    def save_current_timeline_data(self):
    262          if self.timeline:
    263              self.main_panel.save_view_properties(self.timeline)
    264
    265      # Timeline Menu action handlers
    266 -    def _measure_distance_between_events(self):
    267          event1, event2 = self._get_selected_events()
    268          self._display_distance(event1.distance_to(event2))
    269
    270 -    def _get_selected_events(self):
    271          event_id_1, event_id_2 = self.main_panel.get_ids_of_two_first_selected_events()
    272          event1 = self.timeline.find_event_with_id(event_id_1)
    273          event2 = self.timeline.find_event_with_id(event_id_2)
    274          return event1, event2
    275
    276 -    def _display_distance(self, distance):
    277          caption = _("Distance between selected events")
    278          if distance is None:
    279              distance_text = _("Events are overlapping or distance is 0")
    280          else:
    281              distance_text = self.timeline.get_time_type().format_delta(distance)
    282          display_information_message(caption, distance_text)
    283
    284 -    def _set_category(self):
    285          dialog = SetCategoryDialog(self, self.timeline)
    286          dialog.ShowModal()
    287          dialog.Destroy()
    288          self.main_panel.redraw_timeline()
    289
    290 -    def _set_category_to_selected_events(self):
    291          selected_event_ids = self.main_panel.get_selected_event_ids()
    292          dialog = SetCategoryDialog(self, self.timeline, selected_event_ids)
    293          dialog.ShowModal()
    294          dialog.Destroy()
    295
    296 -    def _edit_eras(self):
    297          dialog = ErasEditorDialog(self, self.timeline, self.config)
    298          dialog.ShowModal()
    299          dialog.Destroy()
    300          self.main_panel.redraw_timeline()
    301
    302 -    def _fit_all_events(self):
    303          all_period = self._period_for_all_visible_events()
    304          if all_period is None:
    305              return
    306          if all_period.is_period():
    307              self.main_panel.Navigate(lambda tp: tp.update(all_period.start_time, all_period.end_time))
    308          else:
    309              self.main_panel.Navigate(lambda tp: tp.center(all_period.mean_time()))
    310
    311 -    def _period_for_all_visible_events(self):
    312          try:
    313              visible_events = self._all_visible_events()
    314              if len(visible_events) > 0:
    315                  start = self._first_time(visible_events)
    316                  end = self._last_time(visible_events)
    317                  return TimePeriod(start, end).zoom(-1)
    318              else:
    319                  return None
    320          except ValueError as ex:
    321              display_error_message(str(ex))
    322          return None
    323
    324 -    def _all_visible_events(self):
    325          all_events = self.timeline.get_all_events()
    326          return self.main_panel.get_visible_events(all_events)
    327
    328 -    def _first_time(self, events):
    329          start_time = lambda event: event.get_start_time()
    330          return start_time(min(events, key=start_time))
    331
    332 -    def _last_time(self, events):
    333          end_time = lambda event: event.get_end_time()
    334          return end_time(max(events, key=end_time))
    335
    336 -    def get_export_periods(self):
    337          events = self._all_visible_events()
    338          first_time = self._first_time(events)
    339          last_time = self._last_time(events)
    340          return self.main_panel.get_export_periods(first_time, last_time)
    341
    342      # Timer event handlers
    343 -    def _handle_event_alerts(self):
    344          if self.timeline is None:
    345              return
    346          if self.alert_dialog_open:
    347              return
    348          self._display_events_alerts()
    349          self.alert_dialog_open = False
    350
    351 -    def _display_events_alerts(self):
    352          self.alert_dialog_open = True
    353          all_events = self.timeline.get_all_events()
    354          AlertController(self).display_events_alerts(all_events, self.timeline.get_time_type())
    355