Gnumed.timelinelib.wxgui.dialogs.eventeditor

Package Gnumed :: Package timelinelib :: Package wxgui :: Package dialogs :: Module eventeditor |
[frames] | no frames]

# Source Code for Module Gnumed.timelinelib.wxgui.dialogs.eventeditor

      1  # Copyright (C) 2009, 2010, 2011  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
     22  import webbrowser
     23
     24  from timelinelib.db.exceptions import TimelineIOError
     25  from timelinelib.editors.event import EventEditor
     26  from timelinelib.repositories.dbwrapper import DbWrapperEventRepository
     27  from timelinelib.wxgui.components.categorychoice import CategoryChoice
     28  from timelinelib.wxgui.dialogs.containereditor import ContainerEditorDialog
     29  from timelinelib.wxgui.utils import BORDER
     30  from timelinelib.wxgui.utils import _display_error_message
     31  from timelinelib.wxgui.utils import _set_focus_and_select
     32  from timelinelib.wxgui.utils import time_picker_for
     33  import timelinelib.wxgui.utils as gui_utils
     34
     35
     36 -class EventEditorDialog(wx.Dialog):
     37
     38 -    def __init__(self, parent, config, title, timeline,
     39                   start=None, end=None, event=None):
     40          wx.Dialog.__init__(self, parent, title=title, name="event_editor",
     41                             style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
     42          self.start = start
     43          self.event = event
     44          self.timeline = timeline
     45          self.config = config
     46          self._create_gui()
     47          self.controller = EventEditor(self)
     48          self.controller.edit(timeline.get_time_type(), DbWrapperEventRepository(timeline),
     49                               timeline, start, end, event)
     50
     51 -    def _create_gui(self):
     52          properties_box = self._create_properties_box()
     53          self._create_checkbox_add_more(properties_box)
     54          self._create_buttons(properties_box)
     55          self.SetSizerAndFit(properties_box)
     56
     57 -    def _create_properties_box(self):
     58          properties_box = wx.BoxSizer(wx.VERTICAL)
     59          self._create_propeties_controls(properties_box)
     60          return properties_box
     61
     62 -    def _create_propeties_controls(self, sizer):
     63          groupbox = wx.StaticBox(self, wx.ID_ANY, _("Event Properties"))
     64          main_box_content = wx.StaticBoxSizer(groupbox, wx.VERTICAL)
     65          self._create_detail_content(main_box_content)
     66          self._create_notebook_content(main_box_content)
     67          sizer.Add(main_box_content, flag=wx.EXPAND|wx.ALL,
     68                             border=BORDER, proportion=1)
     69
     70 -    def _create_detail_content(self, properties_box_content):
     71          details = self._create_details()
     72          properties_box_content.Add(details, flag=wx.ALL|wx.EXPAND,
     73                                     border=BORDER)
     74
     75 -    def _create_details(self):
     76          grid = wx.FlexGridSizer(4, 2, BORDER, BORDER)
     77          grid.AddGrowableCol(1)
     78          self._create_time_details(grid)
     79          self._create_checkboxes(grid)
     80          self._create_text_field(grid)
     81          self._create_categories_listbox(grid)
     82          self._create_container_listbox(grid)
     83          return grid
     84
     85 -    def _create_time_details(self, grid):
     86          grid.Add(wx.StaticText(self, label=_("When:")),
     87                   flag=wx.ALIGN_CENTER_VERTICAL)
     88          self.dtp_start = self._create_time_picker()
     89          self.lbl_to = wx.StaticText(self, label=_("to"))
     90          self.dtp_end = self._create_time_picker()
     91          when_box = wx.BoxSizer(wx.HORIZONTAL)
     92          when_box.Add(self.dtp_start, proportion=1)
     93          when_box.AddSpacer(BORDER)
     94          flag = wx.ALIGN_CENTER_VERTICAL|wx.RESERVE_SPACE_EVEN_IF_HIDDEN
     95          when_box.Add(self.lbl_to, flag=flag)
     96          when_box.AddSpacer(BORDER)
     97          when_box.Add(self.dtp_end, proportion=1,
     98                       flag=wx.RESERVE_SPACE_EVEN_IF_HIDDEN)
     99          grid.Add(when_box)
    100
    101 -    def _create_time_picker(self):
    102          time_type = self.timeline.get_time_type()
    103          return time_picker_for(time_type)(self, config=self.config)
    104
    105 -    def _create_checkboxes(self, grid):
    106          grid.AddStretchSpacer()
    107          when_box = wx.BoxSizer(wx.HORIZONTAL)
    108          self.chb_period = self._create_period_checkbox(when_box)
    109          if self.timeline.get_time_type().is_date_time_type():
    110              self.chb_show_time = self._create_show_time_checkbox(when_box)
    111          self.chb_fuzzy = self._create_fuzzy_checkbox(when_box)
    112          self.chb_locked = self._create_locked_checkbox(when_box)
    113          self.chb_ends_today = self._create_ends_today_checkbox(when_box)
    114          grid.Add(when_box)
    115
    116 -    def _create_container_listbox(self, grid):
    117          grid.AddStretchSpacer()
    118          container_box = wx.BoxSizer(wx.HORIZONTAL)
    119          grid.Add(container_box)
    120          self._create_containers_listbox(grid)
    121
    122 -    def _create_containers_listbox(self, grid):
    123          self.lst_containers = wx.Choice(self, wx.ID_ANY)
    124          label = wx.StaticText(self, label=_("Container:"))
    125          grid.Add(label, flag=wx.ALIGN_CENTER_VERTICAL)
    126          grid.Add(self.lst_containers)
    127          self.Bind(wx.EVT_CHOICE, self._lst_containers_on_choice,
    128                    self.lst_containers)
    129
    130 -    def _lst_containers_on_choice(self, e):
    131          new_selection_index = e.GetSelection()
    132          if new_selection_index > self.last_real_container_index:
    133              self.lst_containers.SetSelection(self.current_container_selection)
    134              if new_selection_index == self.add_container_item_index:
    135                  self._add_container()
    136              elif new_selection_index == self.edit_container_item_index:
    137                  self._edit_containers()
    138          else:
    139              self.current_container_selection = new_selection_index
    140          self._enable_disable_checkboxes()
    141
    142 -    def _enable_disable_checkboxes(self):
    143          self._enable_disable_ends_today()
    144          self._enable_disable_locked()
    145
    146 -    def _enable_disable_ends_today(self):
    147          enable = (self._container_not_selected() and
    148                    not self.chb_locked.GetValue() and
    149                    self.controller.start_is_in_history())
    150          self.chb_ends_today.Enable(enable)
    151
    152 -    def _enable_disable_locked(self):
    153          enable = self._container_not_selected()
    154          self.chb_locked.Enable(enable)
    155
    156 -    def _container_not_selected(self):
    157          index = self.lst_containers.GetSelection()
    158          return (index == 0)
    159
    160 -    def _add_container(self):
    161          def create_container_editor():
    162              return ContainerEditorDialog(self, _("Add Container"), self.timeline, None)
    163          def handle_success(dialog):
    164              if dialog.GetReturnCode() == wx.ID_OK:
    165                  try:
    166                      self._fill_containers_listbox(dialog.get_edited_container())
    167                  except TimelineIOError, e:
    168                      gui_utils.handle_db_error_in_dialog(self, e)
    169          gui_utils.show_modal(create_container_editor,
    170                               gui_utils.create_dialog_db_error_handler(self),
    171                               handle_success)
    172
    173 -    def _create_period_checkbox(self, box):
    174          handler = self._chb_period_on_checkbox
    175          return self._create_chb(box, _("Period"), handler)
    176
    177 -    def _chb_period_on_checkbox(self, e):
    178          self._show_to_time(e.IsChecked())
    179
    180 -    def _create_show_time_checkbox(self, box):
    181          handler = self._chb_show_time_on_checkbox
    182          return self._create_chb(box, _("Show time"), handler)
    183
    184 -    def _chb_show_time_on_checkbox(self, e):
    185          self.dtp_start.show_time(e.IsChecked())
    186          self.dtp_end.show_time(e.IsChecked())
    187
    188 -    def _create_fuzzy_checkbox(self, box):
    189          handler = None
    190          return self._create_chb(box, _("Fuzzy"), handler)
    191
    192 -    def _create_locked_checkbox(self, box):
    193          handler = self._chb_show_time_on_locked
    194          return self._create_chb(box, _("Locked"), handler)
    195
    196 -    def _chb_show_time_on_locked(self, e):
    197          self._enable_disable_ends_today()
    198
    199 -    def _create_ends_today_checkbox(self, box):
    200          handler = None
    201          return self._create_chb(box, _("Ends today"), handler)
    202
    203 -    def _create_chb(self, box, label, handler):
    204          chb = wx.CheckBox(self, label=label)
    205          if handler is not None:
    206              self.Bind(wx.EVT_CHECKBOX, handler, chb)
    207          box.Add(chb)
    208          return chb
    209
    210 -    def _create_text_field(self, grid):
    211          self.txt_text = wx.TextCtrl(self, wx.ID_ANY, name="text")
    212          grid.Add(wx.StaticText(self, label=_("Text:")),
    213                   flag=wx.ALIGN_CENTER_VERTICAL)
    214          grid.Add(self.txt_text, flag=wx.EXPAND)
    215
    216 -    def _create_categories_listbox(self, grid):
    217          self.lst_category = CategoryChoice(self, self.timeline)
    218          label = wx.StaticText(self, label=_("Category:"))
    219          grid.Add(label, flag=wx.ALIGN_CENTER_VERTICAL)
    220          grid.Add(self.lst_category)
    221          self.Bind(wx.EVT_CHOICE, self.lst_category.on_choice, self.lst_category)
    222
    223 -    def _create_notebook_content(self, properties_box_content):
    224          notebook = self._create_notebook()
    225          properties_box_content.Add(notebook, border=BORDER,
    226                                     flag=wx.ALL|wx.EXPAND, proportion=1)
    227
    228 -    def _create_notebook(self):
    229          self.event_data = []
    230          notebook = wx.Notebook(self, style=wx.BK_DEFAULT)
    231          for data_id in self.timeline.supported_event_data():
    232              self._add_editor(notebook, data_id)
    233          return notebook
    234
    235 -    def _add_editor(self, notebook, data_id):
    236          editor_class_decription = self._get_editor_class_description(data_id)
    237          if editor_class_decription is None:
    238              return
    239          editor = self._create_editor(notebook, editor_class_decription)
    240          self.event_data.append((data_id, editor))
    241
    242 -    def _get_editor_class_description(self, editor_class_id):
    243          editors = {"description" : (_("Description"), DescriptionEditor),
    244                     "alert" : (_("Alert"), AlertEditor),
    245                     "icon" : (_("Icon"), IconEditor),
    246                     "hyperlink" :  (_("Hyperlink"), HyperlinkEditor),}
    247          if editors.has_key(editor_class_id):
    248              return editors[editor_class_id]
    249          else:
    250              return None
    251
    252 -    def _create_editor(self, notebook, editor_class_decription):
    253          name, editor_class = editor_class_decription
    254          panel = wx.Panel(notebook)
    255          editor = editor_class(panel, self)
    256          notebook.AddPage(panel, name)
    257          sizer = wx.BoxSizer(wx.VERTICAL)
    258          sizer.Add(editor, flag=wx.EXPAND, proportion=1)
    259          panel.SetSizer(sizer)
    260          return editor
    261
    262 -    def _create_checkbox_add_more(self, properties_box):
    263          label = _("Add more events after this one")
    264          self.chb_add_more = wx.CheckBox(self, label=label)
    265          properties_box.Add(self.chb_add_more, flag=wx.ALL, border=BORDER)
    266
    267 -    def _create_buttons(self, properties_box):
    268          button_box = self.CreateStdDialogButtonSizer(wx.OK|wx.CANCEL)
    269          self.Bind(wx.EVT_BUTTON, self._btn_ok_on_click, id=wx.ID_OK)
    270          properties_box.Add(button_box, flag=wx.EXPAND|wx.ALL, border=BORDER)
    271
    272 -    def _btn_ok_on_click(self, evt):
    273          self.controller.create_or_update_event()
    274
    275 -    def _show_to_time(self, show=True):
    276          self.lbl_to.Show(show)
    277          self.dtp_end.Show(show)
    278
    279 -    def _fill_containers_listbox(self, select_container):
    280          # We can not do error handling here since this method is also called
    281          # from the constructor (and then error handling is done by the code
    282          # calling the constructor).
    283          self.lst_containers.Clear()
    284          self.lst_containers.Append("", None) # The None-container
    285          selection_set = False
    286          current_item_index = 1
    287          if select_container != None and select_container not in self.timeline.get_containers():
    288              self.lst_containers.Append(select_container.text, select_container)
    289              self.lst_containers.SetSelection(current_item_index)
    290              current_item_index += 1
    291              selection_set = True
    292          for container in self.timeline.get_containers():
    293              self.lst_containers.Append(container.text, container)
    294              if not selection_set:
    295                  if container == select_container:
    296                      self.lst_containers.SetSelection(current_item_index)
    297                      selection_set = True
    298              current_item_index += 1
    299
    300          self.last_real_container_index = current_item_index - 1
    301          self.add_container_item_index = self.last_real_container_index + 2
    302          self.edit_container_item_index = self.last_real_container_index + 3
    303          self.lst_containers.Append("", None)
    304          self.lst_containers.Append(_("Add new"), None)
    305          if not selection_set:
    306              self.lst_containers.SetSelection(0)
    307          self.current_container_selection = self.lst_containers.GetSelection()
    308          self._enable_disable_checkboxes()
    309
    310 -    def set_start(self, start):
    311          self.dtp_start.set_value(start)
    312 -    def get_start(self):
    313          return self.dtp_start.get_value()
    314
    315 -    def set_end(self, start):
    316          self.dtp_end.set_value(start)
    317 -    def get_end(self):
    318          return self.dtp_end.get_value()
    319
    320 -    def set_show_period(self, show):
    321          self.chb_period.SetValue(show)
    322          self._show_to_time(show)
    323 -    def get_show_period(self):
    324          return self.chb_period.IsChecked()
    325
    326 -    def set_show_time(self, checked):
    327          self.chb_show_time.SetValue(checked)
    328          self.dtp_start.show_time(checked)
    329          self.dtp_end.show_time(checked)
    330
    331 -    def get_fuzzy(self):
    332          return self.chb_fuzzy.GetValue()
    333 -    def set_fuzzy(self, fuzzy):
    334          self.chb_fuzzy.SetValue(fuzzy)
    335
    336 -    def get_locked(self):
    337          return self.chb_locked.GetValue()
    338 -    def set_locked(self, locked):
    339          self.chb_locked.SetValue(locked)
    340          self._enable_disable_ends_today()
    341
    342 -    def get_ends_today(self):
    343          return self.chb_ends_today.GetValue()
    344 -    def set_ends_today(self, value):
    345          self.chb_ends_today.SetValue(value)
    346
    347 -    def set_name(self, name):
    348          self.txt_text.SetValue(name)
    349
    350 -    def get_name(self):
    351          return self.txt_text.GetValue().strip()
    352
    353 -    def set_category(self, category):
    354          self.lst_category.select(category)
    355
    356 -    def get_category(self):
    357          return self.lst_category.get()
    358
    359 -    def set_container(self, container):
    360          self._fill_containers_listbox(container)
    361
    362 -    def get_container(self):
    363          selection = self.lst_containers.GetSelection()
    364          if selection != -1:
    365              container = self.lst_containers.GetClientData(selection)
    366          else:
    367              container = None
    368          return container
    369
    370 -    def set_event_data(self, event_data):
    371          for data_id, editor in self.event_data:
    372              if event_data.has_key(data_id):
    373                  data = event_data[data_id]
    374                  if data is not None:
    375                      editor.set_data(data)
    376
    377 -    def get_event_data(self):
    378          event_data = {}
    379          for data_id, editor in self.event_data:
    380              data = editor.get_data()
    381              if data != None:
    382                  event_data[data_id] = editor.get_data()
    383          return event_data
    384
    385 -    def set_show_add_more(self, visible):
    386          self.chb_add_more.Show(visible)
    387          self.chb_add_more.SetValue(False)
    388 -    def get_show_add_more(self):
    389          return self.chb_add_more.GetValue()
    390
    391 -    def set_focus(self, control_name):
    392          controls = {"start" : self.dtp_start, "text" : self.txt_text}
    393          if controls.has_key(control_name):
    394              controls[control_name].SetFocus()
    395          else:
    396              self.dtp_start.SetFocus()
    397
    398 -    def display_invalid_start(self, message):
    399          self._display_invalid_input(message, self.dtp_start)
    400
    401 -    def display_invalid_end(self, message):
    402          self._display_invalid_input(message, self.dtp_end)
    403
    404 -    def display_invalid_name(self, message):
    405          self._display_invalid_input(message, self.txt_text)
    406
    407 -    def _display_invalid_input(self, message, control):
    408          _display_error_message(message, self)
    409          _set_focus_and_select(control)
    410
    411 -    def display_db_exception(self, e):
    412          gui_utils.handle_db_error_in_dialog(self, e)
    413
    414 -    def display_error_message(self, message):
    415          _display_error_message(message, self)
    416
    417 -    def clear_dialog(self):
    418          self.controller.clear()
    419          for data_id, editor in self.event_data:
    420              editor.clear_data()
    421
    422 -    def close(self):
    423          # TODO: Replace with EventRuntimeData
    424          self.EndModal(wx.ID_OK)
    425
    426
    427 -class DescriptionEditor(wx.TextCtrl):
    428
    429 -    def __init__(self, parent, editor):
    430          wx.TextCtrl.__init__(self, parent, style=wx.TE_MULTILINE)
    431
    432 -    def get_data(self):
    433          description = self.GetValue().strip()
    434          if description != "":
    435              return description
    436          return None
    437
    438 -    def set_data(self, data):
    439          self.SetValue(data)
    440
    441 -    def clear_data(self):
    442          self.SetValue("")
    443
    444
    445 -class IconEditor(wx.Panel):
    446
    447 -    def __init__(self, parent, editor):
    448          wx.Panel.__init__(self, parent)
    449          self.MAX_SIZE = (128, 128)
    450          # Controls
    451          self.img_icon = wx.StaticBitmap(self, size=self.MAX_SIZE)
    452          label = _("Images will be scaled to fit inside a %ix%i box.")
    453          description = wx.StaticText(self, label=label % self.MAX_SIZE)
    454          btn_select = wx.Button(self, wx.ID_OPEN)
    455          btn_clear = wx.Button(self, wx.ID_CLEAR)
    456          self.Bind(wx.EVT_BUTTON, self._btn_select_on_click, btn_select)
    457          self.Bind(wx.EVT_BUTTON, self._btn_clear_on_click, btn_clear)
    458          # Layout
    459          sizer = wx.GridBagSizer(5, 5)
    460          sizer.Add(description, wx.GBPosition(0, 0), wx.GBSpan(1, 2))
    461          sizer.Add(btn_select, wx.GBPosition(1, 0), wx.GBSpan(1, 1))
    462          sizer.Add(btn_clear, wx.GBPosition(1, 1), wx.GBSpan(1, 1))
    463          sizer.Add(self.img_icon, wx.GBPosition(0, 2), wx.GBSpan(2, 1))
    464          self.SetSizerAndFit(sizer)
    465          # Data
    466          self.bmp = None
    467
    468 -    def get_data(self):
    469          return self.get_icon()
    470
    471 -    def set_data(self, data):
    472          self.set_icon(data)
    473
    474 -    def clear_data(self):
    475          self.set_icon(None)
    476
    477 -    def set_icon(self, bmp):
    478          self.bmp = bmp
    479          if self.bmp == None:
    480              self.img_icon.SetBitmap(wx.EmptyBitmap(1, 1))
    481          else:
    482              self.img_icon.SetBitmap(bmp)
    483          self.GetSizer().Layout()
    484
    485 -    def get_icon(self):
    486          return self.bmp
    487
    488 -    def _btn_select_on_click(self, evt):
    489          dialog = wx.FileDialog(self, message=_("Select Icon"),
    490                                 wildcard="*", style=wx.FD_OPEN)
    491          if dialog.ShowModal() == wx.ID_OK:
    492              path = dialog.GetPath()
    493              if os.path.exists(path):
    494                  image = wx.EmptyImage(0, 0)
    495                  success = image.LoadFile(path)
    496                  # LoadFile will show error popup if not successful
    497                  if success:
    498                      # Resize image if too large
    499                      (w, h) = image.GetSize()
    500                      (W, H) = self.MAX_SIZE
    501                      if w > W:
    502                          factor = float(W) / float(w)
    503                          w = w * factor
    504                          h = h * factor
    505                      if h > H:
    506                          factor = float(H) / float(h)
    507                          w = w * factor
    508                          h = h * factor
    509                      image = image.Scale(w, h, wx.IMAGE_QUALITY_HIGH)
    510                      self.set_icon(image.ConvertToBitmap())
    511          dialog.Destroy()
    512
    513 -    def _btn_clear_on_click(self, evt):
    514          self.set_icon(None)
    515
    516
    517 -class AlertEditor(wx.Panel):
    518
    519 -    def __init__(self, parent, editor):
    520          wx.Panel.__init__(self, parent)
    521          self.editor = editor
    522          self._create_gui()
    523          self._initialize_data()
    524
    525 -    def _create_gui(self):
    526          self._create_controls()
    527          self._layout_controls()
    528
    529 -    def _initialize_data(self):
    530          self._set_initial_time()
    531          self._set_initial_text()
    532          self._set_visible(False)
    533
    534 -    def _set_initial_time(self):
    535          if self.editor.event is not None:
    536              self.dtp_start.set_value(self.editor.event.time_period.start_time)
    537          else:
    538              self.dtp_start.set_value(self.editor.start)
    539
    540 -    def _set_initial_text(self):
    541          self.text_data.SetValue("")
    542
    543 -    def _create_controls(self):
    544          self.btn_add = self._create_add_button()
    545          self.btn_clear = self._create_clear_button()
    546          self.url_panel = self._create_input_controls()
    547
    548 -    def _layout_controls(self):
    549          self._layout_input_controls(self.url_panel)
    550          sizer = wx.GridBagSizer(5, 5)
    551          sizer.Add(self.btn_add, wx.GBPosition(0, 0), wx.GBSpan(1, 1))
    552          sizer.Add(self.btn_clear, wx.GBPosition(0, 1), wx.GBSpan(1, 1))
    553          sizer.Add(self.url_panel, wx.GBPosition(1, 0), wx.GBSpan(4, 5))
    554          self.SetSizerAndFit(sizer)
    555
    556 -    def _create_add_button(self):
    557          btn_add = wx.Button(self, wx.ID_ADD)
    558          self.Bind(wx.EVT_BUTTON, self._btn_add_on_click, btn_add)
    559          return btn_add
    560
    561 -    def _create_clear_button(self):
    562          btn_clear = wx.Button(self, wx.ID_CLEAR)
    563          self.Bind(wx.EVT_BUTTON, self._btn_clear_on_click, btn_clear)
    564          return btn_clear
    565
    566 -    def _create_input_controls(self):
    567          alert_panel = wx.Panel(self)
    568          time_type = self.editor.timeline.get_time_type()
    569          self.dtp_start =  time_picker_for(time_type)(alert_panel, config=self.editor.config)
    570          self.text_data = wx.TextCtrl(alert_panel, size=(300,80), style=wx.TE_MULTILINE)
    571          return alert_panel
    572
    573 -    def _layout_input_controls(self, alert_panel):
    574          when = wx.StaticText(alert_panel, label=_("When:"))
    575          text = wx.StaticText(alert_panel, label=_("Text:"))
    576          sizer = wx.GridBagSizer(5, 10)
    577          sizer.Add(when, wx.GBPosition(0, 0), wx.GBSpan(1, 1))
    578          sizer.Add(self.dtp_start, wx.GBPosition(0, 1), wx.GBSpan(1, 3))
    579          sizer.Add(text, wx.GBPosition(1, 0), wx.GBSpan(1, 1))
    580          sizer.Add(self.text_data, wx.GBPosition(1, 1), wx.GBSpan(1, 9))
    581          alert_panel.SetSizerAndFit(sizer)
    582
    583 -    def get_data(self):
    584          if self.url_visible:
    585              time = self.dtp_start.get_value()
    586              text = self.text_data.GetValue()
    587              return (time, text)
    588          else:
    589              return None
    590
    591 -    def set_data(self, data):
    592          if data == None:
    593              self._set_visible(False)
    594          else:
    595              self._set_visible(True)
    596              time, text = data
    597              self.dtp_start.set_value(time)
    598              self.text_data.SetValue(text)
    599
    600 -    def _btn_add_on_click(self, evt):
    601          self._set_visible(True)
    602
    603 -    def _btn_clear_on_click(self, evt):
    604          self.clear_data()
    605
    606 -    def clear_data(self):
    607          self._set_initial_time()
    608          self._set_initial_text()
    609          self._set_visible(False)
    610
    611 -    def _set_visible(self, value):
    612          self.url_visible = value
    613          self.url_panel.Show(self.url_visible)
    614          self.btn_add.Enable(not value)
    615          self.btn_clear.Enable(value)
    616          self.GetSizer().Layout()
    617
    618
    619 -class HyperlinkEditor(wx.Panel):
    620
    621 -    def __init__(self, parent, editor):
    622          wx.Panel.__init__(self, parent)
    623          self.editor = editor
    624          self._create_gui()
    625          self._initialize_data()
    626
    627 -    def _create_gui(self):
    628          self._create_controls()
    629          self._layout_controls()
    630
    631 -    def _initialize_data(self):
    632          self._set_initial_text()
    633          self._set_visible(False)
    634
    635 -    def _set_initial_text(self):
    636          self.text_data.SetValue("")
    637
    638 -    def _create_controls(self):
    639          self.btn_add = self._create_add_button()
    640          self.btn_clear = self._create_clear_button()
    641          self.btn_test = self._create_test_button()
    642          self.url_panel = self._create_input_controls()
    643
    644 -    def _layout_controls(self):
    645          self._layout_input_controls(self.url_panel)
    646          sizer = wx.GridBagSizer(5, 5)
    647          sizer.Add(self.btn_add, wx.GBPosition(0, 0), wx.GBSpan(1, 1))
    648          sizer.Add(self.btn_clear, wx.GBPosition(0, 1), wx.GBSpan(1, 1))
    649          sizer.Add(self.btn_test, wx.GBPosition(0, 2), wx.GBSpan(1, 1))
    650          sizer.Add(self.url_panel, wx.GBPosition(1, 0), wx.GBSpan(4, 5))
    651          self.SetSizerAndFit(sizer)
    652
    653 -    def _create_add_button(self):
    654          btn_add = wx.Button(self, wx.ID_ADD)
    655          self.Bind(wx.EVT_BUTTON, self._btn_add_on_click, btn_add)
    656          return btn_add
    657
    658 -    def _create_clear_button(self):
    659          btn_clear = wx.Button(self, wx.ID_CLEAR)
    660          self.Bind(wx.EVT_BUTTON, self._btn_clear_on_click, btn_clear)
    661          return btn_clear
    662
    663 -    def _create_test_button(self):
    664          btn_test = wx.Button(self, wx.ID_ANY, _("Test"))
    665          self.Bind(wx.EVT_BUTTON, self._btn_test_on_click, btn_test)
    666          return btn_test
    667
    668 -    def _create_input_controls(self):
    669          alert_panel = wx.Panel(self)
    670          self.text_data = wx.TextCtrl(alert_panel, size=(300,20))
    671          return alert_panel
    672
    673 -    def _layout_input_controls(self, alert_panel):
    674          text = wx.StaticText(alert_panel, label=_("URL:"))
    675          sizer = wx.GridBagSizer(5, 10)
    676          sizer.Add(text, wx.GBPosition(1, 0), wx.GBSpan(1, 1))
    677          sizer.Add(self.text_data, wx.GBPosition(1, 1), wx.GBSpan(1, 9))
    678          alert_panel.SetSizerAndFit(sizer)
    679
    680 -    def get_data(self):
    681          if self.url_visible:
    682              return self.text_data.GetValue()
    683          else:
    684              return None
    685
    686 -    def set_data(self, data):
    687          if data == None:
    688              self._set_visible(False)
    689          else:
    690              self._set_visible(True)
    691              self.text_data.SetValue(data)
    692
    693 -    def _btn_add_on_click(self, evt):
    694          self._set_visible(True)
    695
    696 -    def _btn_clear_on_click(self, evt):
    697          self.clear_data()
    698
    699 -    def _btn_test_on_click(self, evt):
    700          webbrowser.open(self.get_data())
    701
    702 -    def clear_data(self):
    703          self._set_initial_text()
    704          self._set_visible(False)
    705
    706 -    def _set_visible(self, value):
    707          self.url_visible = value
    708          self.url_panel.Show(self.url_visible)
    709          self.btn_add.Enable(not value)
    710          self.btn_clear.Enable(value)
    711          self.btn_test.Enable(value)
    712          self.GetSizer().Layout()
    713
    714
    715 -def open_event_editor_for(parent, config, db, handle_db_error, event):
    716      def create_event_editor():
    717          if event.is_container():
    718              title = _("Edit Container")
    719              return ContainerEditorDialog(parent, title, db, event)
    720          else:
    721              return EventEditorDialog(
    722                  parent, config, _("Edit Event"), db, event=event)
    723      gui_utils.show_modal(create_event_editor, handle_db_error)
    724
    725
    726 -def open_create_event_editor(parent, config, db, handle_db_error, start=None, end=None):
    727      def create_event_editor():
    728          return EventEditorDialog(
    729              parent, config, _("Create Event"), db, start, end)
    730      gui_utils.show_modal(create_event_editor, handle_db_error)
    731