class cls_pildevbase:

   def __init__(self):

      self.__aid__ = 0x00          # accessory id 
      self.__defaddr__ = 0         # default address alter AAU
      self.__did__ = ""            # device id
      self.__status__ = 0          # HP-IL status
      self.__addr__ = 0            # HP-IL primary address (by TAD,LAD)
                                   # bits 0-5=AAD or AEP, bit 7=1 means
                                   # auto address taken
      self.__addr2nd__ = 0         # HP-IL secondary address (by SAD)
                                   # bits 0-5=AES, bit 7 means auto addr taken
      self.__ilstate__ =0          # state machine flag
                                   # bit 7, bit 6, bit5, bit 4
                                   # 0      0      0     0 idle
                                   # 0      0      1     0 addressed listener in
                                   #                       second. add. mode
                                   # 0      0      0     1 addressed talker in
                                   #                       second. add. mode
                                   # 1      0      0     0 addressed listener
                                   # 0      1      0     0 addressed talker 
                                   # bit 0 or bit 1 set    active takler
                                   # bit 1: SDA, SDI
                                   # bit 0: SST, SDI, SAI, NRD
      self.__talker_frame__=0      # frame sent as talker
      self.__ptsdi__ = 0           # output pointer for device id
      self.__status_len__=1        # length of device status in bytes
      self.__ptssi__ = 0           # output pointer for hp-il status
      self.__isactive__= False     # device active in loop
      self.__addr_framecounter__=0 # framecounter when device got an address
      self.__threadobject__=None   # reference to the thread object
      self.__status_lock__= threading.Lock()
      self.__islocked__= False
      self.__access_lock__= threading.Lock()
#
# --- public functions ---
#
#  set device active/inactive. If the device becomes active check if an AAU,
#  AAD, AEP or AES happened in the mean time. In this case reset the device
#  address
#
   def setactive(self, active):
      if not self.__isactive__ and active:
         if self.__addr_framecounter__ != self.__threadobject__.get_addr_framecounter():
            self.__addr__=0
            self.__addr2nd__=0
      self.__isactive__= active
#
#  set object reference to thread object
#
   def setThreadObject(self,obj):
      self.__threadobject__= obj
#
#  set local and update globel addr_framecounter
#
   def update_addr_framecounter(self):
      self.__addr_framecounter__= self.__threadobject__.get_framecounter()
      self.__threadobject__.update_addr_framecounter(self.__addr_framecounter__)
#
#  return device status
#
   def getstatus(self):
      self.__access_lock__.acquire()
      status="idle"
      if self.__ilstate__ & 0x03:
         status="act. talker"
      else:
         if self.__ilstate__ & 0xA0:
            status="addr. listener"
         elif self.__ilstate__ & 0x50:
            status="addr. talker"

      ret= [self.__isactive__, self.__did__, self.__aid__, self.__addr__, self.__addr2nd__, status]
      self.__access_lock__.release()
      return ret

#
#  lock device, all output is disabled
#
   def setlocked(self,locked):
      self.__access_lock__.acquire()
      self.__islocked__= locked
      self.__access_lock__.release()

#
#  Process device
#
   def process(self,frame):

#
#     if device is not active, return
#
      if not self.__isactive__:
         return(frame)
#
#     process frames
#
      if (frame & 0x400) == 0:
         frame= self.__do_doe__(frame)
      elif (frame & 0x700) == 0x400:
         frame= self.__do_cmd__(frame)
      elif (frame & 0x700) == 0x500:
         frame= self.__do_rdy__(frame)
#
#     set service request bit if data available status bit set
#
      if self.__getstatus__() & 0x40:
         if (frame & 0x700) == 0x000:  # data 00x xxxx xxxx -> 001 xxxx xxxx
            frame= frame | 0x100
         if (frame & 0x700) == 0x200:  # end  01x xxxx xxxx -> 011 xxxx xxxx
            frame= frame | 0x100
         if (frame & 0x700) == 0x600:  # idy  11x xxxx xxxx -> 111 xxxx xxxx
            frame= frame | 0x100
      return(frame)

#
#  --- private ---
#
#
#  get status byte from device thread safe
#
   def __getstatus__(self):
      self.__status_lock__.acquire()
      status= self.__status__
      self.__status_lock__.release()
      return(status)
#
# set status byte of device thread safe
#
   def __setstatus__(self,status):
      self.__status_lock__.acquire()
      self.__status__= status
      self.__status_lock__.release()
#
#  output data stub
#
   def __outdata__(self,frame):
      return frame

#
#  input data stub
#
   def __indata__(self,frame):
      return

#
#  device clear stub
#
   def __clear_device__(self):
      # reset service request bit
      s= self.__getstatus__()
      s &= ~ 0x40
      self.__setstatus__(s)
      return
#
#  stub for extended sad commands
#
   def __cmd_sad_ext__(self,frame):
      return frame

#
# stub for extended commands
#
   def __cmd_ext__(self,frame):
      return frame

#
#  manage HPIL data frames, returns the returned frame
#
   def __do_doe__(self,frame):
      talker_error= False

      if (self.__ilstate__ & 0xC0) == 0x40: # addressed talker?

         if (self.__ilstate__ & 0x03) != 0: # active talker?

#           compare last talker frame with actual frame without SRQ bit
            talker_error = ((frame & 0x6FF) != (self.__talker_frame__ &0x6FF))

#           data (SDA) status (SST) or accessory ID (SDI)
            if (not talker_error) and (self.__ilstate__ & 0x02 !=0): # talker

#              save current SRQ bit
               SrqBit= frame & 0x100 

#              status (SST) or accessory ID (SDI)
               if (self.__ilstate__ & 0x01) != 0: 

#                 0x43: active talker (multibyte status)
                  if self.__ptssi__ > 0:   # SST
                     self.__ptssi__= self.__ptssi__-1
                     if self.__ptssi__ > 0:
                        frame= (self.__getstatus__() >> (( self.__status_len__-self.__ptssi__ ) * 8)) & 0xFF
                  if self.__ptsdi__ > 0:   # SDI
                     if self.__ptsdi__ == len(self.__did__):
                        frame=0
                        self.__ptsdi__=0
                     else: # SDI
                        frame= ord(self.__did__ [self.__ptsdi__])
                        self.__ptsdi__= self.__ptsdi__+1
                  if self.__ptssi__ == 0 and self.__ptsdi__ == 0 : 
#                    EOT for SST and SDI
                     frame= 0x540
               else: # 0x42 active talker (data)
                  frame=self.__outdata__(frame) # SDA
#              a set SRQ bit doesn't matter on ready class frames
               frame |= SrqBit
            else: # 0x41 active talker (single byte status)
               frame= 0x540 # end of SAI or NRD or talker error

         if frame == 0x540: # EOT
#           check for error and set ETE frame
            if talker_error:
               frame= 0x541 # ETE
            self.__ilstate__ &= ~ 0x03 # delete active talker
         self.__talker_frame__= frame

      if (self.__ilstate__ & 0xC0)==0x80:  # listener
            self.__indata__(frame)
      return(frame)
#
# manage HP-IL command frames
#
   def __do_cmd__(self,frame):

      n= frame & 0xff
      t= n >> 5
      if t == 0:
         if n == 4:   # SDC
            if (self.__ilstate__ & 0x80) != 0: # listener
               self.__clear_device__()
         elif n == 20: # DCL
             self.__clear_device__()
      elif t == 1:  # LAD
         n= n & 31
         if n == 31: # UNL, if not talker then go to idle state
             if (self.__ilstate__ & 0xA0) != 0: # not talker ?
                self.__ilstate__ &= 0x50 # idle
         else:       # if MLA go to listen state
             if (self.__ilstate__ &0x80) ==0 and  n == (self.__addr__ & 31):
                if (self.__addr2nd__ & 0x80) == 0:
                   self.__ilstate__ = 0x80 # set addressed listener
                else:
                   self.__ilstate__ = 0x20 # set addressed listener second. add mode
      elif t == 2:  # TAD  
         n= n & 31
         if n == (self.__addr__ & 31):
            if (self.__addr2nd__ & 0x80) == 0: # if MTA go to talker state
               self.__ilstate__= 0x40 # set addressed talker
            else:
               self.__ilstate__= 0x10 # set addressed talker, second. add. mode
         else:
            if ( self.__ilstate__ & 0x50) != 0: # addressed talker?
               self.__ilstate__ &= 0xA0 # idle
      elif t == 3: # SAD
         if (self.__ilstate__ & 0x30) !=0: # addressed talker or listener 2nd addr mode?
            n = n & 31
            if n == (self.__addr2nd__ & 31):
               self.__ilstate__<<=2  # switch to addressed listener/talker
            else:
               self.__ilstate__=0 # idle
         else:
            frame= self.__cmd_sad_ext__(frame)
      elif t == 4: 
         n= n & 31
         if n == 16: # IFC
            self.__ilstate__= 0x0 # idle
         elif n == 26: # AAU
            self.update_addr_framecounter()
            self.__addr__=  self.__defaddr__
            self.__addr2nd__= 0
      else:
         frame= self.__cmd_ext__(frame)
      return(frame)
            
#
# HP-IL ready frames
#
   def __do_rdy__(self,frame):

      n= frame & 0xFF
      if n <= 127:
         if ( self.__ilstate__ & 0x40) !=0:  # SOT, addressed talker?
            if  n == 66:  #NRD
               self.__ptsdi__ = 0
               self.__ptssi__ = 0
               self.__ilstate__ = 0x41 # abort transfer, clear SDA/SDI

            elif n == 96: # SDA
               frame=self.__outdata__(frame)
               if frame != 0x560: # not sda received data
                  self.__ilstate__= 0x42 # active talker, SDA/SDI
                  self.__talker_frame__= frame # last talker frame
            elif n == 97: # SST
                # reset service request bit
                s= self.__getstatus__()
                s &= ~ 0x40
                self.__setstatus__(s)
                # update IL status and return no. of status bytes
                self.__ptssi__ = self.__status_len__
                if self.__ptssi__ > 0: # response to status request
                   frame = (self.__getstatus__() >> ((self.__status_len__-self.__ptssi__) * 8)) & 0xFF
                   self.__ilstate__= 0x43 # active talker
                   self.__talker_frame__= frame # last talker frame
            elif n == 98:  # SDI
               if self.__did__ != "":
                  frame= ord(self.__did__[0])
                  self.__ptsdi__ = 1 # other 2
                  self.__ilstate__= 0x43 # active talker, SDA/SDI, SST/SDI/SAI
                  self.__talker_frame__= frame
            elif n == 99: # SAI
                  frame= self.__aid__ & 0xFF
                  self.__ilstate__= 0x41 # active talker, SST/SDI/SAI
                  self.__talker_frame__= frame
      else:
         if n < 0x80 +31: # AAD
            if ((self.__addr__ & 0x80) == 0 and self.__addr2nd__ ==0):
               # AAD, if not already an assigned address, take it
               self.update_addr_framecounter()
               self.__addr__ = n
               frame=frame+1
         elif  (n >= 0xA0 and n < 0xA0 + 31): # AEP
            # AEP, if not already an assigned address and got an AES frame,
            if ((self.__addr__ & 0x80) == 0 and (self.__addr2nd__ & 0x80) != 0):
               # take it
               self.update_addr_framecounter()
               self.__addr__= n & 0x9F
         elif (n >= 0xC0 and n < 0xC0 +31): # AES
            if (self.__addr__ & 0x80) == 0:
               self.update_addr_framecounter()
               self.__addr2nd__= n & 0x9F
               frame=frame + 1
      return (frame) class cls_tabgeneric(QtWidgets.QWidget):

   def __init__(self,parent,name):
      super().__init__()
      self.name= name
      self.active= PILCONFIG.get(self.name,"active",False)
      self.parent=parent
      self.font_name=FONT
      self.font_size=0
      self.font_width=0
      self.font_height=0
      self.width= 0
      self.height= 0
      self.guiobject= None
      self.cbLogging= None
      self.logging= False
      self.pildevice=None
      self.widget_index=1
      self.cBut= None
#
#     Build basic layout
#
      self.vbox= QtWidgets.QVBoxLayout()
      self.setLayout(self.vbox)
#
#     hbox1: container of GUI Object
#
      self.hbox1=QtWidgets.QHBoxLayout()
      self.hbox1.setContentsMargins(10,10,10,10)
#
#     hbox2: container of control widgets
# 
      self.hbox2= QtWidgets.QHBoxLayout()
      self.hbox2.setContentsMargins(10,3,10,3)

      self.cbActive= QtWidgets.QCheckBox('Device enabled')
      self.cbActive.setChecked(self.active)
      self.cbActive.setEnabled(False)
      self.cbActive.stateChanged.connect(self.do_cbActive)

      self.hbox2.addWidget(self.cbActive)
      self.hbox2.addStretch(1)

      self.vbox.addLayout(self.hbox1)
      self.vbox.addLayout(self.hbox2)
#
#  add the gui object to hbox1
#
   def add_guiobject(self,guiobject):
      self.guiobject= guiobject
      self.hbox1.addWidget(self.guiobject)
#
#  insert a control widget
#
   def add_controlwidget(self,widget):
      self.hbox2.insertWidget(self.widget_index,widget)
      self.widget_index+=1
#
#  add a config cascading menu: always at the end of the control box
#
   def add_configwidget(self):
      self.cBut= cls_config_tool_button(self.name,"Configuration")
      self.hbox2.addWidget(self.cBut)
#
#  insert the cbLogging widget in hbox2 after the cbActive Widget
#
   def add_logging(self):
      self.cbLogging= LogCheckboxWidget(self.name)
      self.hbox2.insertWidget(1,self.cbLogging)
      self.logging= PILCONFIG.get(self.name,"logging",False)
      self.cbLogging.setChecked(self.logging)
      self.cbLogging.setEnabled(False)
      self.cbLogging.stateChanged.connect(self.do_cbLogging)
      self.widget_index+=1
#
#  if config button present, then add log buffer option
#
      if self.cBut is not None:
         self.cBut.add_option("Log buffering","buffer_log",T_BOOLEAN,[True,False])
#
#  insert a status widget
#
   def add_statuswidget(self,statuswidget):
      self.hbox2.insertWidget(self.widget_index,statuswidget)
      self.hbox2.insertStretch(self.widget_index,1)
#
#  tab config changed, handle buffer_log change
#
   def do_tabconfig_changed(self):
     param= self.cBut.get_changed_option_name()
     if param== "buffer_log":
         self.cbLogging.set_buffering(PILCONFIG.get(self.name,"buffer_log"))
#
#  reconfigure, nothing to do here
#
   def reconfigure(self):
      return
# 
#  disable tab
#
   def disable(self):
      if self.cbLogging is not None:
         if self.logging:
            self.cbLogging.logClose()
         self.cbLogging.setEnabled(False)
      self.cbActive.setEnabled(False)
#
#  enable tab
#
   def enable(self):
      self.cbActive.setEnabled(True)
      if self.cbLogging is not None:
         if self.logging:
            self.cbLogging.logOpen()
         self.cbLogging.setEnabled(True)
#
#  toggle active/inactive
#
   def toggle_active(self):
      return
#
#  action: toogle active checkbox
#
   def do_cbActive(self):
      self.active= self.cbActive.isChecked()
      PILCONFIG.put(self.name,"active",self.active)
      self.pildevice.setactive(self.active)
      self.toggle_active()
#
#  action: toggle log checkbox
#
   def do_cbLogging(self):
      self.cbLogging.setEnabled(False)
      self.logging= self.cbLogging.isChecked()
      self.pildevice.setlocked(True)
      if self.logging:
         self.cbLogging.logOpen()
      else:
         self.cbLogging.logClose()
      PILCONFIG.put(self.name,"logging",self.logging)
      self.pildevice.setlocked(False)
      self.cbLogging.setEnabled(True) T_STRING=2 T_INTEGER=3 O_DEFAULT= -1 class PilConfigError(Exception):
   def __init__(self,msg,add_msg= None):
      self.msg= msg
      if add_msg is None:
         self.add_msg=""
      else:
         self.add_msg = add_msg PILCONFIG=  cls_pilconfig() CHARSET_HP71=0
CHARSET_HP41=1
CHARSET_HP75=2
CHARSET_ROMAN8=3
CHARSET_HP2225=4
def charconv(s, charset):
def icharconv(i, charset):
def barrconv(barr, charset):
def stringconv(string, charset): class LifError(Exception):
class cls_LifDir:
class cls_LifFile:
   def __init__(self,msg,add_msg=None):
   def __init__(self,liffile):
   def open(self):
   def rewind(self):
   def getNextEntry(self):
   def getTypeLen(self):
   def __init__(self):
   def set_filename(self,name):
   def __open__(self):
   def __close__(self):
   def rrec(self,recno):
   def lifopen(self):
   def lifclose(self):
   def getLifHeader(self): class cls_lifpack(QtWidgets.QDialog):

   def __init__(self,parent= None):
      super().__init__()

   @staticmethod
   def exec(lifimagefile):
      d=cls_lifpack()
      reply = QtWidgets.QMessageBox.question(d, 'Message', 'Do you really want to pack the LIF image file', QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
      if reply == QtWidgets.QMessageBox.Yes:
         exec_single(d,[add_path("lifpack"),lifimagefile]) class cls_lifpurge(QtWidgets.QDialog):

   def __init__(self,parent= None):
      super().__init__()

   @staticmethod
   def exec(lifimagefile,liffile):
      d=cls_lifpurge()
      reply = QtWidgets.QMessageBox.question(d, 'Message', 'Do you really want to purge '+liffile, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
      if reply == QtWidgets.QMessageBox.Yes:
         exec_single(d,[add_path("lifpurge"),lifimagefile, liffile]) class cls_lifrename (QtWidgets.QDialog):

   def __init__(self,lifimagefile,filename,parent= None):
      super().__init__()
      self.lifimagefile=lifimagefile
      self.oldfilename=filename
      self.setWindowTitle("Rename File")
      self.vlayout= QtWidgets.QVBoxLayout()
      self.setLayout(self.vlayout)

      self.lbl=QtWidgets.QLabel("Rename file:")
      self.vlayout.addWidget(self.lbl)
      self.leditFileName=QtWidgets.QLineEdit(self)
      self.leditFileName.setText(filename)
      self.leditFileName.setMaxLength(10)
      self.leditFileName.textChanged.connect(self.do_checkenable)
      self.validator = cls_LIF_validator()
      self.leditFileName.setValidator(self.validator)
      self.vlayout.addWidget(self.leditFileName)

      self.buttonBox = QtWidgets.QDialogButtonBox(self)
      self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
      self.buttonBox.setCenterButtons(True)
      self.buttonBox.accepted.connect(self.do_ok)
      self.buttonBox.rejected.connect(self.do_cancel)
      self.vlayout.addWidget(self.buttonBox)

#
#  check, if the OK button can be enabled
#
   def do_checkenable(self):
      self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
      if (self.leditFileName.text() != ""):
         self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
      return

# 
#  ok rename file
#
   def do_ok(self):
      newfilename=self.leditFileName.text()
      if newfilename != "":
         exec_single(self,[add_path("lifrename"),self.lifimagefile,self.oldfilename,newfilename])
      super().accept()

#
#  cancel do nothing
#
   def do_cancel(self):
      super().reject()

   @staticmethod
   def exec(lifimagefile,liffile):
      d=cls_lifrename(lifimagefile,liffile)
      result= d.exec_() class cls_lifexport (QtWidgets.QDialog):

   def __init__(self,lifimagefile,liffilename,liffiletype,workdir,parent= None):
      super().__init__()
      self.lifimagefile= lifimagefile
      self.liffiletype= liffiletype
      self.liffilename= liffilename
      self.workdir=workdir
      self.setWindowTitle("Export File")
      self.vlayout= QtWidgets.QVBoxLayout()
      self.setLayout(self.vlayout)

      self.gBox0=QtWidgets.QGroupBox("File to be exported to file system")
      self.lblLifFilename=QtWidgets.QLabel(self.liffilename+" (type: "+self.liffiletype+")")
      self.vbox0=QtWidgets.QVBoxLayout()
      self.vbox0.addWidget(self.lblLifFilename)
      self.vbox0.addStretch(1)
      self.gBox0.setLayout(self.vbox0)
      self.vlayout.addWidget(self.gBox0)
      
      self.gBox1=QtWidgets.QGroupBox("Postprocessing options")
      self.radioLifAscii= QtWidgets.QRadioButton("convert LIF-Text to ASCII")
      self.radioLifAscii.clicked.connect(self.do_radio)
      self.radioLifAscii.setEnabled(False)
      self.radioTxt75Ascii= QtWidgets.QRadioButton("convert HP-75 text file to ASCII, omit line numbers")
      self.radioTxt75Ascii.clicked.connect(self.do_radio)
      self.radioTxt75Ascii.setEnabled(False)
      self.radioTxt75AsciiNumbers= QtWidgets.QRadioButton("convert HP-75 text file to ASCII, retain line numbers")
      self.radioTxt75AsciiNumbers.clicked.connect(self.do_radio)
      self.radioTxt75AsciiNumbers.setEnabled(False)
      self.radioEramco= QtWidgets.QRadioButton("unpack Eramco MLDL-OS ROM file")
      self.radioEramco.clicked.connect(self.do_radio)
      self.radioEramco.setEnabled(False)
      self.radioHepax= QtWidgets.QRadioButton("unpack HEPAX HP41 SDATA ROM file")
      self.radioHepax.setEnabled(False)
      self.radioHepax.clicked.connect(self.do_radio)
      self.radioRaw= QtWidgets.QRadioButton("remove LIF header, create RAW file")
      self.radioRaw.clicked.connect(self.do_radio)
      self.radioNone= QtWidgets.QRadioButton("None")
      self.radioNone.clicked.connect(self.do_radio)

      if self.liffiletype== "TEXT":
         self.radioLifAscii.setEnabled(True)
         self.radioLifAscii.setChecked(True)
         self.outputextension=".txt"
      elif self.liffiletype== "TXT75":
         self.radioTxt75Ascii.setEnabled(True)
         self.radioTxt75Ascii.setChecked(True)
         self.radioTxt75AsciiNumbers.setEnabled(True)
         self.outputextension=".txt"
      elif self.liffiletype== "X-M41":
         self.radioEramco.setEnabled(True)
         self.radioEramco.setChecked(True)
         self.outputextension=".rom"
      elif self.liffiletype== "SDATA":
         self.radioNone.setChecked(True)
         self.radioHepax.setEnabled(True)
         self.outputextension=".lif"
      else:
         self.radioNone.setChecked(True)
         self.outputextension=".lif"

      self.vbox=QtWidgets.QVBoxLayout()
      self.vbox.addWidget(self.radioLifAscii)
      self.vbox.addWidget(self.radioTxt75Ascii)
      self.vbox.addWidget(self.radioTxt75AsciiNumbers)
      self.vbox.addWidget(self.radioEramco)
      self.vbox.addWidget(self.radioHepax)
      self.vbox.addWidget(self.radioRaw)
      self.vbox.addWidget(self.radioNone)
      self.vbox.addStretch(1)
      self.gBox1.setLayout(self.vbox)

      self.vlayout.addWidget(self.gBox1)

      self.gBox2=QtWidgets.QGroupBox("Output file")
      self.hbox=QtWidgets.QHBoxLayout()
      self.outputfile=os.path.join(self.workdir, self.liffilename.lower()+self.outputextension)
      self.lblFilename=QtWidgets.QLabel(self.outputfile)
      self.hbox.addWidget(self.lblFilename)
      self.hbox.addStretch(1)
      self.butChange= QtWidgets.QPushButton("Change")
      self.butChange.clicked.connect(self.do_filenameChanged)
      self.hbox.addWidget(self.butChange)
      self.gBox2.setLayout(self.hbox)

      self.vlayout.addWidget(self.gBox2)

      self.buttonBox = QtWidgets.QDialogButtonBox(self)
      self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
      self.buttonBox.setCenterButtons(True)
      self.buttonBox.accepted.connect(self.do_ok)
      self.buttonBox.rejected.connect(self.do_cancel)
      self.vlayout.addWidget(self.buttonBox)
#
#  Radio button clicked, adjust file type
#
   def do_radio(self):
      if self.radioLifAscii.isChecked():
         self.outputextension=".txt"
      elif self.radioTxt75Ascii.isChecked():
         self.outputextension=".txt"
      elif self.radioTxt75AsciiNumbers.isChecked():
         self.outputextension=".txt"
      elif self.radioHepax.isChecked():
         self.outputextension=".rom"
      elif self.radioEramco.isChecked():
         self.outputextension=".rom"
      elif self.radioRaw.isChecked():
         self.outputextension=".raw"
      else:
         self.outputextension=".lif"
      self.outputfile=os.path.join(self.workdir, self.liffilename.lower()+self.outputextension)
      self.lblFilename.setText(self.outputfile)
#
#  enter output file name dialog
#
   def get_outputFilename(self):
      dialog=QtWidgets.QFileDialog()
      dialog.setWindowTitle("Select Output File")
      dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
      dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
      dialog.setNameFilters( ["All Files (*)"] )
      dialog.selectFile(self.liffilename.lower()+self.outputextension)
      dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog)
      if dialog.exec():
         return dialog.selectedFiles()
#
#  callback
#
#
#  change output file name
#

   def do_filenameChanged(self):
      flist= self.get_outputFilename()
      if flist is None:
         return
      self.outputfile=flist[0]
      self.lblFilename.setText(self.outputfile)
#
#  export
#
   def do_ok(self):
      if self.outputfile != "":

         if self.radioLifAscii.isChecked():
            exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename],add_path("liftext"),self.outputfile)
         elif self.radioTxt75Ascii.isChecked():
            exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename],add_path("liftext75"),self.outputfile)
         elif self.radioTxt75AsciiNumbers.isChecked():
            exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename], [add_path("liftext75"),"-n"],self.outputfile)
         elif self.radioEramco.isChecked():
            exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename],add_path("er41rom"),self.outputfile)
         elif self.radioHepax.isChecked():
            exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename],add_path("hx41rom"),self.outputfile)
         elif self.radioRaw.isChecked():
            exec_single(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename,self.outputfile])
         elif self.radioNone.isChecked():
            exec_single(self,[add_path("lifget"),self.lifimagefile,self.liffilename,self.outputfile])
      super().accept()
#
#  cancel: do nothing
#
   def do_cancel(self):
      super().reject()

   @staticmethod
   def exec(lifimagefile,liffilename,liffiletype,workdir):
      d=cls_lifexport(lifimagefile,liffilename,liffiletype,workdir)
      result= d.exec_() class cls_lifimport (QtWidgets.QDialog):

   def __init__(self,lifimagefile,workdir,parent= None):
      super().__init__()
      self.inputfile=""
      self.lifimagefile=lifimagefile
      self.workdir= workdir
      self.liffilename=""

      self.setWindowTitle("Import File")
      self.vlayout= QtWidgets.QVBoxLayout()
      self.setLayout(self.vlayout)

      self.gBox0=QtWidgets.QGroupBox("Input file")
      self.hbox=QtWidgets.QHBoxLayout()
      self.lblFilename=QtWidgets.QLabel(self.inputfile)
      self.hbox.addWidget(self.lblFilename)
      self.hbox.addStretch(1)
      self.butChange= QtWidgets.QPushButton("Change")
      self.butChange.clicked.connect(self.do_filenameChanged)
      self.hbox.addWidget(self.butChange)
      self.gBox0.setLayout(self.hbox)
      self.vlayout.addWidget(self.gBox0)

      self.gBox1=QtWidgets.QGroupBox("Preprocessing options")
      self.bGroup=QtWidgets.QButtonGroup()
      self.radioLif41= QtWidgets.QRadioButton("convert from ASCII to LIF-Text (HP-41)")
      self.radioLif71= QtWidgets.QRadioButton("convert from ASCII to LIF-Text (HP-71)")
      self.radioTxt75= QtWidgets.QRadioButton("convert from ASCII to HP-75 text, create new line numbers")
      self.radioTxt75Numbers= QtWidgets.QRadioButton("convert from ASCII to HP-75 text, take existing line numbers")
      self.radioHepax= QtWidgets.QRadioButton("convert HP-41 rom file to SDATA file (HEPAX)")
      self.radioEramco= QtWidgets.QRadioButton("convert HP-41 rom file to XM-41 file (Eramco MLDL-OS)")
      self.radioFocal= QtWidgets.QRadioButton("add LIF header to HP41 FOCAL raw file")
      self.radioNone= QtWidgets.QRadioButton("None")
      self.radioNone.setChecked(True)
      self.bGroup.addButton(self.radioLif41) 
      self.bGroup.addButton(self.radioLif71)
      self.bGroup.addButton(self.radioTxt75)
      self.bGroup.addButton(self.radioTxt75Numbers)
      self.bGroup.addButton(self.radioHepax)
      self.bGroup.addButton(self.radioEramco)
      self.bGroup.addButton(self.radioFocal)
      self.bGroup.addButton(self.radioNone)
      self.bGroup.buttonClicked.connect(self.do_butclicked)

      self.vbox=QtWidgets.QVBoxLayout()
      self.vbox.addWidget(self.radioLif41)
      self.vbox.addWidget(self.radioLif71)
      self.vbox.addWidget(self.radioTxt75)
      self.vbox.addWidget(self.radioTxt75Numbers)
      self.vbox.addWidget(self.radioHepax)
      self.vbox.addWidget(self.radioEramco)
      self.vbox.addWidget(self.radioFocal)

      self.hbox2=QtWidgets.QHBoxLayout()
      self.lbl=QtWidgets.QLabel("LIF Filename:")
      self.hbox2.addWidget(self.lbl)
      self.leditFileName=QtWidgets.QLineEdit(self)
      self.leditFileName.setText(self.liffilename)
      self.leditFileName.setMaxLength(10)
      self.validator = cls_LIF_validator()
      self.leditFileName.setValidator(self.validator)
      self.leditFileName.setEnabled(False)
      self.leditFileName.textChanged.connect(self.do_checkenable)
      self.hbox2.addWidget(self.leditFileName)
      self.vbox.addLayout(self.hbox2)
      self.gBox1.setLayout(self.vbox)
      self.gBox1.setEnabled(False)
      self.vlayout.addWidget(self.gBox1)
      self.vbox.addWidget(self.radioNone)
      self.vbox.addStretch(1)

      self.buttonBox = QtWidgets.QDialogButtonBox(self)
      self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
      self.buttonBox.setCenterButtons(True)
      self.buttonBox.accepted.connect(self.do_ok)
      self.buttonBox.rejected.connect(self.do_cancel)
      self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
      self.vlayout.addWidget(self.buttonBox)
#
#  dialog to enter input file name
#
   def get_inputFilename(self):
      dialog=QtWidgets.QFileDialog()
      dialog.setWindowTitle("Select Input File")
      dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
      dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile)
      dialog.setNameFilters( ["All Files (*)"] )
      dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog)
      if dialog.exec():
         return dialog.selectedFiles()

#
# callbacks
#

#
# change filename button
#
   def do_filenameChanged(self):
      flist= self.get_inputFilename()
      if flist is None:
         return
      self.inputfile=flist[0]
      self.lblFilename.setText(self.inputfile)
      self.gBox1.setEnabled(True)
      self.do_checkenable()
#
#  any radio button clicked, enable/disable lif filename entry, check ok button
#
   def do_butclicked(self,id):
      if id== self.radioNone:
         self.leditFileName.setEnabled(False)
      else:
         self.leditFileName.setEnabled(True)
      self.do_checkenable()

#
#  check, if the OK button can be enabled
#
   def do_checkenable(self):
      self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
      if self.radioNone.isChecked():
         self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
      else:
         if self.leditFileName.text() != "":
            self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
      return

#
#  OK button callback import file
#
   def do_ok(self):
      if self.inputfile != "":
         if self.radioNone.isChecked():
            if  cls_chk_import.exec(None, self.inputfile):
               exec_single(self,[add_path("lifput"),self.lifimagefile,self.inputfile])
         else:
            self.liffilename=self.leditFileName.text()
            if self.radioLif41.isChecked():
               exec_double_import(self,[add_path("textlif"),"-r 0",self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile)
            elif self.radioLif71.isChecked():
               exec_double_import(self,[add_path("textlif"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile)
            elif self.radioTxt75.isChecked():
               exec_double_import(self,[add_path("textlif75"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile)
            elif self.radioTxt75Numbers.isChecked():
               exec_double_import(self,[add_path("textlif75"),"-n",self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile)
            elif self.radioHepax.isChecked():
               exec_double_import(self,[add_path("rom41hx"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile)
            elif self.radioEramco.isChecked():
               exec_double_import(self,[add_path("rom41er"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile)
            elif self.radioFocal.isChecked():
               exec_double_import(self,[add_path("raw41lif"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile)
      super().accept()

#
#  cancel
#
   def do_cancel(self):
      super().reject()

   @staticmethod
   def exec(lifimagefile,workdir):
      d=cls_lifimport(lifimagefile,workdir)
      result= d.exec_() class cls_lifview(QtWidgets.QDialog):

   def __init__(self,workdir,parent= None):
      super().__init__()
      self.workdir=workdir
      self.outputfile=""

      self.setWindowTitle("View File")
      self.vlayout= QtWidgets.QVBoxLayout()
      self.setLayout(self.vlayout)

      self.viewer=QtWidgets.QTextEdit()
      self.viewer.setMinimumWidth(600)
      self.viewer.setMinimumHeight(600)
      self.viewer.setReadOnly(True)
      self.font=QtGui.QFont(FONT)
      self.font.setPixelSize(12)
      self.viewer.setFont(self.font)

      self.vlayout.addWidget(self.viewer)
      self.saveButton= QtWidgets.QPushButton("Save")
      self.saveButton.setFixedWidth(60)
      self.saveButton.clicked.connect(self.do_save)
      self.exitButton= QtWidgets.QPushButton("Exit")
      self.exitButton.setFixedWidth(60)
      self.exitButton.clicked.connect(self.do_exit)
      self.hlayout= QtWidgets.QHBoxLayout()
      self.hlayout.addWidget(self.saveButton)
      self.hlayout.addWidget(self.exitButton)
      self.vlayout.addLayout(self.hlayout)

   def set_text(self,output):
      self.viewer.setText(output)
      return
#
#  enter output file name dialog
#
   def get_outputFilename(self):
      dialog=QtWidgets.QFileDialog()
      dialog.setWindowTitle("Select Output File")
      dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
      dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
      dialog.setNameFilters( ["All Files (*)"] )
      dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog)
      if dialog.exec():
         return dialog.selectedFiles()
#
# save content to file
#
   def do_save(self):
      flist= self.get_outputFilename()
      if flist is None:
         return
      outputfile=flist[0]
      if os.access(self.outputfile,os.W_OK):
         reply=QtWidgets.QMessageBox.warning(self,'Warning',"Do you really want to overwrite file "+self.outputfile,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Cancel)
         if reply== QtWidgets.QMessageBox.Cancel:
            return
      try:
         if isWINDOWS() and PILCONFIG.get("pyilper","usebom"):
            outfile=open(outputfile,"a",encoding="UTF-8-SIG")
         else:
            outfile=open(outputfile,"a",encoding="UTF-8")

         outfile.write(str(self.viewer.toPlainText()))
         outfile.close()
      except OSError as e:
         reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot write to file: "+ e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
      return

#
# exit, do nothing
#
   def do_exit(self):
      super().accept()

#
# get file and pipe it to filter program, show output in editor window
#
   @staticmethod
   def exec(lifimagefile, liffilename, liffiletype,workdir,charset):
      d=cls_lifview(workdir)
      ft=get_finfo_name(liffiletype)
      call= get_finfo_type(ft)[1]
#
# decomp41 needs additional parameters (xmoms)
#
      if call == "decomp41":
         call= cls_chkxrom.exec()
#
# liftext75 has the option to show line numbers
#
      elif call == "liftext75":
         call= add_path(call)
         reply=QtWidgets.QMessageBox.question(None,'',"Show line numbers?",QtWidgets.QMessageBox.Yes,QtWidgets.QMessageBox.No)
         if reply== QtWidgets.QMessageBox.Yes:
            call= [ add_path(call), "-n"]
#
# all other lifutil progs
#
      else:
         call= add_path(call)
      output=exec_double_export(d,[add_path("lifget"),"-r",lifimagefile,liffilename],call,"")
#
# convert and show the file content
#
      if output is None:
         return
      d.set_text(barrconv(output,charset))
      result= d.exec_() class cls_liflabel (QtWidgets.QDialog):

   def __init__(self,lifimagefile,oldlabel,parent= None):
      super().__init__()
      self.lifimagefile=lifimagefile
      self.setWindowTitle("Label LIF image file")
      self.vlayout= QtWidgets.QVBoxLayout()
      self.setLayout(self.vlayout)

      self.lbl=QtWidgets.QLabel("Label:")
      self.vlayout.addWidget(self.lbl)
      self.leditLabel=QtWidgets.QLineEdit(self)
      self.leditLabel.setText(oldlabel)
      self.leditLabel.setMaxLength(6)
      self.validator = cls_LIF_validator()
      self.leditLabel.setValidator(self.validator)
      self.vlayout.addWidget(self.leditLabel)

      self.buttonBox = QtWidgets.QDialogButtonBox(self)
      self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
      self.buttonBox.setCenterButtons(True)
      self.buttonBox.accepted.connect(self.do_ok)
      self.buttonBox.rejected.connect(self.do_cancel)
      self.vlayout.addWidget(self.buttonBox)

   def do_ok(self):
      newlabel=self.leditLabel.text()
      if newlabel != "":
         exec_single(self,[add_path("liflabel"),self.lifimagefile, newlabel])
      else:
         exec_single(self,[add_path("liflabel"),"-c",self.lifimagefile])
      super().accept()

   def do_cancel(self):
      super().reject()

   @staticmethod
   def exec(lifimagefile,oldlabel):
      d=cls_liflabel(lifimagefile,oldlabel)
      result= d.exec_() def check_lifutils():
   set_lifutils_path("")
   required_version_installed=False
   installed_version= 0
#
#  check if we have a configured path to lifversion
#
   lifversionpath=PILCONFIG.get("pyilper","lifutilspath")
   if lifversionpath != "":
      installed_version=get_lifversion(lifversionpath)
#
#  not found, check if we have lifversion in the path
#
   if installed_version == 0:
      installed_version=get_lifversion("lifversion")
#
#  not found, use well known default locations
#
   if installed_version == 0:
#
#     Windows: query Registry to get install location from uninstaller info
#     local user installation preceeds system wide installation
#     Note: this requires at least lifutils version 1.7.7 (nsis package build)
#
      if isWINDOWS():
         path=""
         try:
            hkey=winreg.OpenKey(winreg.HKEY_CURRENT_USER,r"Software\\Microsoft\\Windows\\CurrentVersion\\uninstall\\"+LIFUTILS_UUID)
            path=winreg.QueryValueEx(hkey,"InstallLocation")[0]
            hkey.Close()
         except OSError as e:
            pass
         if path=="":
            try:
               hkey=winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r"Software\\Microsoft\\Windows\\CurrentVersion\\uninstall\\"+LIFUTILS_UUID)
               path=winreg.QueryValueEx(hkey,"InstallLocation")[0]
               hkey.Close()
            except OSError as e:
               pass
         if path!="":
            p=pathlib.Path(path)
            p=p / "lifversion"
            lifversionpath=str(p)
            installed_version=get_lifversion(lifversionpath)
      else:
#
#      Linux / mac OS: try /usr/ or /usr/local
#
         lifversionpath="/usr/bin/lifversion"
         installed_version=get_lifversion(lifversionpath)
         if installed_version == 0:
            lifversionpath="/usr/local/bin/lifversion"
            installed_version=get_lifversion(lifversionpath)
#
#  lifutils path found, set lifutils_path as prefix for calling the 
#  executables and set LIFUTILSXROMDIR as environment variable
#
   if installed_version != 0 and lifversionpath !="" :
      p= pathlib.Path(lifversionpath)
      set_lifutils_path(str(p.parent))
      if isWINDOWS():
         xromdir=p.parent / "xroms"
      else:
         xromdir=p.parents[1] / "share" / "lifutils" / "xroms"
      if xromdir.is_dir():
         os.environ["LIFUTILSXROMDIR"]=str(xromdir)
#
#  check if we have the required version
#
   if installed_version >= LIFUTILS_REQUIRED_VERSION:
      required_version_installed=True
   return required_version_installed, installed_version class cls_lifbarcode(QtWidgets.QDialog):
 

   def __init__(self):
      super().__init__()

   @staticmethod
   def exec (lifimagefile, liffilename, ft,papersize):
      d= cls_lifbarcode()
#
#     get output file name
#
      flist= cls_pdfprinter.get_pdfFilename()
      if flist is None:
         return
      output_filename= flist[0]
#
#     generate binary barcode data from lifutils prog41bar or sdatabar
#
      if ft== 0xE080:
         output=exec_double_export(d,[add_path("lifget"),"-r",lifimagefile,liffilename],[add_path("prog41bar")],"")
         title="Barcodes for HP-41 program file: "+liffilename
      else:
         output=exec_double_export(d,[add_path("lifget"),"-r",lifimagefile,liffilename],[add_path("sdatabar")],"")
         title="Barcodes for HP-41 data file: "+liffilename
      if output is None:
         return

#
#     initialize pdf printer
#
      pdfprinter=cls_pdfprinter(papersize,PDF_ORIENTATION_PORTRAIT, output_filename,title,True,1)
      pdfprinter.begin()
#
#     process binary barcode data and generate PDF file
#
      i=0
      row=0
      while i < len(output):
#        Process barcode, print title
#
         row+=1
         length=(output[i] &0xF)+1
         barcode_row= []
         i+=1
         for k in range(length):
           if i== len(output):
              return
           barcode_row.append(output[i])
           i+=1
         barcode_header="Row: "+str(row)
         barcode_item= cls_barcodeItem(barcode_header,barcode_row,BARCODE_HEIGHT, BARCODE_NARROW_W, BARCODE_WIDE_W, BARCODE_SPACING)
         pdfprinter.print_item(barcode_item)
#
      pdfprinter.end() class cls_pdfprinter(QtCore.QObject):

   def __init__(self,papersize, orientation, outputfilename, title, pagenumbering, columns):
      super().__init__()
      self.papersize= papersize
      self.orientation= orientation
      self.outputfilename= outputfilename
      self.title= title
      self.pagenumbering= pagenumbering
      self.columns= columns
#
#     initialize printer and printer scene
#
      self.printer = QtPrintSupport.QPrinter (QtPrintSupport.QPrinter.HighResolution)
      if self.orientation== PDF_ORIENTATION_PORTRAIT:
         self.printer.setOrientation(QtPrintSupport.QPrinter.Portrait)
      else:
         self.printer.setOrientation(QtPrintSupport.QPrinter.Landscape)
      self.printer.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat)
      self.pdfscene=QtWidgets.QGraphicsScene()
#
#     page set up, we use 1/10 mm as scene units
#
      if self.papersize== PDF_FORMAT_A4:
         self.printer.setPageSize(QtPrintSupport.QPrinter.A4)
         self.scene_w= 2100
         self.scene_h=2970
         self.pdfscene.setSceneRect(0,0,self.scene_w,self.scene_h)
      else:
         self.printer.setPageSize(QtPrintSupport.QPrinter.Letter)
         self.scene_w= 2160
         self.scene_h= 2790
         self.pdfscene.setSceneRect(0,0,self.scene_w,self.scene_h)

      self.pdfpainter = QtGui.QPainter()
      self.font=QtGui.QFont()
      self.font.setPointSize(20)
#
#     set output filename
#
      self.printer.setOutputFileName(self.outputfilename)
#
#     variables
#
      self.pdfitems=[]
      self.anzitems=0
      self.row=0
      self.column=0
      self.topy=0
      self.x=0
      self.y=0
      self.max_y= self.scene_h-PDF_MARGINS
      self.columnshift=(self.scene_w- 2* PDF_MARGINS)/ self.columns
#
#     start printing
#
   def begin(self):
      self.pdfpainter.begin(self.printer)
      self.titleitem=QtWidgets.QGraphicsSimpleTextItem(self.title)
      self.titleitem.setFont(self.font)
      self.pdfscene.addItem(self.titleitem)
      self.titleitem.setPos(PDF_MARGINS,PDF_MARGINS)
      self.pageno=1
      if self.pagenumbering:
         self.pagenumberitem=QtWidgets.QGraphicsSimpleTextItem("Page "+str(self.pageno))
         self.pagenumberitem.setFont(self.font)
         self.pdfscene.addItem(self.pagenumberitem)
         self.pagenumberitem.setPos(self.scene_w-self.pagenumberitem.boundingRect().width(),PDF_MARGINS)
#
#    init variables
#
      self.pdfitems=[]
      self.anzitems=0
      self.row=0
      self.column=0
      self.top_y=PDF_MARGINS+self.titleitem.boundingRect().height()+40
      self.x= PDF_MARGINS
      self.y= self.top_y

   def print_item(self,item):
#
#     no more rows on page
#
      if self.y > self.max_y:
#
#        no more columns, issue page break
#
         if self.column == self.columns-1:
            self.pdfscene.render(self.pdfpainter)
            self.printer.newPage()
            self.x=PDF_MARGINS
            self.y= self.top_y

            for l in reversed(range(self.anzitems)):
               self.pdfscene.removeItem(self.pdfitems[l])
               del self.pdfitems[-1]
            self.pageno+=1
            if self.pagenumbering:
               self.pagenumberitem.setText("Page "+str(self.pageno))
            self.anzitems=0
            self.column=0
            self.row=0
#
#        next column
#
         else:
            self.x+= self.columnshift
            self.y= self.top_y
            self.column+=1
            self.row=0
#
#     add item to scene
#
      self.row+=1
      self.pdfscene.addItem(item)
      self.y+= item.boundingRect().height()
      item.setPos(self.x,self.y)
      self.anzitems+=1
      self.pdfitems.append(item)
#
#  output remaining data and terminate printing
#
   def end(self):
      if self.anzitems > 0:
         self.pdfscene.render(self.pdfpainter)
         for l in reversed(range(self.anzitems)):
            self.pdfscene.removeItem(self.pdfitems[l])
            del self.pdfitems[-1]
#
#     clean up
#
      self.pdfscene.removeItem(self.titleitem)
      if self.pagenumbering:
         self.pdfscene.removeItem(self.pagenumberitem)
      self.pdfpainter.end()
 

#     file name input dialogue for pdf print file
#
   @staticmethod
   def get_pdfFilename():
      dialog=QtWidgets.QFileDialog()
      dialog.setWindowTitle("Enter PDF file name")
      dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
      dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
      dialog.setDefaultSuffix("pdf")
      dialog.setNameFilters( ["PDF (*.pdf )", "All Files (*)"] )
      dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog)
      if dialog.exec():
         return dialog.selectedFiles() class cls_textItem(QtWidgets.QGraphicsItem):

   def __init__(self,text):
      super().__init__()
      self.text=text
#     self.font= QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont)
#     self.font.setStyleHint(QtGui.QFont.TypeWriter)
      self.font= QtGui.QFont(FONT)
      self.font.setPointSize(2)
      metrics= QtGui.QFontMetrics(self.font)
      self.font_h= metrics.height()
      self.font_w= metrics.width("A")
      self.spacing=20
      self.h= self.font_h+self.spacing*2
      self.w= len(text)* self.font_w
      self.rect= QtCore.QRectF(0,0,self.w,self.h)

   def setPos(self,x,y):
      super().setPos(x,y-self.h)

   def boundingRect(self):
      return self.rect

   def paint(self,painter,option,widget):
      posx=0
      posy=self.font_h
      painter.setFont(self.font)
      painter.drawText(posx,posy,self.text)
from PyQt5 import QtCore, QtGui, QtWidgets
from .pilcore import *
from .pildevbase import cls_pildevbase
from .pilwidgets import cls_tabgeneric, T_STRING, T_INTEGER, O_DEFAULT
from .pilconfig import PilConfigError, PILCONFIG
from .pilcharconv import CHARSET_HP71, charsets
from .lifutils import cls_LifFile,cls_LifDir,LifError, getLifInt, putLifInt
from .lifcore import *
from .lifexec import cls_lifpack, cls_lifpurge, cls_lifrename, cls_lifexport, cls_lifimport, cls_lifview, cls_liflabel, check_lifutils, cls_lifbarcode
from .pilpdf import cls_pdfprinter,cls_textItem
import time
import threading
import os
#
   def setDrivetypeChecked(self):
      i=0
      for w in self.gbox_buttonlist:
         if i == self.drivetype:
            w.setChecked(True)
         else:
            w.setChecked(False)
         i+=1
#
#  becomes visible, activate update timer
#
   def becomes_visible(self):
      self.timer.start(REFRESH_RATE)
      return
#
#  becomes invisible, deactivate update timer
#
   def becomes_invisible(self):
      self.timer.stop()
      return
#
#  Callbacks
#
   def do_filenameChanged(self):
      flist= self.get_lifFilename()
      if flist is None:
         return
      status, tracks, surfaces, blocks= self.lifMediumCheck(flist[0],False)
      if status:
         self.filename=flist[0]
      else:
         self.filename=""
      PILCONFIG.put(self.name,'filename',self.filename)
      try:
         PILCONFIG.save()
      except PilConfigError as e:
         reply=QtWidgets.QMessageBox.critical(self.parent.parent.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)

      if self.pildevice is not None:
         self.pildevice.setlocked(True)
         self.pildevice.sethdisk(self.filename,tracks,surfaces,blocks)
         self.pildevice.setlocked(False)
      self.lblFilename.setText(self.filename)
      self.lifdir.setFileName(self.filename)
      if self.filename=="":
         self.lifdir.clear()
      else:
         self.lifdir.refresh()
      self.toggle_controls()

   def do_drivetypeChanged(self):
      i=0
      for w in self.gbox_buttonlist:
         if w.isChecked():
            self.drivetype=i
            break
         i+=1
      PILCONFIG.put(self.name,'drivetype', self.drivetype)
#
#     remove filename
#
      if self.filename != "":
         self.filename=""
         PILCONFIG.put(self.name,'filename',self.filename)
         self.lblFilename.setText(self.filename)
         self.lifdir.clear()
         reply=QtWidgets.QMessageBox.warning(self.parent.parent.ui,'Warning',"Drive type changed. You have to reopen the LIF image file",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
      did,aid= self.deviceinfo[self.drivetype]
      try:
         PILCONFIG.save()
      except PilConfigError as e:
         reply=QtWidgets.QMessageBox.critical(self.parent.parent.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
      if self.pildevice is not None:
         self.pildevice.setlocked(True)
         self.pildevice.setdevice(did,aid)
         self.pildevice.setlocked(False)
      self.toggle_controls()

   def do_pack(self):
      cls_lifpack.exec(self.filename)
      self.lifdir.refresh()

   def do_import(self):
      workdir=PILCONFIG.get('pyilper','workdir')
      cls_lifimport.exec(self.filename, workdir)
      self.lifdir.refresh()

   def do_label(self):
      oldlabel=self.lifdir.getLabel()
      cls_liflabel.exec(self.filename, oldlabel)
      self.lifdir.refresh()

   def do_dirlist(self):
#
#     get medium summary, return if blank or no medium
#
      mediumsummary= self.lifdir.getMediumSummary()
      linebreak= mediumsummary.find("Label")
      if linebreak == -1:
         return
      mediuminfo= mediumsummary[:linebreak]
      labelinfo=mediumsummary[linebreak:]
#
#     get output file name
#
      flist= cls_pdfprinter.get_pdfFilename()
      if flist is None:
         return
      output_filename= flist[0]
#
#     initialize pdf printer
#
      title="Directory listing of: "+self.filename
      pdfprinter=cls_pdfprinter(self.papersize,PDF_ORIENTATION_PORTRAIT, output_filename,title,True,1)
      pdfprinter.begin()

      model= self.lifdir.getModel()
      cols=6
      rows= self.lifdir.getRowCount()

      pdfprinter.print_item(cls_textItem(mediuminfo))