Gnumed.wxpython.gmExportAreaWidgets

Package Gnumed :: Package wxpython :: Module gmExportAreaWidgets |
[frames] | no frames]

# Source Code for Module Gnumed.wxpython.gmExportAreaWidgets

       1  """GNUmed patient export area widgets."""
       2  #================================================================
       3  __author__ = "Karsten.Hilbert@gmx.net"
       4  __license__ = "GPL v2 or later"
       5
       6  # std lib
       7  import sys
       8  import logging
       9  import os.path
      10  import shutil
      11
      12
      13  # 3rd party
      14  import wx
      15
      16
      17  # GNUmed libs
      18  if __name__ == '__main__':
      19          sys.path.insert(0, '../../')
      20
      21  from Gnumed.pycommon import gmDispatcher
      22  from Gnumed.pycommon import gmTools
      23  from Gnumed.pycommon import gmMimeLib
      24  from Gnumed.pycommon import gmDateTime
      25  from Gnumed.pycommon import gmPrinting
      26  from Gnumed.pycommon import gmShellAPI
      27  from Gnumed.pycommon import gmNetworkTools
      28  from Gnumed.pycommon import gmCfg2
      29  from Gnumed.pycommon import gmLog2
      30
      31  from Gnumed.business import gmPerson
      32  from Gnumed.business import gmExportArea
      33  from Gnumed.business import gmPraxis
      34
      35  from Gnumed.wxpython import gmRegetMixin
      36  from Gnumed.wxpython import gmGuiHelpers
      37  from Gnumed.wxpython import gmDocumentWidgets
      38
      39
      40  _log = logging.getLogger('gm.ui')
      41  _cfg = gmCfg2.gmCfgData()
      42
      43  #============================================================
      44 -def _add_file_to_export_area(**kwargs):
      45          try:
      46                  del kwargs['signal']
      47                  del kwargs['sender']
      48          except KeyError:
      49                  pass
      50          wx.CallAfter(add_file_to_export_area, **kwargs)
      51
      52 -def _add_files_to_export_area(**kwargs):
      53          try:
      54                  del kwargs['signal']
      55                  del kwargs['sender']
      56          except KeyError:
      57                  pass
      58          wx.CallAfter(add_files_to_export_area, **kwargs)
      59
      60  #----------------------
      61 -def add_file_to_export_area(parent=None, filename=None, hint=None, unlock_patient=False):
      62          return add_files_to_export_area (
      63                  parent = parent,
      64                  filenames = [filename],
      65                  hint = hint,
      66                  unlock_patient = unlock_patient
      67          )
      68
      69  #----------------------
      70 -def add_files_to_export_area(parent=None, filenames=None, hint=None, unlock_patient=False):
      71          pat = gmPerson.gmCurrentPatient()
      72          if not pat.connected:
      73                  gmDispatcher.send(signal = 'statustext', msg = _('Cannot add files to export area. No patient.'), beep = True)
      74                  return False
      75
      76          wx.BeginBusyCursor()
      77          if parent is None:
      78                  parent = wx.GetApp().GetTopWindow()
      79          if not pat.export_area.add_files(filenames = filenames, hint = hint):
      80                  wx.EndBusyCursor()
      81                  gmGuiHelpers.gm_show_error (
      82                          aMessage = _('Cannot import files into export area.'),
      83                          aTitle = _('Export area')
      84                  )
      85                  return False
      86
      87          wx.EndBusyCursor()
      88          # remove non-temp files
      89          tmp_dir = gmTools.gmPaths().tmp_dir
      90          files2remove = [ f for f in filenames if not f.startswith(tmp_dir) ]
      91          if len(files2remove) > 0:
      92                  do_delete = gmGuiHelpers.gm_show_question (
      93                          _(      'Successfully imported files into export area.\n'
      94                                  '\n'
      95                                  'Do you want to delete imported files from the filesystem ?\n'
      96                                  '\n'
      97                                  ' %s'
      98                          ) % '\n '.join(files2remove),
      99                          _('Removing files')
     100                  )
     101                  if do_delete:
     102                          for fname in files2remove:
     103                                  gmTools.remove_file(fname)
     104          else:
     105                  gmDispatcher.send(signal = 'statustext', msg = _('Imported files into export area.'), beep = True)
     106          return True
     107
     108          #if unlock_patient:
     109          #       pat.locked = False
     110
     111  #----------------------
     112  gmDispatcher.connect(signal = 'add_file_to_export_area', receiver = _add_file_to_export_area)
     113  gmDispatcher.connect(signal = 'add_files_to_export_area', receiver = _add_files_to_export_area)
     114
     115  #============================================================
     116  from Gnumed.wxGladeWidgets import wxgExportAreaExportToMediaDlg
     117
     118 -class cExportAreaExportToMediaDlg(wxgExportAreaExportToMediaDlg.wxgExportAreaExportToMediaDlg):
     119
     120 -        def __init__(self, *args, **kwargs):
     121                  self.__patient = kwargs['patient']
     122                  del kwargs['patient']
     123                  self.__item_count = kwargs['item_count']
     124                  del kwargs['item_count']
     125                  super().__init__(*args, **kwargs)
     126
     127                  self.__init_ui()
     128
     129          #--------------------------------------------------------
     130          # event handling
     131          #--------------------------------------------------------
     132 -        def _on_reload_media_list_button_pressed(self, event):
     133                  event.Skip()
     134                  wx.BeginBusyCursor()
     135                  try:
     136                          self.__update_media_list()
     137                  finally:
     138                          wx.EndBusyCursor()
     139
     140          #--------------------------------------------------------
     141 -        def _on_media_selected(self, event):
     142                  self.__update_ui_state()
     143
     144          #--------------------------------------------------------
     145 -        def _on_media_deselected(self, event):
     146                  self.__update_ui_state()
     147
     148          #--------------------------------------------------------
     149 -        def _on_use_subdirectory_toggled(self, event):
     150                  event.Skip()
     151                  self.__update_ui_state()
     152
     153          #--------------------------------------------------------
     154 -        def _on_open_directory_button_pressed(self, event):
     155                  event.Skip()
     156                  path = self.__calc_path()
     157                  if not os.path.isdir(path):
     158                          return
     159                  gmMimeLib.call_viewer_on_file(path, block = False)
     160
     161          #--------------------------------------------------------
     162 -        def _on_clear_directory_button_pressed(self, event):
     163                  event.Skip()
     164                  path = self.__calc_path()
     165                  if not os.path.isdir(path):
     166                          return
     167
     168                  delete_data = gmGuiHelpers.gm_show_question (
     169                          title = _('Clearing out a directory'),
     170                          question = _(
     171                                  'Do you really want to delete all existing data\n'
     172                                  'from the following directory ?\n'
     173                                  '\n'
     174                                  ' %s\n'
     175                                  '\n'
     176                                  'Note, this can NOT be reversed without backups.'
     177                          ) % path,
     178                          cancel_button = False
     179                  )
     180                  if delete_data:
     181                          gmTools.rm_dir_content(path)
     182                          self.__update_ui_state()
     183
     184          #--------------------------------------------------------
     185 -        def _on_save2media_button_pressed(self, event):
     186                  event.Skip()
     187
     188                  path = self.__calc_path()
     189                  if path is None:
     190                          return                  # no media selected, should not happen
     191                  if path == -2:          # not mounted, should not happen
     192                          return
     193                  if path == -1:          # optical drive
     194                          if self.__burn_script is None:
     195                                  return          # no burn script, should not happen
     196
     197                  self.EndModal(wx.ID_SAVE)
     198
     199          #--------------------------------------------------------
     200          # internal API
     201          #--------------------------------------------------------
     202 -        def __init_ui(self):
     203                  msg = _(
     204                          '\n'
     205                          'Number of entries to export: %s\n'
     206                          '\n'
     207                          'Patient: %s\n'
     208                          '\n'
     209                          'Select the media to export onto below.\n'
     210                  ) % (
     211                          self.__item_count,
     212                          self.__patient['description_gender']
     213                  )
     214                  self._LBL_header.Label = msg
     215                  self._LCTRL_removable_media.set_columns([_('Type'), _('Medium'), _('Details')])
     216                  self._LCTRL_removable_media.set_column_widths([wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE])
     217                  self._LCTRL_removable_media.set_resize_column()
     218                  self._LCTRL_removable_media.select_callback = self._on_media_selected
     219                  self._LCTRL_removable_media.deselect_callback = self._on_media_deselected
     220
     221                  self.__update_media_list()
     222                  self.__update_ui_state()
     223
     224          #--------------------------------------------------------
     225 -        def __calc_path(self):
     226                  media = self._LCTRL_removable_media.get_selected_item_data(only_one = True)
     227                  if media is None:
     228                          return None
     229
     230                  if media['type'] == 'cd':
     231                          return -1
     232
     233                  if media['is_mounted'] is False:
     234                          return -2
     235
     236                  mnt_path = media['mountpoint']
     237                  if self._CHBOX_use_subdirectory.IsChecked():
     238                          return os.path.join(mnt_path, self.__patient.subdir_name)
     239
     240                  return mnt_path
     241
     242          #--------------------------------------------------------
     243 -        def __update_media_list(self):
     244
     245                  self._LCTRL_removable_media.remove_items_safely()
     246                  items = []
     247                  data = []
     248
     249                  found, self.__burn_script = gmShellAPI.detect_external_binary('gm-burn_doc')
     250
     251                  # USB / MMC drives
     252                  removable_partitions = gmTools.enumerate_removable_partitions()
     253                  for key in removable_partitions:
     254                          part = removable_partitions[key]
     255                          if part['is_mounted'] is False:
     256                                  continue
     257                          items.append ([
     258                                  part['bus'].upper(),
     259                                  _('%s (%s %s) - %s free') % (
     260                                          part['fs_label'],
     261                                          part['vendor'],
     262                                          part['model'],
     263                                          gmTools.size2str(part['bytes_free'])
     264                                  ),
     265                                  _('%s (%s): %s in %s on %s') % (
     266                                          part['mountpoint'],
     267                                          gmTools.size2str(part['size_in_bytes']),
     268                                          part['fs_type'],
     269                                          part['partition'],
     270                                          part['device']
     271                                  )
     272                          ])
     273                          data.append(part)
     274                  for key in removable_partitions:
     275                          part = removable_partitions[key]
     276                          if part['is_mounted'] is True:
     277                                  continue
     278                          items.append ([
     279                                  part['bus'].upper(),
     280                                  '%s (%s %s)' % (
     281                                          part['fs_label'],
     282                                          part['vendor'],
     283                                          part['model']
     284                                  ),
     285                                  _('%s on %s, not mounted') % (
     286                                          part['partition'],
     287                                          part['device']
     288                                  )
     289                          ])
     290                          data.append(part)
     291
     292                  # optical drives: CD/DVD/BD
     293                  optical_writers = gmTools.enumerate_optical_writers()
     294                  for cdrw in optical_writers:
     295                          items.append ([
     296                                  cdrw['type'].upper(),
     297                                  cdrw['model'],
     298                                  cdrw['device']
     299                          ])
     300                          data.append(cdrw)
     301
     302                  self._LCTRL_removable_media.set_string_items(items)
     303                  self._LCTRL_removable_media.set_data(data)
     304                  self._LCTRL_removable_media.set_column_widths()
     305
     306                  self._BTN_save2media.Disable()
     307
     308          #--------------------------------------------------------
     309 -        def __update_ui_state(self):
     310
     311                  media = self._LCTRL_removable_media.get_selected_item_data(only_one = True)
     312                  if media is None:
     313                          self._BTN_save2media.Disable()
     314                          self._BTN_open_directory.Disable()
     315                          self._BTN_clear_directory.Disable()
     316                          self._CHBOX_encrypt.Disable()
     317                          self._CHBOX_use_subdirectory.Disable()
     318                          self._LBL_directory.Label = ''
     319                          self._LBL_dir_is_empty.Label = ''
     320                          return
     321
     322                  if media['type'] == 'cd':
     323                          self._BTN_open_directory.Disable()
     324                          self._BTN_clear_directory.Disable()
     325                          self._LBL_directory.Label = ''
     326                          if self.__burn_script is None:
     327                                  self._BTN_save2media.Disable()
     328                                  self._CHBOX_use_subdirectory.Disable()
     329                                  self._CHBOX_encrypt.Disable()
     330                                  self._LBL_dir_is_empty.Label = _('helper <gm-burn_doc(.bat)> not found')
     331                          else:
     332                                  self._BTN_save2media.Enable()
     333                                  self._CHBOX_use_subdirectory.Enable()
     334                                  self._CHBOX_encrypt.Enable()
     335                                  self._LBL_dir_is_empty.Label = ''
     336                          return
     337
     338                  if media['is_mounted'] is False:
     339                          self._BTN_save2media.Disable()
     340                          self._BTN_open_directory.Disable()
     341                          self._BTN_clear_directory.Disable()
     342                          self._CHBOX_use_subdirectory.Disable()
     343                          self._CHBOX_encrypt.Disable()
     344                          self._LBL_directory.Label = ''
     345                          self._LBL_dir_is_empty.Label = _('media not mounted')
     346                          return
     347
     348                  self._BTN_save2media.Enable()
     349                  self._CHBOX_use_subdirectory.Enable()
     350                  self._CHBOX_encrypt.Enable()
     351
     352                  path = self.__calc_path()
     353                  self._LBL_directory.Label = path + os.sep
     354                  is_empty = gmTools.dir_is_empty(directory = path)
     355                  if is_empty is True:
     356                          self._LBL_dir_is_empty.Label = ''
     357                          self._BTN_open_directory.Enable()
     358                          self._BTN_clear_directory.Disable()
     359                  elif is_empty is False:
     360                          self._LBL_dir_is_empty.Label = _('directory contains data')
     361                          self._BTN_open_directory.Enable()
     362                          self._BTN_clear_directory.Enable()
     363                  else:   # we don't know, say, use_subdir and subdir does not yet exist
     364                          self._LBL_dir_is_empty.Label = ''
     365                          self._BTN_open_directory.Disable()
     366                          self._BTN_clear_directory.Disable()
     367
     368  #============================================================
     369  from Gnumed.wxGladeWidgets import wxgExportAreaSaveAsDlg
     370
     371 -class cExportAreaSaveAsDlg(wxgExportAreaSaveAsDlg.wxgExportAreaSaveAsDlg):
     372
     373 -        def __init__(self, *args, **kwargs):
     374                  self.__patient = kwargs['patient']
     375                  del kwargs['patient']
     376                  self.__item_count = kwargs['item_count']
     377                  del kwargs['item_count']
     378                  super().__init__(*args, **kwargs)
     379
     380                  self.__init_ui()
     381
     382          #--------------------------------------------------------
     383          # event handlers
     384          #--------------------------------------------------------
     385 -        def _on_use_subdirectory_toggled(self, event):
     386                  event.Skip()
     387                  self.__update_ui_state()
     388
     389          #--------------------------------------------------------
     390 -        def _on_save_as_encrypted_toggled(self, event):
     391                  event.Skip()
     392                  self.__update_ui_state()
     393
     394          #--------------------------------------------------------
     395 -        def _on_select_directory_button_pressed(self, event):
     396                  event.Skip()
     397                  curr_path = self._LBL_directory.Label.rstrip(os.sep).rstrip('/')
     398                  if not os.path.isdir(curr_path):
     399                          curr_path = os.path.join(gmTools.gmPaths().home_dir, 'gnumed')
     400                  msg = _('Select directory where to save the archive or files.')
     401                  dlg = wx.DirDialog(self, message = msg, defaultPath = curr_path, style = wx.DD_DEFAULT_STYLE)# | wx.DD_DIR_MUST_EXIST)
     402                  choice = dlg.ShowModal()
     403                  selected_path = dlg.GetPath().rstrip(os.sep).rstrip('/')
     404                  dlg.DestroyLater()
     405                  if choice != wx.ID_OK:
     406                          return
     407
     408                  self._LBL_directory.Label = selected_path + os.sep
     409                  self.__update_ui_state()
     410
     411          #--------------------------------------------------------
     412 -        def _on_open_directory_button_pressed(self, event):
     413                  event.Skip()
     414                  path = self._LBL_directory.Label.strip().rstrip(os.sep).rstrip('/')
     415                  if not os.path.isdir(path):
     416                          return
     417                  gmMimeLib.call_viewer_on_file(path, block = False)
     418
     419          #--------------------------------------------------------
     420 -        def _on_clear_directory_button_pressed(self, event):
     421                  event.Skip()
     422                  path = self._LBL_directory.Label.strip().rstrip(os.sep).rstrip('/')
     423                  if not os.path.isdir(path):
     424                          return
     425                  delete_data = gmGuiHelpers.gm_show_question (
     426                          title = _('Clearing out a directory'),
     427                          question = _(
     428                                  'Do you really want to delete all existing data\n'
     429                                  'from the following directory ?\n'
     430                                  '\n'
     431                                  ' %s\n'
     432                                  '\n'
     433                                  'Note, this can NOT be reversed without backups.'
     434                          ) % path,
     435                          cancel_button = False
     436                  )
     437                  if delete_data:
     438                          gmTools.rm_dir_content(path)
     439                          self.__update_ui_state()
     440
     441          #--------------------------------------------------------
     442 -        def _on_save_archive_button_pressed(self, event):
     443                  event.Skip()
     444                  self.EndModal(wx.ID_SAVE)
     445
     446          #--------------------------------------------------------
     447          # internal API
     448          #--------------------------------------------------------
     449 -        def __init_ui(self):
     450                  msg = ('\n' + _('Number of entries to save: %s') + '\n\n' + _('Patient: %s') + '\n') % (
     451                          self.__item_count,
     452                          self.__patient['description_gender']
     453                  )
     454                  self._LBL_header.Label = msg
     455                  self._LBL_directory.Label = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', self.__patient.subdir_name) + os.sep
     456                  self.__update_ui_state()
     457
     458          #--------------------------------------------------------
     459 -        def __update_ui_state(self):
     460                  subdir_name = self.__patient.subdir_name
     461                  path = self._LBL_directory.Label.strip().rstrip(os.sep).rstrip('/')
     462                  if self._CHBOX_use_subdirectory.IsChecked():
     463                          # add subdir if needed
     464                          if not path.endswith(subdir_name):
     465                                  path = os.path.join(path, subdir_name)
     466                                  self._LBL_directory.Label = path + os.sep
     467                  else:
     468                          # remove subdir if there
     469                          if path.endswith(subdir_name):
     470                                  path = path[:-len(subdir_name)].rstrip(os.sep).rstrip('/')
     471                                  self._LBL_directory.Label = path + os.sep
     472
     473                  if self._CHBOX_encrypt.IsChecked():
     474                          self._CHBOX_convert2pdf.Enable()
     475                  else:
     476                          self._CHBOX_convert2pdf.Disable()
     477
     478                  is_empty = gmTools.dir_is_empty(directory = path)
     479                  if is_empty is True:
     480                          self._LBL_dir_is_empty.Label = ''
     481                          self._BTN_open_directory.Disable()
     482                          self._BTN_clear_directory.Disable()
     483                          self._BTN_save_files.Enable()
     484                          self._BTN_save_archive.Enable()
     485                  elif is_empty is False:
     486                          self._LBL_dir_is_empty.Label = _('directory contains data')
     487                          self._BTN_open_directory.Enable()
     488                          self._BTN_clear_directory.Enable()
     489                          self._BTN_save_files.Disable()
     490                          self._BTN_save_archive.Disable()
     491                  else:   # we don't know, say, use_subdir and subdir does not yet exist
     492                          self._LBL_dir_is_empty.Label = ''
     493                          self._BTN_open_directory.Disable()
     494                          self._BTN_clear_directory.Disable()
     495                          self._BTN_save_files.Enable()
     496                          self._BTN_save_archive.Enable()
     497
     498                  self.Layout()
     499
     500  #============================================================
     501  from Gnumed.wxGladeWidgets import wxgExportAreaPluginPnl
     502
     503 -class cExportAreaPluginPnl(wxgExportAreaPluginPnl.wxgExportAreaPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
     504          """Panel holding a number of items for further processing.
     505
     506          Acts on the current patient.
     507
     508          Used as notebook page."""
     509 -        def __init__(self, *args, **kwargs):
     510                  wxgExportAreaPluginPnl.wxgExportAreaPluginPnl.__init__(self, *args, **kwargs)
     511                  gmRegetMixin.cRegetOnPaintMixin.__init__(self)
     512                  self.__init_ui()
     513                  self.__register_interests()
     514
     515          #--------------------------------------------------------
     516          # event handling
     517          #--------------------------------------------------------
     518 -        def __register_interests(self):
     519                  gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
     520  #               gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
     521                  gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_table_mod)
     522
     523          #--------------------------------------------------------
     524 -        def _on_pre_patient_unselection(self):
     525                  self._LCTRL_items.set_string_items([])
     526
     527          #--------------------------------------------------------
     528 -        def _on_table_mod(self, *args, **kwargs):
     529                  if kwargs['table'] != 'clin.export_item':
     530                          return
     531                  pat = gmPerson.gmCurrentPatient()
     532                  if not pat.connected:
     533                          return
     534                  if kwargs['pk_identity'] != pat.ID:
     535                          return
     536                  self._schedule_data_reget()
     537
     538          #--------------------------------------------------------
     539 -        def _on_list_item_selected(self, event):
     540                  event.Skip()
     541
     542          #--------------------------------------------------------
     543 -        def _on_show_item_button_pressed(self, event):
     544                  event.Skip()
     545                  item = self._LCTRL_items.get_selected_item_data(only_one = True)
     546                  if item is None:
     547                          return
     548                  item.display_via_mime(block = False)
     549
     550          #--------------------------------------------------------
     551 -        def _on_add_items_button_pressed(self, event):
     552                  event.Skip()
     553                  dlg = wx.FileDialog (
     554                          parent = self,
     555                          message = _("Select files to add to the export area"),
     556                          defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
     557                          style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE | wx.FD_PREVIEW
     558                  )
     559                  choice = dlg.ShowModal()
     560                  fnames = dlg.GetPaths()
     561                  dlg.DestroyLater()
     562                  if choice != wx.ID_OK:
     563                          return
     564                  if not gmPerson.gmCurrentPatient().export_area.add_files(fnames):
     565                          gmGuiHelpers.gm_show_error (
     566                                  title = _('Adding files to export area'),
     567                                  error = _('Cannot add (some of) the following files to the export area:\n%s ') % '\n '.join(fnames)
     568                          )
     569
     570          #--------------------------------------------------------
     571 -        def _on_add_directory_button_pressed(self, event):
     572                  event.Skip()
     573                  dlg = wx.DirDialog (
     574                          parent = self,
     575                          message = _("Select directory to add to the export area"),
     576                          defaultPath = os.path.expanduser(os.path.join('~', 'gnumed')),
     577                          style = wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST
     578                  )
     579                  choice = dlg.ShowModal()
     580                  path = dlg.GetPath()
     581                  dlg.DestroyLater()
     582                  if choice != wx.ID_OK:
     583                          return
     584                  if not gmPerson.gmCurrentPatient().export_area.add_path(path):
     585                          gmGuiHelpers.gm_show_error (
     586                                  title = _('Adding path to export area'),
     587                                  error = _('Cannot add the following path to the export area:\n%s ') % path
     588                          )
     589
     590          #--------------------------------------------------------
     591 -        def _on_add_from_archive_button_pressed(self, event):
     592                  event.Skip()
     593                  selected_docs = gmDocumentWidgets.manage_documents (
     594                          parent = self,
     595                          msg = _('Select the documents to be put into the export area:'),
     596                          single_selection = False
     597                  )
     598                  if selected_docs is None:
     599                          return
     600                  gmPerson.gmCurrentPatient().export_area.add_documents(documents = selected_docs)
     601
     602          #--------------------------------------------------------
     603 -        def _on_clipboard_items_button_pressed(self, event):
     604                  event.Skip()
     605                  clip = gmGuiHelpers.clipboard2file(check_for_filename = True)
     606                  if clip is None:
     607                          return
     608                  if clip is False:
     609                          return
     610                  if not gmPerson.gmCurrentPatient().export_area.add_file(filename = clip, hint = _('clipboard')):
     611                          gmGuiHelpers.gm_show_error (
     612                                  title = _('Loading clipboard item (saved to file) into export area'),
     613                                  error = _('Cannot add the following clip to the export area:\n%s ') % clip
     614                          )
     615
     616          #--------------------------------------------------------
     617 -        def _on_scan_items_button_pressed(self, event):
     618                  event.Skip()
     619                  scans = gmDocumentWidgets.acquire_images_from_capture_device(calling_window = self)
     620                  if scans is None:
     621                          return
     622
     623                  if not gmPerson.gmCurrentPatient().export_area.add_files(scans, _('scan')):
     624                          gmGuiHelpers.gm_show_error (
     625                                  title = _('Scanning files into export area'),
     626                                  error = _('Cannot add (some of) the following scans to the export area:\n%s ') % '\n '.join(fnames)
     627                          )
     628
     629          #--------------------------------------------------------
     630 -        def _on_remove_items_button_pressed(self, event):
     631                  event.Skip()
     632                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     633                  if len(items) == 0:
     634                          return
     635                  really_delete = gmGuiHelpers.gm_show_question (
     636                          title = _('Deleting document from export area.'),
     637                          question = _('Really remove %s selected document(s)\nfrom the patient export area ?') % len(items)
     638                  )
     639                  if not really_delete:
     640                          return
     641                  for item in items:
     642                          gmPerson.gmCurrentPatient().export_area.remove_item(item)
     643
     644          #--------------------------------------------------------
     645 -        def _on_print_items_button_pressed(self, event):
     646                  event.Skip()
     647                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     648                  if len(items) == 0:
     649                          return
     650
     651                  files2print = []
     652                  for item in items:
     653                          files2print.append(item.save_to_file())
     654
     655                  if len(files2print) == 0:
     656                          return
     657
     658                  jobtype = 'export_area'
     659                  printed = gmPrinting.print_files(filenames = files2print, jobtype = jobtype, verbose = _cfg.get(option = 'debug'))
     660                  if not printed:
     661                          gmGuiHelpers.gm_show_error (
     662                                  aMessage = _('Error printing documents.'),
     663                                  aTitle = _('Printing [%s]') % jobtype
     664                          )
     665                          return False
     666
     667                  self.__save_soap_note(soap = _('Printed:\n - %s') % '\n - '.join([ i['description'] for i in items ]))
     668                  return True
     669
     670          #--------------------------------------------------------
     671 -        def _on_remote_print_button_pressed(self, event):
     672                  event.Skip()
     673                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     674                  if len(items) == 0:
     675                          return
     676                  for item in items:
     677                          item.is_print_job = True
     678                  gmDispatcher.send(signal = 'statustext', msg = _('Item(s) moved to Print Center.'))
     679
     680          #--------------------------------------------------------
     681 -        def _on_save_items_button_pressed(self, event):
     682                  event.Skip()
     683
     684                  items = self.__get_items_to_work_on(_('Saving entries'))
     685                  if items is None:
     686                          return
     687
     688                  pat = gmPerson.gmCurrentPatient()
     689                  dlg = cExportAreaSaveAsDlg(self, -1, patient = pat, item_count = len(items))
     690                  choice = dlg.ShowModal()
     691                  path = dlg._LBL_directory.Label
     692                  generate_metadata = dlg._CHBOX_generate_metadata.IsChecked()
     693                  use_subdir = dlg._CHBOX_use_subdirectory.IsChecked()
     694                  encrypt = dlg._CHBOX_encrypt.IsChecked()
     695                  convert2pdf = dlg._CHBOX_convert2pdf.IsChecked()
     696                  dlg.DestroyLater()
     697                  if choice == wx.ID_CANCEL:
     698                          return
     699
     700                  if choice == wx.ID_SAVE:
     701                          create_archive = True
     702                  elif choice == wx.ID_OK:
     703                          create_archive = False
     704                  else:
     705                          raise Exception('invalid return')
     706
     707                  if create_archive:
     708                          export_dir = path
     709                          zip_file = self.__export_as_zip (
     710                                  gmTools.coalesce (
     711                                          value2test = encrypt,
     712                                          value2return = _('Saving entries as encrypted ZIP archive'),
     713                                          return_instead = _('Saving entries as unencrypted ZIP archive')
     714                                  ),
     715                                  items = items,
     716                                  encrypt = encrypt
     717                          )
     718                          if zip_file is None:
     719                                  gmDispatcher.send(signal = 'statustext', msg = _('Cannot save: aborted or error.'))
     720                                  return
     721                          gmTools.mkdir(export_dir)
     722                          final_zip_file = shutil.move(zip_file, export_dir)
     723                          gmDispatcher.send(signal = 'statustext', msg = _('Saved entries into [%s]') % final_zip_file)
     724                          target = final_zip_file
     725                  else:
     726                          export_dir = self.__export_as_files (
     727                                  gmTools.coalesce (
     728                                          value2test = encrypt,
     729                                          value2return = _('Saving entries as encrypted files'),
     730                                          return_instead = _('Saving entries as unencrypted files')
     731                                  ),
     732                                  base_dir = path,
     733                                  items = items,
     734                                  encrypt = encrypt,
     735                                  with_metadata = generate_metadata,
     736                                  convert2pdf = convert2pdf
     737                          )
     738                          if export_dir is None:
     739                                  gmDispatcher.send(signal = 'statustext', msg = _('Cannot save: aborted or error.'))
     740                                  return
     741
     742                          gmDispatcher.send(signal = 'statustext', msg = _('Saved entries into [%s]') % export_dir)
     743                          target = export_dir
     744
     745                  self.__save_soap_note(soap = _('Saved from export area to [%s]:\n - %s') % (
     746                          target,
     747                          '\n - '.join([ i['description'] for i in items ])
     748                  ))
     749                  self.__browse_patient_data(export_dir)
     750
     751                  # remove_entries ?
     752
     753                  return True
     754
     755                  # cleanup - ask !
     756                  # - files corresponding to DIR/DIR CONTENT entries
     757                  # - entries in export area
     758  #               remove_items = gmGuiHelpers.gm_show_question (
     759  #                       title = _('Creating zip archive'),
     760  #                       question = _(
     761  #                               'Zip archive created as:\n'
     762  #                               '\n'
     763  #                               ' [%s]\n'
     764  #                               '\n'
     765  #                               'Remove archived entries from export area ?'
     766  #                       ) % zip_file,
     767  #                       cancel_button = False
     768  #               )
     769  #               if remove_items:
     770  #                       exp_area.remove_items(items = items)
     771  #               return True
     772
     773          #--------------------------------------------------------
     774 -        def _on_export_items_button_pressed(self, event):
     775                  event.Skip()
     776
     777                  items = self.__get_items_to_work_on(_('Exporting entries'))
     778                  if items is None:
     779                          return
     780
     781                  # export dialog
     782                  pat = gmPerson.gmCurrentPatient()
     783                  dlg = cExportAreaExportToMediaDlg(self, -1, patient = pat, item_count = len(items))
     784                  choice = dlg.ShowModal()
     785                  media = dlg._LCTRL_removable_media.get_selected_item_data(only_one = True)
     786                  use_subdir = dlg._CHBOX_use_subdirectory.IsChecked()
     787                  encrypt = dlg._CHBOX_encrypt.IsChecked()
     788                  dlg.DestroyLater()
     789                  if choice == wx.ID_CANCEL:
     790                          return
     791
     792                  # export the files
     793                  if media['type'] == 'cd':
     794                          base_dir = gmTools.mk_sandbox_dir(prefix = 'iso-')
     795                  else:
     796                          base_dir = media['mountpoint']
     797                  if use_subdir:
     798                          dir2save2 = os.path.join(base_dir, pat.subdir_name)
     799                  else:
     800                          dir2save2 = base_dir
     801                  export_dir = self.__export_as_files (
     802                          gmTools.coalesce(encrypt, _('Exporting encrypted entries'), _('Exporting entries')),
     803                          base_dir = dir2save2,
     804                          items = items,
     805                          encrypt = encrypt,
     806                          with_metadata = True
     807                  )
     808                  if export_dir is None:
     809                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot export: aborted or error.'))
     810                          return
     811
     812                  if media['type'] == 'cd':
     813                          if not self.__burn_dir_to_disk(base_dir = base_dir):
     814                                  return
     815                          gmDispatcher.send(signal = 'statustext', msg = _('Entries successfully burned to disk.'))
     816                          self.__save_soap_note(soap = _('Burned onto CD/DVD:\n - %s') % '\n - '.join([ i['description'] for i in items ]))
     817                  else:
     818                          gmDispatcher.send(signal = 'statustext', msg = _('Exported entries into [%s]') % export_dir)
     819                          self.__save_soap_note(soap = _('Exported onto removable media:\n - %s') % '\n - '.join([ i['description'] for i in items ]))
     820
     821                  self.__browse_patient_data(dir2save2, encrypted = encrypt, archive = False, has_metadata = True)
     822
     823                  # remove_entries ?
     824
     825                  return True
     826
     827          #--------------------------------------------------------
     828 -        def _on_archive_items_button_pressed(self, event):
     829                  print("Event handler '_on_archive_items_button_pressed' not implemented!")
     830                  event.Skip()
     831
     832          #--------------------------------------------------------
     833 -        def _on_mail_items_button_pressed(self, event):
     834                  event.Skip()
     835
     836                  _log.debug('gm-mail_doc(.bat) API: "MAIL-PROGRAM PRAXIS-VCF ZIP-ARCHIVE"')
     837
     838                  found, external_cmd = gmShellAPI.detect_external_binary('gm-mail_doc')
     839                  if not found:
     840                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot send e-mail: <gm-mail_doc(.bat)> not found'))
     841                          return False
     842
     843                  zip_file = self.__export_as_zip (
     844                          _('Mailing documents as zip archive'),
     845                          encrypt = True
     846                  )
     847                  if zip_file is None:
     848                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot send e-mail: no archive created.'))
     849                          return False
     850
     851                  prax = gmPraxis.gmCurrentPraxisBranch()
     852                  args = [external_cmd, prax.vcf, zip_file]
     853                  success, ret_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = _cfg.get(option = 'debug'))
     854                  if not success:
     855                          gmGuiHelpers.gm_show_error (
     856                                  aMessage = _('Error mailing documents.'),
     857                                  aTitle = _('Mailing documents')
     858                          )
     859                          return False
     860
     861                  #self.__save_soap_note(soap = _('Mailed:\n - %s') % '\n - '.join([ i['description'] for i in items ]))
     862                  self.__save_soap_note(soap = _('Mailed export area content.'))
     863                  return True
     864
     865          #--------------------------------------------------------
     866 -        def _on_fax_items_button_pressed(self, event):
     867                  event.Skip()
     868
     869                  _log.debug('gm-fax_doc(.bat) API: "FAX-PROGRAM FAXNUMBER-OR-<EMPTY> LIST-OF-FILES-TO-FAX" (any file type !)')
     870
     871                  found, external_cmd = gmShellAPI.detect_external_binary('gm-fax_doc')
     872                  if not found:
     873                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot send fax: <gm-fax_doc(.bat)> not found'))
     874                          return False
     875
     876                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     877                  if len(items) == 0:
     878                          items = self._LCTRL_items.get_item_data()
     879                          if len(items) == 0:
     880                                  gmDispatcher.send(signal = 'statustext', msg = _('Cannot send fax: no items'))
     881                                  return None
     882                          if len(items) > 1:
     883                                  # ask, might be a lot
     884                                  process_all = gmGuiHelpers.gm_show_question (
     885                                          title = _('Faxing documents'),
     886                                          question = _('You have not selected any entries.\n\nSend fax with all %s entries ?') % len(items),
     887                                          cancel_button = False
     888                                  )
     889                                  if not process_all:
     890                                          return None
     891
     892                          return False
     893
     894                  files2fax = []
     895                  for item in items:
     896                          files2fax.append(item.save_to_file())
     897                  fax_number = wx.GetTextFromUser (
     898                          _('Please enter the fax number here !\n\n'
     899                            'It can be left empty if the external\n'
     900                            'fax software knows how to get the number.'),
     901                          caption = _('Faxing documents'),
     902                          parent = self,
     903                          centre = True
     904                  )
     905                  if fax_number == '':
     906                          fax_number = 'EMPTY'
     907                  args = [external_cmd, fax_number, ' '.join(files2fax)]
     908                  success, ret_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = _cfg.get(option = 'debug'))
     909                  if not success:
     910                          gmGuiHelpers.gm_show_error (
     911                                  aMessage = _('Error faxing documents to\n\n  %s') % fax_number,
     912                                  aTitle = _('Faxing documents')
     913                          )
     914                          return False
     915
     916                  self.__save_soap_note(soap = _('Faxed to [%s]:\n - %s') % (fax_number, '\n - '.join([ i['description'] for i in items ])))
     917                  return True
     918
     919          #--------------------------------------------------------
     920 -        def repopulate_ui(self):
     921                  self._populate_with_data()
     922
     923          #--------------------------------------------------------
     924          # internal API
     925          #--------------------------------------------------------
     926 -        def __init_ui(self):
     927                  self._LCTRL_items.set_columns([_('By'), _('When'), _('Description')])
     928
     929                  self._BTN_archive_items.Disable()
     930
     931                  # there's no GetToolTipText() in wx2.8
     932                  self.__mail_script_exists, path = gmShellAPI.detect_external_binary(binary = r'gm-mail_doc')
     933                  if not self.__mail_script_exists:
     934                          self._BTN_mail_items.Disable()
     935                          tt = self._BTN_mail_items.GetToolTipText() + '\n\n' + _('<gm-mail_doc(.bat) not found>')
     936                          self._BTN_mail_items.SetToolTip(tt)
     937
     938                  self.__fax_script_exists, path = gmShellAPI.detect_external_binary(binary = r'gm-fax_doc')
     939                  if not self.__fax_script_exists:
     940                          self._BTN_fax_items.Disable()
     941                          tt = self._BTN_fax_items.GetToolTipText() + '\n\n' + _('<gm-fax_doc(.bat) not found>')
     942                          self._BTN_fax_items.SetToolTip(tt)
     943
     944                  self.__burn_script_exists, path = gmShellAPI.detect_external_binary(binary = r'gm-burn_doc')
     945                  if not self.__burn_script_exists:
     946                          self._BTN_burn_items.Disable()
     947                          tt = self._BTN_burn_items.GetToolTipText() + '\n\n' + _('<gm-burn_doc(.bat) not found>')
     948                          self._BTN_burn_items.SetToolTip(tt)
     949
     950                  # make me and listctrl file drop targets
     951                  dt = gmGuiHelpers.cFileDropTarget(target = self)
     952                  self.SetDropTarget(dt)
     953                  dt = gmGuiHelpers.cFileDropTarget(on_drop_callback = self._drop_target_consume_filenames)
     954                  self._LCTRL_items.SetDropTarget(dt)
     955
     956          #--------------------------------------------------------
     957 -        def __save_soap_note(self, soap=None):
     958                  if soap.strip() == '':
     959                          return
     960                  emr = gmPerson.gmCurrentPatient().emr
     961                  epi = emr.add_episode(episode_name = 'administrative', is_open = False)
     962                  emr.add_clin_narrative (
     963                          soap_cat = None,
     964                          note = soap,
     965                          episode = epi
     966                  )
     967
     968          #--------------------------------------------------------
     969 -        def __export_as_files(self, msg_title, base_dir=None, encrypt=False, with_metadata=False, items=None, convert2pdf=False):
     970                  data_pwd = None
     971                  if encrypt:
     972                          data_pwd = self.__get_password(msg_title)
     973                          if data_pwd is None:
     974                                  _log.debug('user aborted by not providing the same password twice')
     975                                  gmDispatcher.send(signal = 'statustext', msg = _('Password not provided twice. Aborting.'))
     976                                  return None
     977
     978                  wx.BeginBusyCursor()
     979                  try:
     980                          exp_area = gmPerson.gmCurrentPatient().export_area
     981                          if with_metadata:
     982                                  export_dir = exp_area.export(base_dir = base_dir, items = items, passphrase = data_pwd)
     983                          else:
     984                                  export_dir = exp_area.dump_items_to_disk(base_dir = base_dir, items = items, passphrase = data_pwd, convert2pdf = convert2pdf)
     985                  finally:
     986                          wx.EndBusyCursor()
     987                  if export_dir is None:
     988                          gmGuiHelpers.gm_show_error (
     989                                  aMessage = _('Error exporting entries.'),
     990                                  aTitle = msg_title
     991                          )
     992                          return None
     993
     994                  return export_dir
     995
     996          #--------------------------------------------------------
     997 -        def __export_as_zip(self, msg_title, encrypt=True, items=None):
     998                  # get password
     999                  zip_pwd = None
    1000                  if encrypt:
    1001                          zip_pwd = self.__get_password(msg_title)
    1002                          if zip_pwd is None:
    1003                                  _log.debug('user aborted by not providing the same password twice')
    1004                                  gmDispatcher.send(signal = 'statustext', msg = _('Password not provided twice. Aborting.'))
    1005                                  return None
    1006                  # create archive
    1007                  wx.BeginBusyCursor()
    1008                  zip_file = None
    1009                  try:
    1010                          exp_area = gmPerson.gmCurrentPatient().export_area
    1011                          zip_file = exp_area.export_as_zip(passphrase = zip_pwd, items = items)
    1012                  except Exception:
    1013                          _log.exception('cannot create zip file')
    1014                  wx.EndBusyCursor()
    1015                  if zip_file is None:
    1016                          gmGuiHelpers.gm_show_error (
    1017                                  aMessage = _('Error creating zip file.'),
    1018                                  aTitle = msg_title
    1019                          )
    1020                  return zip_file
    1021
    1022          #--------------------------------------------------------
    1023 -        def __get_password(self, msg_title):
    1024                  while True:
    1025                          data_pwd = wx.GetPasswordFromUser (
    1026                                  message = _(
    1027                                          'Enter passphrase to protect the data with.\n'
    1028                                          '\n'
    1029                                          '(minimum length: 5, trailing blanks will be stripped)'
    1030                                  ),
    1031                                  caption = msg_title
    1032                          )
    1033                          # minimal weakness check
    1034                          data_pwd = data_pwd.rstrip()
    1035                          if len(data_pwd) > 4:
    1036                                  break
    1037                          retry = gmGuiHelpers.gm_show_question (
    1038                                  title = msg_title,
    1039                                  question = _(
    1040                                          'Insufficient passphrase.\n'
    1041                                          '\n'
    1042                                          '(minimum length: 5, trailing blanks will be stripped)\n'
    1043                                          '\n'
    1044                                          'Enter another passphrase ?'
    1045                                  )
    1046                          )
    1047                          if not retry:
    1048                                  # user changed her mind
    1049                                  return None
    1050                  # confidentiality
    1051                  gmLog2.add_word2hide(data_pwd)
    1052                  # reget password
    1053                  while True:
    1054                          data_pwd4comparison = wx.GetPasswordFromUser (
    1055                                  message = _(
    1056                                          'Once more enter passphrase to protect the data with.\n'
    1057                                          '\n'
    1058                                          '(this will protect you from typos)\n'
    1059                                          '\n'
    1060                                          'Abort by leaving empty.'
    1061                                  ),
    1062                                  caption = msg_title
    1063                          )
    1064                          data_pwd4comparison = data_pwd4comparison.rstrip()
    1065                          if data_pwd4comparison == '':
    1066                                  # user changed her mind ...
    1067                                  return None
    1068                          if data_pwd == data_pwd4comparison:
    1069                                  break
    1070                          gmGuiHelpers.gm_show_error (
    1071                                  error = _(
    1072                                          'Passphrases do not match.\n'
    1073                                          '\n'
    1074                                          'Retry, or abort with an empty passphrase.'
    1075                                  ),
    1076                                  title = msg_title
    1077                          )
    1078                  return data_pwd
    1079
    1080          #--------------------------------------------------------
    1081 -        def __get_items_to_work_on(self, msg_title):
    1082
    1083                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
    1084                  if len(items) > 0:
    1085                          return items
    1086
    1087                  items = self._LCTRL_items.get_item_data()
    1088                  if len(items) == 0:
    1089                          gmDispatcher.send(signal = 'statustext', msg = _('Export area empty. Nothing to do.'))
    1090                          return None
    1091
    1092                  if len(items) == 1:
    1093                          return items
    1094
    1095                  process_all = gmGuiHelpers.gm_show_question (
    1096                          title = msg_title,
    1097                          question = _('You have not selected any entries.\n\nReally use all %s entries ?') % len(items),
    1098                          cancel_button = False
    1099                  )
    1100                  if process_all:
    1101                          return items
    1102
    1103                  return None
    1104
    1105          #--------------------------------------------------------
    1106 -        def __burn_dir_to_disk(self, base_dir):
    1107
    1108                  _log.debug('gm-burn_doc(.bat) API: "DIRECTORY-TO-BURN-FROM"')
    1109
    1110                  found, burn_cmd = gmShellAPI.detect_external_binary('gm-burn_doc')
    1111                  if not found:
    1112                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot burn to disk: Helper not found.'))     # should not happen
    1113                          return False
    1114
    1115                  args = [burn_cmd, base_dir]
    1116                  wx.BeginBusyCursor()
    1117                  try:
    1118                          success, ret_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = _cfg.get(option = 'debug'))
    1119                  finally:
    1120                          wx.EndBusyCursor()
    1121                  if success:
    1122                          return True
    1123
    1124                  gmGuiHelpers.gm_show_error (
    1125                          aMessage = _('Error burning documents to CD/DVD.'),
    1126                          aTitle = _('Burning documents')
    1127                  )
    1128                  return False
    1129
    1130          #--------------------------------------------------------
    1131 -        def __browse_patient_data(self, base_dir):
    1132
    1133                  msg = _('Documents saved into:\n\n %s') % base_dir
    1134                  browse_index = gmGuiHelpers.gm_show_question (
    1135                          title = _('Browsing patient data excerpt'),
    1136                          question = msg + '\n\n' + _('Browse saved entries ?'),
    1137                          cancel_button = False
    1138                  )
    1139                  if not browse_index:
    1140                          return
    1141
    1142                  if os.path.isfile(os.path.join(base_dir, 'index.html')):
    1143                          gmNetworkTools.open_url_in_browser(url = 'file://%s' % os.path.join(base_dir, 'index.html'))
    1144                          return
    1145
    1146                  gmMimeLib.call_viewer_on_file(base_dir, block = False)
    1147
    1148          #--------------------------------------------------------
    1149          # file drop target API
    1150          #--------------------------------------------------------
    1151 -        def _drop_target_consume_filenames(self, filenames):
    1152                  pat = gmPerson.gmCurrentPatient()
    1153                  if not pat.connected:
    1154                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot accept new documents. No active patient.'))
    1155                          return
    1156
    1157                  # dive into folders dropped onto us and extract files (one level deep only)
    1158                  real_filenames = []
    1159                  for pathname in filenames:
    1160                          try:
    1161                                  files = os.listdir(pathname)
    1162                                  gmDispatcher.send(signal='statustext', msg=_('Extracting files from folder [%s] ...') % pathname)
    1163                                  for file in files:
    1164                                          fullname = os.path.join(pathname, file)
    1165                                          if not os.path.isfile(fullname):
    1166                                                  continue
    1167                                          real_filenames.append(fullname)
    1168                          except OSError:
    1169                                  real_filenames.append(pathname)
    1170
    1171                  if not pat.export_area.add_files(real_filenames, hint = _('Drag&Drop')):
    1172                          gmGuiHelpers.gm_show_error (
    1173                                  title = _('Adding files to export area'),
    1174                                  error = _('Cannot add (some of) the following files to the export area:\n%s ') % '\n '.join(real_filenames)
    1175                          )
    1176          #--------------------------------------------------------
    1177          # reget mixin API
    1178          #
    1179          # remember to call
    1180          #       self._schedule_data_reget()
    1181          # whenever you learn of data changes from database
    1182          # listener threads, dispatcher signals etc.
    1183          #--------------------------------------------------------
    1184 -        def _populate_with_data(self):
    1185                  pat = gmPerson.gmCurrentPatient()
    1186                  if not pat.connected:
    1187                          return True
    1188
    1189                  items = pat.export_area.items
    1190                  self._LCTRL_items.set_string_items ([
    1191                          [       i['created_by'],
    1192                                  gmDateTime.pydt_strftime(i['created_when'], '%Y %b %d %H:%M'),
    1193                                  i['description']
    1194                          ] for i in items
    1195                  ])
    1196                  self._LCTRL_items.set_column_widths([wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE])
    1197                  self._LCTRL_items.set_data(items)
    1198
    1199                  self._LCTRL_items.SetFocus()
    1200
    1201                  return True
    1202
    1203  #============================================================
    1204  from Gnumed.wxGladeWidgets import wxgPrintMgrPluginPnl
    1205
    1206 -class cPrintMgrPluginPnl(wxgPrintMgrPluginPnl.wxgPrintMgrPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
    1207          """Panel holding print jobs.
    1208
    1209          Used as notebook page."""
    1210
    1211 -        def __init__(self, *args, **kwargs):
    1212                  wxgPrintMgrPluginPnl.wxgPrintMgrPluginPnl.__init__(self, *args, **kwargs)
    1213                  gmRegetMixin.cRegetOnPaintMixin.__init__(self)
    1214                  self.__init_ui()
    1215                  self.__register_interests()
    1216          #--------------------------------------------------------
    1217          # event handling
    1218          #--------------------------------------------------------
    1219 -        def __register_interests(self):
    1220                  gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
    1221                  gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
    1222                  gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_table_mod)
    1223          #--------------------------------------------------------
    1224 -        def _on_pre_patient_unselection(self):
    1225                  self._RBTN_active_patient_only.Enable(False)
    1226                  self._RBTN_all_patients.Value = True
    1227                  self._BTN_export_printouts.Enable(False)
    1228          #--------------------------------------------------------
    1229 -        def _on_post_patient_selection(self):
    1230                  self._RBTN_active_patient_only.Enable(True)
    1231                  self._BTN_export_printouts.Enable(True)
    1232          #--------------------------------------------------------
    1233 -        def _on_table_mod(self, *args, **kwargs):
    1234                  if kwargs['table'] != 'clin.export_item':
    1235                          return
    1236                  if self._RBTN_all_patients.Value is True:
    1237                          self._schedule_data_reget()
    1238                          return
    1239                  pat = gmPerson.gmCurrentPatient()
    1240                  if not pat.connected:
    1241                          return
    1242                  if kwargs['pk_identity'] != pat.ID:
    1243                          return
    1244                  self._schedule_data_reget()
    1245          #--------------------------------------------------------
    1246 -        def _on_all_patients_selected(self, event):
    1247                  event.Skip()
    1248                  self._schedule_data_reget()
    1249          #--------------------------------------------------------
    1250 -        def _on_active_patient_only_selected(self, event):
    1251                  event.Skip()
    1252                  self._schedule_data_reget()
    1253          #--------------------------------------------------------
    1254 -        def _on_view_button_pressed(self, event):
    1255                  event.Skip()
    1256                  printout = self._LCTRL_printouts.get_selected_item_data(only_one = True)
    1257                  if printout is None:
    1258                          return
    1259                  printout.display_via_mime(block = False)
    1260          #--------------------------------------------------------
    1261 -        def _on_print_button_pressed(self, event):
    1262                  event.Skip()
    1263                  printouts = self._LCTRL_printouts.get_selected_item_data(only_one = False)
    1264                  if len(printouts) == 0:
    1265                          return
    1266
    1267                  files2print = []
    1268                  for printout in printouts:
    1269                          files2print.append(printout.save_to_file())
    1270
    1271                  if len(files2print) == 0:
    1272                          return
    1273
    1274                  jobtype = 'print_manager'
    1275                  printed = gmPrinting.print_files(filenames = files2print, jobtype = jobtype, verbose = _cfg.get(option = 'debug'))
    1276                  if not printed:
    1277                          gmGuiHelpers.gm_show_error (
    1278                                  aMessage = _('Error printing documents.'),
    1279                                  aTitle = _('Printing [%s]') % jobtype
    1280                          )
    1281                          return False
    1282
    1283                  return True
    1284          #--------------------------------------------------------
    1285 -        def _on_export_button_pressed(self, event):
    1286                  event.Skip()
    1287                  pat = gmPerson.gmCurrentPatient()
    1288                  if not pat.connected:
    1289                          return
    1290                  printouts = self._LCTRL_printouts.get_selected_item_data(only_one = False)
    1291                  for printout in printouts:
    1292                          printout.is_print_job = False
    1293          #--------------------------------------------------------
    1294 -        def _on_delete_button_pressed(self, event):
    1295                  event.Skip()
    1296                  printouts = self._LCTRL_printouts.get_selected_item_data(only_one = False)
    1297                  if len(printouts) == 0:
    1298                          return
    1299                  if len(printouts) > 1:
    1300                          really_delete = gmGuiHelpers.gm_show_question (
    1301                                  title = _('Deleting document from export area.'),
    1302                                  question = _('Really remove %s selected document(s)\nfrom the patient export area ?') % len(printouts)
    1303                          )
    1304                          if not really_delete:
    1305                                  return
    1306                  for printout in printouts:
    1307                          gmExportArea.delete_export_item(pk_export_item = printout['pk_export_item'])
    1308
    1309          #--------------------------------------------------------
    1310          # internal API
    1311          #--------------------------------------------------------
    1312 -        def __init_ui(self):
    1313                  self._BTN_export_printouts.Enable(False)
    1314          #--------------------------------------------------------
    1315          # reget mixin API
    1316          #--------------------------------------------------------
    1317 -        def _populate_with_data(self):
    1318                  if self._RBTN_all_patients.Value is True:
    1319                          columns = [_('Patient'), _('Provider'), _('Description')]
    1320                          printouts = gmExportArea.get_print_jobs(order_by = 'pk_identity, description')
    1321                          items = [[
    1322                                  '%s, %s (%s)' % (
    1323                                          p['lastnames'],
    1324                                          p['firstnames'],
    1325                                          p['gender']
    1326                                  ),
    1327                                  p['created_by'],
    1328                                  p['description']
    1329                          ] for p in printouts ]
    1330                  else:
    1331                          pat = gmPerson.gmCurrentPatient()
    1332                          if pat.connected:
    1333                                  columns = [_('Provider'), _('Created'), _('Description')]
    1334                                  printouts = pat.export_area.get_printouts(order_by = 'created_when, description')
    1335                                  items = [[
    1336                                          p['created_by'],
    1337                                          gmDateTime.pydt_strftime(p['created_when'], '%Y %b %d %H:%M'),
    1338                                          p['description']
    1339                                  ] for p in printouts ]
    1340                          else:
    1341                                  columns = [_('Patient'), _('Provider'), _('Description')]
    1342                                  printouts = gmExportArea.get_print_jobs(order_by = 'pk_identity, description')
    1343                                  items = [[
    1344                                          '%s, %s (%s)' % (
    1345                                                  p['lastnames'],
    1346                                                  p['firstnames'],
    1347                                                  p['gender']
    1348                                          ),
    1349                                          p['created_by'],
    1350                                          p['description']
    1351                                  ] for p in printouts ]
    1352                  self._LCTRL_printouts.set_columns(columns)
    1353                  self._LCTRL_printouts.set_string_items(items)
    1354                  self._LCTRL_printouts.set_column_widths([wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE])
    1355                  self._LCTRL_printouts.set_data(printouts)
    1356                  self._LCTRL_printouts.SetFocus()
    1357                  return True
    1358