home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / hplip / ui / faxsendjobform.py < prev    next >
Encoding:
Python Source  |  2006-08-30  |  35.2 KB  |  995 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2006 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20. #
  21.  
  22. # Std Lib
  23. import glob, Queue, socket, struct
  24.  
  25. # Local
  26. from base.g import *
  27. from base import utils, device, magic, pml, service, msg
  28.  
  29. try:
  30.     from fax import fax
  31. except ImportError:
  32.     # This can fail on Python < 2.3 due to the datetime module
  33.     log.error("Fax send disabled - Python 2.3+ required.")
  34.     sys.exit(1)
  35.  
  36. from prnt import cups
  37.  
  38. # Qt/UI
  39. from qt import *
  40. from faxsendjobform_base import FaxSendJobForm_base
  41. from waitform import WaitForm
  42. from faxsettingsform import FaxSettingsForm
  43. from faxallowabletypesdlg import FaxAllowableTypesDlg
  44.  
  45. coverpages_enabled = False
  46. try:
  47.     import reportlab
  48. except ImportError:
  49.     log.error("Coverpages disabled. Reportlab not installed.")
  50. else:
  51.     from fax import coverpages
  52.     from coverpageform import CoverpageForm
  53.     coverpages_enabled = True
  54.  
  55.  
  56. class FileListViewItem(QListViewItem):
  57.     def __init__(self, parent, title, mime_type_desc, path, str_pages):
  58.         QListViewItem.__init__(self, parent, title, mime_type_desc, str_pages)
  59.         self.path = path
  60.  
  61.  
  62. class FaxSendJobForm(FaxSendJobForm_base):
  63.  
  64.     def __init__(self, sock, device_uri, printer_name, args, 
  65.                  parent=None, name=None, 
  66.                  modal=0, fl=0):
  67.  
  68.         FaxSendJobForm_base.__init__(self, parent, name, modal, fl)
  69.         icon = QPixmap(os.path.join(prop.image_dir, 'HPmenu.png'))
  70.         self.setIcon(icon)
  71.         self.sock = sock
  72.         self.init_failed = False
  73.         self.device_uri = device_uri
  74.         self.dev = None
  75.         self.printer_name = printer_name
  76.         bus = 'cups'
  77.         self.document_num = 1
  78.         self.filename = ''
  79.         self.waitdlg = None
  80.         self.recipient_list = []
  81.         self.username = prop.username
  82.         self.args = args
  83.  
  84.         self.update_queue = Queue.Queue() # UI updates from send thread
  85.         self.event_queue = Queue.Queue() # UI events (from hpssd) to send thread
  86.  
  87.         pix = QPixmap(os.path.join(prop.image_dir, 'folder_remove.png'))
  88.         self.delFileButton.setPixmap(pix)
  89.  
  90.         pix = QPixmap(os.path.join(prop.image_dir, 'status_refresh.png'))
  91.         self.refreshToolButton.setPixmap(pix)
  92.  
  93.         pix = QPixmap(os.path.join(prop.image_dir, 'up.png'))
  94.         self.upFileButton.setPixmap(pix)
  95.  
  96.         pix = QPixmap(os.path.join(prop.image_dir, 'down.png'))
  97.         self.downFileButton.setPixmap(pix)
  98.  
  99.         self.fileListView.setSorting(-1)
  100.  
  101.         # TODO: Hook these up. Need to read current settings
  102.         # from PDD file for the current device queue
  103.         #self.paperSizeButtonGroup.setEnabled(False)
  104.         #self.qualityButtonGroup.setEnabled(False)
  105.  
  106.         self.addCoverpagePushButton.setEnabled(coverpages_enabled)
  107.         self.cover_page_func, cover_page_png = None, None
  108.         self.cover_page_message = ''
  109.         self.cover_page_re = ''
  110.         self.cover_page_name = ''
  111.  
  112.         self.event_handler = self.addFileFromJob
  113.  
  114.         self.file_list = []
  115.  
  116.         self.db =  fax.FaxAddressBook() # kirbybase instance
  117.  
  118.         self.allowable_mime_types = cups.getAllowableMIMETypes()
  119.         self.allowable_mime_types.append("application/hplip-fax")
  120.         self.allowable_mime_types.append("application/x-python")
  121.  
  122.         log.debug(self.allowable_mime_types)
  123.  
  124.         self.MIME_TYPES_DESC = \
  125.         {
  126.             "application/pdf" : (self.__tr("PDF Document"), '.pdf'),
  127.             "application/postscript" : (self.__tr("Postscript Document"), '.ps'),
  128.             "application/vnd.hp-HPGL" : (self.__tr("HP Graphics Language File"), '.hgl, .hpg, .plt, .prn'),
  129.             "application/x-cshell" : (self.__tr("C Shell Script"), '.csh'),
  130.             "application/x-perl" : (self.__tr("Perl Script"), '.pl'),
  131.             "application/x-python" : (self.__tr("Python Program"), '.py'),
  132.             "application/x-shell" : (self.__tr("Shell Script"), '.sh'),
  133.             "text/plain" : (self.__tr("Plain Text"), '.txt, .log, etc'),
  134.             "text/html" : (self.__tr("HTML Dcoument"), '.htm, .html'),
  135.             "image/gif" : (self.__tr("GIF Image"), '.gif'),
  136.             "image/png" : (self.__tr("PNG Image"), '.png'),
  137.             "image/jpeg" : (self.__tr("JPEG Image"), '.jpg, .jpeg'),
  138.             "image/tiff" : (self.__tr("TIFF Image"), '.tif, .tiff'),
  139.             "image/x-bitmap" : (self.__tr("Bitmap (BMP) Image"), '.bmp'),
  140.             "image/x-photocd" : (self.__tr("Photo CD Image"), '.pcd'),
  141.             "image/x-portable-anymap" : (self.__tr("Portable Image (PNM)"), '.pnm'),
  142.             "image/x-portable-bitmap" : (self.__tr("Portable B&W Image (PBM)"), '.pbm'),
  143.             "image/x-portable-graymap" : (self.__tr("Portable Grayscale Image (PGM)"), '.pgm'),
  144.             "image/x-portable-pixmap" : (self.__tr("Portable Color Image (PPM)"), '.ppm'),
  145.             "image/x-sgi-rgb" : (self.__tr("SGI RGB"), '.rgb'),
  146.             "image/x-xbitmap" : (self.__tr("X11 Bitmap (XBM)"), '.xbm'),
  147.             "image/x-xpixmap" : (self.__tr("X11 Pixmap (XPM)"), '.xpm'),
  148.             "image/x-sun-raster" : (self.__tr("Sun Raster Format"), '.ras'),
  149.             "application/hplip-fax" : (self.__tr("HP Fax"), '.g3'),
  150.             "application/hplip-fax-coverpage" : (self.__tr("HP Fax Coverpage"), 'n/a'),
  151.         }
  152.  
  153.         if self.device_uri and self.printer_name:
  154.             log.error("You may not specify both a printer (-p) and a device (-d).")
  155.             self.FailureUI(self.__tr("<p><b>You may not specify both a printer (-p) and a device (-d)."))
  156.             self.device_uri, self.printer_name = None, None
  157.             self.init_failed = True
  158.  
  159.         self.cups_printers = cups.getPrinters()
  160.         log.debug(self.cups_printers)
  161.  
  162.         if not self.device_uri and not self.printer_name:
  163.             t = device.probeDevices(self.sock, bus=bus, filter='fax')
  164.             probed_devices = []
  165.  
  166.             for d in t:
  167.                 probed_devices.append(d.replace('hp:/', 'hpfax:/'))
  168.  
  169.             probed_devices = utils.uniqueList(probed_devices)
  170.             log.debug(probed_devices)
  171.  
  172.             max_deviceid_size, x, devices = 0, 0, {}
  173.  
  174.             for d in probed_devices:
  175.                 printers = []
  176.                 for p in self.cups_printers:
  177.                     if p.device_uri == d:
  178.                         printers.append(p.name)
  179.                 devices[x] = (d, printers)
  180.                 x += 1
  181.                 max_deviceid_size = max(len(d), max_deviceid_size)
  182.  
  183.             if x == 0:
  184.                 from nodevicesform import NoDevicesForm
  185.                 self.FailureUI(self.__tr("<p><b>No devices found.</b><p>Please make sure your device is properly installed and try again."))
  186.                 self.init_failed = True
  187.  
  188.             elif x == 1:
  189.                 log.info(utils.bold("Using device: %s" % devices[0][0]))
  190.                 self.device_uri = devices[0][0]
  191.  
  192.             else:
  193.                 from chooseprinterdlg import ChoosePrinterDlg
  194.                 dlg = ChoosePrinterDlg(self.cups_printers, ['hpfax'])
  195.                 if dlg.exec_loop() == QDialog.Accepted:
  196.                     self.device_uri = dlg.device_uri
  197.                 else:
  198.                     self.init_failed = True
  199.  
  200.         cmd_print, cmd_scan, cmd_pcard, \
  201.             cmd_copy, cmd_fax, cmd_fab = utils.deviceDefaultFunctions()
  202.  
  203.         self.cmd_fab = user_cfg.commands.fab or cmd_fab
  204.         log.debug("FAB command: %s" % self.cmd_fab)
  205.  
  206.         QTimer.singleShot(0, self.InitialUpdate)
  207.  
  208.  
  209.     # ************************************** Device status
  210.     def InitialUpdate(self):
  211.         if self.init_failed:
  212.             self.close()
  213.             return      
  214.  
  215.         self.printer_list = []
  216.  
  217.         self.dev = fax.FaxDevice(device_uri=self.device_uri, 
  218.                                  printer_name=self.printer_name)
  219.  
  220.         self.device_uri = self.dev.device_uri
  221.  
  222.         log.debug("Device URI=%s" %self.device_uri)
  223.         self.DeviceURIText.setText(self.device_uri)
  224.  
  225.         for p in self.cups_printers:
  226.             if p.device_uri == self.device_uri:
  227.                 self.printer_list.append(p.name)
  228.  
  229.         if not self.printer_list:
  230.             self.FailureUI(self.__tr("<b>No appropriate CUPS fax queue is setup.</b><p>Please install a CUPS queue for device '%1' using the HPLIP Fax PPD file. (You could use 'hp-setup' to do this.)").arg(self.device_uri))
  231.             self.close()
  232.             return
  233.  
  234.         for p in self.printer_list:
  235.             self.printerNameComboBox.insertItem(p)
  236.  
  237.         self.UpdatePrinterStatus()
  238.  
  239.         if self.printer_name is None:
  240.             self.printerNameComboBox.setCurrentItem(0)
  241.         elif self.printer_name in self.printer_list:
  242.             self.printerNameComboBox.setCurrentText(self.printer_name)
  243.  
  244.         self.current_printer = str(self.printerNameComboBox.currentText())
  245.  
  246.         self.UpdatePrinterInfo()
  247.  
  248.         self.UpdateIndividualList()
  249.         self.UpdateGroupList()
  250.         self.CheckSendButtons()
  251.         self.selection = []
  252.  
  253.         if len(self.args):
  254.             for a in self.args:
  255.                 if os.path.exists(a):
  256.                     self.processFile(a, a)
  257.                 else:
  258.                     self.FailureUI(self.__tr("<b>File not found:</b><p>%1").arg(a))
  259.  
  260.  
  261.     def UpdatePrinterStatus(self):
  262.         QApplication.setOverrideCursor(QApplication.waitCursor)
  263.  
  264.         try:
  265.             try:
  266.                 self.dev.open()
  267.             except Error, e:
  268.                 log.warn(e.msg)
  269.  
  270.             try:
  271.                 self.dev.queryDevice(quick=True)
  272.             except Error, e:
  273.                 log.error("Query device error (%s)." % e.msg)
  274.                 self.dev.error_state = ERROR_STATE_ERROR
  275.  
  276.         finally:
  277.             self.dev.close()
  278.             QApplication.restoreOverrideCursor()
  279.  
  280.  
  281.         if self.dev.device_state == DEVICE_STATE_NOT_FOUND:
  282.             self.FailureUI(self.__tr("<b>Unable to communicate with device:</b><p>%1").arg(self.device_uri))
  283.  
  284.  
  285.     def UpdatePrinterInfo(self):
  286.         for p in self.cups_printers:
  287.             if p.name == self.current_printer:
  288.  
  289.                 try:
  290.                     self.LocationText.setText(p.location)
  291.                 except AttributeError:
  292.                     self.LocationText.setText('')
  293.  
  294.                 try:
  295.                     self.CommentText.setText(p.info)
  296.                 except AttributeError:
  297.                     self.CommentText.setText('')
  298.  
  299.                 self.appPrintNoteLabel.setText(self.__tr("<i>You can also add items by printing documents from an application using the CUPS printer '%1'.</i>").arg(self.current_printer))
  300.  
  301.                 break  
  302.         else: # No CUPS queue for this device
  303.             log.error("No CUPS queue found!")
  304.  
  305.  
  306.     def refreshToolButton_clicked(self):
  307.         self.UpdatePrinterStatus()
  308.  
  309.     def printerNameComboBox_highlighted(self, a0):
  310.         self.current_printer = str(a0)
  311.         self.UpdatePrinterInfo()
  312.  
  313.     # ************************************** Recepient handling
  314.     def UpdateIndividualList(self):
  315.         already_checked = []
  316.  
  317.         i = self.individualSendListView.firstChild()
  318.         while i is not None:
  319.             if i.isOn():
  320.                 already_checked.append(str(i.text(1)))
  321.  
  322.             i = i.itemBelow()
  323.  
  324.         self.individualSendListView.clear()
  325.  
  326.         all_entries = self.db.AllRecordEntries()
  327.  
  328.         if len(all_entries) > 0:
  329.  
  330.             for i in all_entries:
  331.                 j = QCheckListItem(self.individualSendListView, '', 
  332.                                     QCheckListItem.CheckBox)
  333.  
  334.                 j.setText(1, i.name)
  335.                 j.setText(2, i.fax)
  336.                 j.setText(3, i.notes)
  337.  
  338.                 if i.name in already_checked:
  339.                     j.setOn(True)
  340.  
  341.  
  342.     def UpdateGroupList(self):
  343.         already_checked = []
  344.  
  345.         i = self.groupSendListView.firstChild()
  346.         while i is not None:
  347.             if i.isOn():
  348.                 already_checked.append(str(i.text(1)))
  349.             i = i.itemBelow()
  350.  
  351.         self.groupSendListView.clear()
  352.         all_groups = self.db.AllGroups()
  353.  
  354.         for g in all_groups:
  355.             j = QCheckListItem(self.groupSendListView, '', 
  356.                                QCheckListItem.CheckBox)
  357.  
  358.             j.setText(1, g)
  359.             j.setText(2, ', '.join(self.db.GroupEntries(g)))
  360.  
  361.             if g in already_checked:
  362.                 j.setOn(True)
  363.  
  364.  
  365.     def CheckSendButtons(self):
  366.         self.sendNowButton.setEnabled(bool(self.recipient_list and self.file_list))
  367.  
  368.  
  369.     def addressBookButton_clicked(self):
  370.         log.debug(self.cmd_fab)
  371.         cmd = ''.join([self.dev.device_vars.get(x, x) \
  372.                          for x in self.cmd_fab.split('%')])
  373.         log.debug(cmd)
  374.  
  375.         path = cmd.split()[0]
  376.         args = cmd.split()
  377.  
  378.         self.CleanupChildren()
  379.         os.spawnvp(os.P_NOWAIT, path, args)        
  380.  
  381.  
  382.     def CleanupChildren(self):
  383.         log.debug("Cleaning up child processes.")
  384.         try:
  385.             os.waitpid(-1, os.WNOHANG)
  386.         except OSError:
  387.             pass
  388.  
  389.  
  390.     def sendLaterButton_clicked(self):
  391.         print "FaxSendJobForm.sendLaterButton_clicked(): Not implemented yet"
  392.  
  393.  
  394.     def individualSendListView_clicked(self,a0):
  395.         self.UpdateSelectionEdit()
  396.  
  397.     def groupSendListView_clicked(self,a0):
  398.         self.UpdateSelectionEdit()
  399.  
  400.  
  401.     def UpdateSelectionEdit(self):
  402.         self.recipient_list = []
  403.         i = self.groupSendListView.firstChild()
  404.         while i is not None:
  405.             if i.isOn():
  406.                 group = str(i.text(1))
  407.                 self.recipient_list.extend(self.db.GroupEntries(group))
  408.             i = i.itemBelow()
  409.  
  410.         i = self.individualSendListView.firstChild()
  411.         while i is not None:
  412.             if i.isOn():
  413.                 self.recipient_list.append(str(i.text(1)))
  414.             i = i.itemBelow()
  415.  
  416.         log.debug("List=%s" % self.recipient_list)
  417.         self.recipient_list = utils.uniqueList(self.recipient_list)
  418.         log.debug("Unique list=%s" % self.recipient_list)
  419.  
  420.         self.selectionEdit.setText(', '.join(self.recipient_list))
  421.         self.CheckSendButtons()
  422.  
  423.  
  424.     # ************************************** Send fax handling
  425.     def sendNowButton_clicked(self):
  426.         phone_num_list = []
  427.  
  428.         log.debug("Current printer=%s" % self.current_printer)
  429.         ppd_file = cups.getPPD(self.current_printer)
  430.  
  431.         if ppd_file is not None and os.path.exists(ppd_file):
  432.             if file(ppd_file, 'r').read().find('HPLIP Fax') == -1:
  433.                 self.FailureUI(self.__tr("<b>Fax configuration error.</b><p>The CUPS fax queue for '%1' is incorrectly configured.<p>Please make sure that the CUPS fax queue is configured with the 'HPLIP Fax' Model/Driver.").arg(self.current_printer))
  434.                 return
  435.  
  436.         QApplication.setOverrideCursor(QApplication.waitCursor)
  437.  
  438.         try:
  439.             try:
  440.                 self.dev.open()
  441.             except Error, e:
  442.                 log.warn(e.msg)
  443.  
  444.             try:
  445.                 self.dev.queryDevice(quick=True)
  446.             except Error, e:
  447.                 log.error("Query device error (%s)." % e.msg)
  448.                 self.dev.error_state = ERROR_STATE_ERROR
  449.  
  450.         finally:
  451.             self.dev.close()
  452.             QApplication.restoreOverrideCursor()
  453.  
  454.  
  455.         if self.dev.error_state in (ERROR_STATE_WARNING, ERROR_STATE_ERROR, ERROR_STATE_BUSY):
  456.             self.FailureUI(self.__tr("<b>Device is busy or in an error state (code=%1)</b><p>Please wait for the device to become idle or clear the error and try again.").arg(self.dev.status_code))
  457.             return
  458.  
  459.         # Check to make sure queue in CUPS is idle
  460.         self.cups_printers = cups.getPrinters()
  461.         for p in self.cups_printers:
  462.             if p.name == self.current_printer:
  463.                 if p.state == cups.IPP_PRINTER_STATE_STOPPED:
  464.                     self.FailureUI(self.__tr("<b>The CUPS queue for '%1' is in a stopped or busy state.</b><p>Please check the queue and try again.").arg(self.current_printer))
  465.                     return
  466.                 break
  467.  
  468.         log.debug("Recipient list:")
  469.  
  470.         for p in self.recipient_list:
  471.             a = fax.AddressBookEntry(self.db.select(['name'], [p])[0])
  472.             phone_num_list.append(a)
  473.             log.debug("Name=%s Number=%s" % (a.name, a.fax))
  474.  
  475.         log.debug("File list:")
  476.  
  477.  
  478.         for f in self.file_list:
  479.             log.debug(str(f))
  480.  
  481.         self.event_handler = self.sendFaxEvent # Switch to send event handler
  482.  
  483.         service.sendEvent(self.sock, EVENT_START_FAX_JOB, device_uri=self.device_uri)
  484.  
  485.         if not self.dev.sendFaxes(phone_num_list, self.file_list, self.cover_page_message, 
  486.                                   self.cover_page_re, self.cover_page_func, self.current_printer,
  487.                                   self.update_queue, self.event_queue):
  488.  
  489.             self.FailureUI(self.__tr("<b>Send fax is active.</b><p>Please wait for operation to complete."))
  490.             service.sendEvent(self.sock, EVENT_FAX_JOB_FAIL, device_uri=self.device_uri)
  491.             return
  492.  
  493.  
  494.         self.waitdlg = WaitForm(0, self.__tr("Initializing..."), self.send_fax_canceled, self, modal=1)
  495.         self.waitdlg.show()
  496.  
  497.         self.send_fax_timer = QTimer(self, "SendFaxTimer")
  498.         self.connect(self.send_fax_timer, SIGNAL('timeout()'), self.send_fax_timer_timeout)
  499.         self.send_fax_timer.start(1000) # 1 sec UI updates
  500.  
  501.  
  502.     def send_fax_canceled(self):
  503.         self.event_queue.put((fax.EVENT_FAX_SEND_CANCELED, '', '', ''))
  504.         service.sendEvent(self.sock, EVENT_FAX_JOB_CANCELED, device_uri=self.device_uri)
  505.  
  506.  
  507.     def send_fax_timer_timeout(self):
  508.         while self.update_queue.qsize():
  509.             try:
  510.                 status, page_num, phone_num = self.update_queue.get(0)
  511.             except Queue.Empty:
  512.                 break
  513.  
  514.             if status == fax.STATUS_IDLE:
  515.                 self.send_fax_timer.stop()
  516.  
  517.                 if self.waitdlg is not None:
  518.                     self.waitdlg.hide()
  519.                     self.waitdlg.close()
  520.                     self.waitdlg = None
  521.  
  522.                 self.event_handler = self.addFileFromJob # Reset handler
  523.  
  524.             elif status == fax.STATUS_PROCESSING_FILES:
  525.                 self.waitdlg.setMessage(self.__tr("Processing page %1...").arg(page_num))
  526.  
  527.             elif status == fax.STATUS_DIALING:
  528.                 self.waitdlg.setMessage(self.__tr("Dialing %1...").arg(phone_num))
  529.  
  530.             elif status == fax.STATUS_CONNECTING:
  531.                 self.waitdlg.setMessage(self.__tr("Connecting to %1...").arg(phone_num))
  532.  
  533.             elif status == fax.STATUS_SENDING:
  534.                 self.waitdlg.setMessage(self.__tr("Sending page %1 to %2...").arg(page_num).arg(phone_num))
  535.  
  536.             elif status == fax.STATUS_CLEANUP:
  537.                 self.waitdlg.setMessage(self.__tr("Cleaning up..."))
  538.  
  539.             elif status in (fax.STATUS_ERROR, fax.STATUS_BUSY, fax.STATUS_COMPLETED):
  540.                 self.send_fax_timer.stop()
  541.  
  542.                 if self.waitdlg is not None:
  543.                     self.waitdlg.hide()
  544.                     self.waitdlg.close()
  545.                     self.waitdlg = None
  546.  
  547.                 self.event_handler = self.addFileFromJob # Reset handler
  548.  
  549.                 if status  == fax.STATUS_ERROR:
  550.                     self.FailureUI(self.__tr("<b>Fax send error.</b><p>"))
  551.                     service.sendEvent(self.sock, EVENT_FAX_JOB_FAIL, device_uri=self.device_uri)
  552.  
  553.                 elif status == fax.STATUS_BUSY:
  554.                     self.FailureUI(self.__tr("<b>Fax device is busy.</b><p>Please try again later."))
  555.                     service.sendEvent(self.sock, EVENT_FAX_JOB_FAIL, device_uri=self.device_uri)
  556.  
  557.                 elif status == fax.STATUS_COMPLETED:
  558.                     self.SuccessUI()
  559.                     service.sendEvent(self.sock, EVENT_END_FAX_JOB, device_uri=self.device_uri)
  560.  
  561.  
  562.     # ************************************** File and event handling
  563.     def delFileButton_clicked(self):
  564.         try:
  565.             path = self.fileListView.currentItem().path
  566.         except AttributeError:
  567.             return
  568.         else:
  569.             temp = self.file_list[:]
  570.             index = 0
  571.             for p, t, d, x, g in temp:
  572.                 if p == path:
  573.                     del self.file_list[index]
  574.  
  575.                     if t == 'application/hplip-fax-coverpage':
  576.                         self.addCoverpagePushButton.setEnabled(coverpages_enabled)
  577.                         self.editCoverpagePushButton.setEnabled(False)
  578.  
  579.                     self.UpdateFileList()
  580.                     break
  581.  
  582.                 index += 1
  583.  
  584.             self.CheckSendButtons()
  585.  
  586.  
  587.     def addFilePushButton_clicked(self):
  588.         self.processFile(self.filename, self.filename)
  589.         self.CheckSendButtons()
  590.  
  591.     def upFileButton_clicked(self):
  592.         try:
  593.             path = self.fileListView.currentItem().path
  594.         except AttributeError:
  595.             return
  596.         else:
  597.             temp = self.file_list[:]
  598.             index = 0
  599.             for p, t, d, x, c in temp:
  600.                 if p == path:
  601.                     self.file_list_move_up(p)
  602.                     self.UpdateFileList(index-1)
  603.                     break
  604.  
  605.                 index += 1
  606.  
  607.  
  608.     def downFileButton_clicked(self):
  609.         try:
  610.             path = self.fileListView.currentItem().path
  611.         except AttributeError:
  612.             return
  613.         else:
  614.             temp = self.file_list[:]
  615.             index = 0
  616.             for p, t, d, x, c in temp:
  617.                 if p == path:
  618.                     self.file_list_move_down(p)
  619.                     self.UpdateFileList(index+1)
  620.                     break
  621.  
  622.                 index += 1
  623.  
  624.     def file_list_move_up(self, p):
  625.         for i in range(1, len(self.file_list)):
  626.             if self.file_list[i][0] == p:
  627.                 self.file_list[i-1], self.file_list[i] = \
  628.                     self.file_list[i], self.file_list[i-1]
  629.  
  630.     def file_list_move_down(self, p):
  631.         for i in range(len(self.file_list)-2, -1, -1):
  632.             if self.file_list[i][0] == p:
  633.                 self.file_list[i], self.file_list[i+1] = \
  634.                     self.file_list[i+1], self.file_list[i]                 
  635.  
  636.     def fileEdit_textChanged(self,a0):
  637.         self.filename = str(self.fileEdit.text())
  638.         self.enableAddFileButton()
  639.  
  640.     def browsePushButton_clicked(self):
  641.         self.filename = str(self.fileEdit.text())
  642.  
  643.         if self.filename and os.path.isdir(self.filename):
  644.             d = self.filename
  645.         else:
  646.             d = os.path.expanduser("~")
  647.  
  648.         s = str(QFileDialog.getOpenFileName(d, self.__tr("All files (*)"), self,
  649.                                               "openfile", self.caption()))
  650.  
  651.         if s and os.path.exists(s):
  652.             self.fileEdit.setText(s)
  653.             self.enableAddFileButton()
  654.  
  655.  
  656.     def enableAddFileButton(self):
  657.         self.addFilePushButton.setEnabled(bool(self.filename and
  658.                                                os.path.exists(self.filename) and 
  659.                                                os.path.isfile(self.filename)))
  660.  
  661.  
  662.     def EventUI(self, event_code, event_type, error_string_short,
  663.                 error_string_long, retry_timeout, job_id,
  664.                 device_uri, printer_name, title, job_size):
  665.  
  666.         log.debug("Event: device_uri=%s code=%d type=%s string=%s timeout=%d id=%d uri=%s printer=%s title=%s" %
  667.                  (device_uri, event_code, event_type,  
  668.                   error_string_short, retry_timeout, 
  669.                   job_id, device_uri, printer_name, title))
  670.  
  671.         if event_code  == EVENT_FAX_RENDER_COMPLETE:
  672.  
  673.             if device_uri == self.dev.device_uri:
  674.                 log.debug("Render completed message received for %s." % device_uri)
  675.  
  676.                 if self.isMinimized():
  677.                     self.showNormal()
  678.  
  679.                 self.event_handler(event_code, title, self.username, job_id, job_size)
  680.  
  681.         elif event_code == EVENT_FAX_RENDER_DISTANT_EARLY_WARNING:
  682.             if device_uri == self.dev.device_uri:
  683.                 QApplication.setOverrideCursor(QApplication.waitCursor)
  684.                 log.debug("Distant early warning received.")
  685.  
  686.         elif event_code == EVENT_FAX_ADDRESS_BOOK_UPDATED:
  687.             log.debug("Address book updated event received.")
  688.             self.UpdateIndividualList()
  689.             self.UpdateGroupList()
  690.             self.UpdateSelectionEdit()
  691.  
  692.         else: # Printer status...
  693.             if device_uri == self.dev.device_uri:
  694.                 log.debug("Device status update event received for %s." % device_uri)
  695.                 self.StateText.setText(error_string_short) 
  696.  
  697.     def allowableTypesPushButton_clicked(self):
  698.         x = {}
  699.         for a in self.allowable_mime_types:
  700.             x[a] = self.MIME_TYPES_DESC.get(a, ('Unknown', 'n/a'))
  701.             
  702.         log.debug(x)
  703.         dlg = FaxAllowableTypesDlg(x, self)
  704.         dlg.exec_loop()
  705.     
  706.     # ************************************** Event handling
  707.     # Event handler for adding files from a external print job (not during fax send thread)
  708.     def addFileFromJob(self, event, title, username, job_id=0, job_size=0):
  709.         log.debug("Transfering job %d (%d bytes)" % (job_id, job_size))
  710.  
  711.         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  712.         try:
  713.             sock.connect((prop.hpssd_host, prop.hpssd_port))
  714.         except socket.error:
  715.             log.error("Unable to contact HPLIP I/O (hpssd).")
  716.             sys.exit(1)    
  717.  
  718.         fax_dir = os.path.expanduser("~/hpfax")
  719.  
  720.         if not os.path.exists(fax_dir):
  721.             os.mkdir(fax_dir)
  722.  
  723.         fax_file = os.path.expanduser(os.path.join(fax_dir, "hpfax-%d.g3" % job_id))
  724.         fd = file(fax_file, 'w')
  725.         bytes_read = 0
  726.         header_read = False
  727.         total_pages = 0
  728.  
  729.         if self.waitdlg is not None:
  730.             self.waitdlg.setMessage(self.__tr("Receiving fax data..."))
  731.         
  732.         while True:
  733.             qApp.processEvents()
  734.             
  735.             fields, data, result_code = \
  736.                 msg.xmitMessage(sock, "FaxGetData", None,
  737.                                      {"username": username,
  738.                                       "job-id": job_id,
  739.                                      })
  740.  
  741.             if data and result_code == ERROR_SUCCESS:
  742.                 fd.write(data)
  743.                 bytes_read += len(data)
  744.  
  745.                 if not header_read and len(data) >= fax.FILE_HEADER_SIZE:
  746.                     magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
  747.                         resolution, encoding, reserved1, reserved2 = \
  748.                         struct.unpack(">8sBIHHBBBII", data[:fax.FILE_HEADER_SIZE])
  749.  
  750.                     log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  751.                               (magic, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
  752.  
  753.                     header_read = True
  754.  
  755.             else:
  756.                 break
  757.  
  758.         fd.close()
  759.         sock.close()
  760.         QApplication.restoreOverrideCursor()
  761.         
  762.         if self.waitdlg is not None:
  763.             self.waitdlg.hide()
  764.             self.waitdlg.close()
  765.             self.waitdlg = None
  766.  
  767.         log.debug("Transfered %d bytes" % bytes_read)
  768.  
  769.         self.file_list.append((fax_file, "application/hplip-fax", "HP Fax", title, total_pages))
  770.  
  771.         self.UpdateFileList()
  772.         self.document_num += 1
  773.         self.fileEdit.setText("")
  774.         self.CheckSendButtons()
  775.  
  776.  
  777.     # Event handler called during fax send thread
  778.     # NEW: Used only for rendering coverpages
  779.     def sendFaxEvent(self, event, title, username, job_id=0, job_size=0): # event handler during send
  780.         self.event_queue.put((event, title, username, job_id, job_size))
  781.         QApplication.restoreOverrideCursor()
  782.  
  783.     # **************************************
  784.  
  785.     def addFile(self, path, title, mime_type, mime_type_desc, pages):
  786.         self.file_list.append((path, mime_type, mime_type_desc, title, pages))
  787.  
  788.         self.UpdateFileList()
  789.         self.document_num += 1
  790.         self.fileEdit.setText("")
  791.         self.CheckSendButtons()
  792.  
  793.  
  794.     def processFile(self, path, title): # Process an arbitrary file ("Add file...")
  795.         path = os.path.realpath(path)
  796.  
  797.         if os.path.exists(path):
  798.             mime_type = magic.mime_type(path)
  799.             log.debug(mime_type)
  800.             mime_type_desc = mime_type
  801.  
  802.             try:
  803.                 mime_type_desc = self.MIME_TYPES_DESC[mime_type][0]
  804.             except KeyError:
  805.                 self.WarningUI(self.__tr("<b>You are trying to add a file that cannot be directly faxed with this utility.</b><p>To print this file, use the print command in the application that created it."))
  806.                 return
  807.             else:
  808.                 log.debug("Adding file: title='%s' file=%s mime_type=%s mime_desc=%s)" % (title, path, mime_type, mime_type_desc))
  809.  
  810.  
  811.                 all_pages = True 
  812.                 page_range = ''
  813.                 page_set = 0
  814.                 nup = 1
  815.  
  816.                 cups.resetOptions()
  817.  
  818.                 if mime_type in ["application/x-cshell",
  819.                                  "application/x-perl",
  820.                                  "application/x-python",
  821.                                  "application/x-shell",
  822.                                  "text/plain",]:
  823.  
  824.                     cups.addOption('prettyprint')
  825.  
  826.                 if nup > 1:
  827.                     cups.addOption('number-up=%d' % nup)
  828.  
  829.                 self.cups_printers = cups.getPrinters()
  830.                 #log.debug(self.cups_printers)
  831.                 
  832.                 printer_state = cups.IPP_PRINTER_STATE_STOPPED
  833.                 for p in self.cups_printers:
  834.                     if p.name == self.current_printer:
  835.                         printer_state = p.state
  836.                         
  837.                 log.debug("Printer state = %d" % printer_state)
  838.                 
  839.                 if printer_state == cups.IPP_PRINTER_STATE_IDLE:
  840.                     sent_job_id = cups.printFile(self.current_printer, path, os.path.basename(path))
  841.                     log.debug("Job ID=%d" % sent_job_id)  
  842.  
  843.                     QApplication.setOverrideCursor(QApplication.waitCursor)
  844.  
  845.                     self.waitdlg = WaitForm(0, self.__tr("Processing fax file..."), None, self, modal=1)
  846.                     self.waitdlg.show()
  847.                   
  848.                 else:
  849.                     self.FailureUI(self.__tr("<b>Printer '%1' is in a stopped or error state.</b><p>Check the printer queue in CUPS and try again.").arg(self.current_printer))
  850.                     cups.resetOptions()
  851.                     return
  852.                     
  853.                 cups.resetOptions()
  854.                 QApplication.restoreOverrideCursor()
  855.  
  856.         else:
  857.             self.FailureUI(self.__tr("<b>Unable to add file '%1' to file list.</b><p>Check the file name and try again.".arg(path)))
  858.  
  859.  
  860.     def UpdateFileList(self, j=0):
  861.         self.fileListView.clear()
  862.         temp = self.file_list[:]
  863.         temp.reverse()
  864.  
  865.         total_pages = 0
  866.  
  867.         if j<0: j=0
  868.         if j>len(temp)-1: j=len(temp)-1
  869.  
  870.         k, selected = len(temp)-1, False
  871.         for path, mime_type, mime_type_desc, title, pages in temp:
  872.  
  873.             if pages == 0:
  874.                 str_pages = '?'
  875.             else:
  876.                 str_pages = str(pages)
  877.  
  878.             i = FileListViewItem(self.fileListView, title, mime_type_desc, path, str_pages)
  879.             total_pages += pages
  880.  
  881.             if k == j and not selected:
  882.                 self.fileListView.setCurrentItem(i)
  883.                 selected = True
  884.  
  885.             k -= 1
  886.  
  887.         self.fileListView.setColumnText(2, self.__tr("Pages (Total=%1)").arg(total_pages))
  888.  
  889.         self.delFileButton.setEnabled(self.fileListView.childCount() > 0)
  890.         self.upFileButton.setEnabled(self.fileListView.childCount() > 1)
  891.         self.downFileButton.setEnabled(self.fileListView.childCount() > 1)
  892.  
  893.  
  894.     # ************************************** Coverpages
  895.  
  896.     def showCoverPageDlg(self):
  897.         dlg = CoverpageForm(self.cover_page_name, self)
  898.         dlg = CoverpageForm(self.cover_page_name, self)
  899.         dlg.messageTextEdit.setText(self.cover_page_message)
  900.         dlg.regardingTextEdit.setText(self.cover_page_re)
  901.  
  902.         if dlg.exec_loop() == QDialog.Accepted:
  903.  
  904.             self.cover_page_func, cover_page_png = dlg.data
  905.             self.cover_page_message = str(dlg.messageTextEdit.text())
  906.             self.cover_page_re = str(dlg.regardingTextEdit.text())
  907.             self.cover_page_name = dlg.coverpage_name
  908.             return True
  909.  
  910.         return False
  911.  
  912.     def addCoverpagePushButton_clicked(self):
  913.         if self.showCoverPageDlg():
  914.             self.file_list.insert(0, ('coverpage', "application/hplip-fax-coverpage", 
  915.                 self.__tr("HP Fax Coverpage"), self.__tr("Cover Page"), 1))
  916.  
  917.             self.UpdateFileList()
  918.             self.document_num += 1
  919.             self.CheckSendButtons()
  920.  
  921.             self.addCoverpagePushButton.setEnabled(False)
  922.             self.editCoverpagePushButton.setEnabled(coverpages_enabled)
  923.  
  924.     def editCoverpagePushButton_clicked(self):
  925.         self.showCoverPageDlg()
  926.  
  927.  
  928.     # ************************************** Misc
  929.  
  930.     def closeEvent(self, event):
  931.         if self.dev is not None and self.dev.isSendFaxActive():
  932.             self.FailureUI(self.__tr("<b>Send fax is active.</b><p>Please wait for operation to complete."))
  933.         else:
  934.             event.accept()
  935.  
  936.  
  937.     def settingsPushButton_clicked(self):
  938.         try:
  939.             self.dev.open()
  940.  
  941.             try:
  942.                 result_code, fax_num = self.dev.getPML(pml.OID_FAX_LOCAL_PHONE_NUM)
  943.             except Error:
  944.                 log.error("PML failure.")
  945.                 self.FailureUI(self.__tr("<p><b>Operation failed. Device busy.</b>"))
  946.                 return
  947.  
  948.             fax_num = str(fax_num)
  949.  
  950.             try:
  951.                 result_code, name = self.dev.getPML(pml.OID_FAX_STATION_NAME)
  952.             except Error:
  953.                 log.error("PML failure.")
  954.                 self.FailureUI(self.__tr("<p><b>Operation failed. Device busy.</b>"))
  955.                 return
  956.  
  957.             name = str(name)
  958.  
  959.             dlg = FaxSettingsForm(self.dev, fax_num, name, self)
  960.             dlg.exec_loop()
  961.  
  962.         finally:
  963.             self.dev.close()
  964.  
  965.  
  966.     def SuccessUI(self):
  967.         QMessageBox.information(self,
  968.                              self.caption(),
  969.                              self.__tr("<p><b>Fax send completed successfully.</b>"),
  970.                               QMessageBox.Ok,
  971.                               QMessageBox.NoButton,
  972.                               QMessageBox.NoButton)
  973.  
  974.     def FailureUI(self, error_text):
  975.         QMessageBox.critical(self,
  976.                              self.caption(),
  977.                              error_text,
  978.                               QMessageBox.Ok,
  979.                               QMessageBox.NoButton,
  980.                               QMessageBox.NoButton)
  981.  
  982.     def WarningUI(self, msg):
  983.         QMessageBox.warning(self,
  984.                              self.caption(),
  985.                              msg,
  986.                               QMessageBox.Ok,
  987.                               QMessageBox.NoButton,
  988.                               QMessageBox.NoButton)        
  989.  
  990.     def __tr(self,s,c = None):
  991.         return qApp.translate("FaxSendJobForm", s, c)
  992.  
  993.     def __trUtf8(self,s,c = None):
  994.         return qApp.translate("FaxSendJobForm", s, c, QApplication.UnicodeUTF8)
  995.