1 # -*- coding: utf-8 -*-
3 # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
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.
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.
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
24 from base import utils, magic, pml
26 from ui_utils import load_pixmap
30 from scrollview import ScrollView, PixmapLabelButton
31 from allowabletypesdlg import AllowableTypesDlg
32 from waitform import WaitForm
36 import struct, Queue, time
39 fax_enabled = prop.fax_build
45 # This can fail on Python < 2.3 due to the datetime module
46 # or if fax was diabled during the build
47 log.warn("Fax send disabled - Python 2.3+ required.")
51 coverpages_enabled = False
55 ver = reportlab.Version
62 coverpages_enabled = True
64 log.warn("Pre-2.0 version of Reportlab installed. Fax coverpages disabled.")
67 log.warn("Reportlab not installed. Fax coverpages disabled.")
70 if not coverpages_enabled:
71 log.warn("Please install version 2.0+ of Reportlab for coverpage support.")
73 if fax_enabled and coverpages_enabled:
74 from fax import coverpages
75 from coverpageform import CoverpageForm
78 # Used to store MIME types for files
79 # added directly in interface.
80 job_types = {} # { job_id : "mime_type", ...}
82 class FileListViewItem(QListViewItem):
83 def __init__(self, parent, order, title, mime_type_desc, str_pages, path):
84 QListViewItem.__init__(self, parent, order, title, mime_type_desc, str_pages, path)
88 class RecipientListViewItem(QListViewItem):
89 def __init__(self, parent, order, name, fax, notes):
90 QListViewItem.__init__(self, parent, order, name, fax, notes)
95 class PhoneNumValidator(QValidator):
96 def __init__(self, parent=None, name=None):
97 QValidator.__init__(self, parent, name)
99 def validate(self, input, pos):
100 input = unicode(input)
102 return QValidator.Acceptable, pos
103 elif input[pos-1] not in u'0123456789-(+) *#':
104 return QValidator.Invalid, pos
105 elif len(input) > 50:
106 return QValidator.Invalid, pos
108 return QValidator.Acceptable, pos
112 class ScrollFaxView(ScrollView):
113 def __init__(self, service, parent = None, form=None, name = None,fl = 0):
114 ScrollView.__init__(self, service, parent, name, fl)
122 self.pages_button_group = 0
123 self.recipient_list = []
124 self.username = prop.username
126 self.allowable_mime_types = cups.getAllowableMIMETypes()
127 self.cover_page_func, cover_page_png = None, None
128 self.cover_page_message = ''
129 self.cover_page_re = ''
130 self.cover_page_name = ''
131 self.update_queue = Queue.Queue() # UI updates from send thread
132 self.event_queue = Queue.Queue() # UI events (cancel) to send thread
133 self.prev_selected_file_path = ''
134 self.prev_selected_recipient = ''
135 self.preserve_formatting = False
137 log.debug(self.allowable_mime_types)
140 self.lock_file = None
143 self.db = fax.FaxAddressBook()
144 self.last_db_modification = self.db.last_modification_time()
146 self.MIME_TYPES_DESC = \
148 "application/pdf" : (self.__tr("PDF Document"), '.pdf'),
149 "application/postscript" : (self.__tr("Postscript Document"), '.ps'),
150 "application/vnd.hp-HPGL" : (self.__tr("HP Graphics Language File"), '.hgl, .hpg, .plt, .prn'),
151 "application/x-cshell" : (self.__tr("C Shell Script"), '.csh, .sh'),
152 "application/x-csource" : (self.__tr("C Source Code"), '.c'),
153 "text/cpp": (self.__tr("C++ Source Code"), '.cpp, .cxx'),
154 "application/x-perl" : (self.__tr("Perl Script"), '.pl'),
155 "application/x-python" : (self.__tr("Python Program"), '.py'),
156 "application/x-shell" : (self.__tr("Shell Script"), '.sh'),
157 "application/x-sh" : (self.__tr("Shell Script"), '.sh'),
158 "text/plain" : (self.__tr("Plain Text"), '.txt, .log, etc'),
159 "text/html" : (self.__tr("HTML Dcoument"), '.htm, .html'),
160 "image/gif" : (self.__tr("GIF Image"), '.gif'),
161 "image/png" : (self.__tr("PNG Image"), '.png'),
162 "image/jpeg" : (self.__tr("JPEG Image"), '.jpg, .jpeg'),
163 "image/tiff" : (self.__tr("TIFF Image"), '.tif, .tiff'),
164 "image/x-bitmap" : (self.__tr("Bitmap (BMP) Image"), '.bmp'),
165 "image/x-bmp" : (self.__tr("Bitmap (BMP) Image"), '.bmp'),
166 "image/x-photocd" : (self.__tr("Photo CD Image"), '.pcd'),
167 "image/x-portable-anymap" : (self.__tr("Portable Image (PNM)"), '.pnm'),
168 "image/x-portable-bitmap" : (self.__tr("Portable B&W Image (PBM)"), '.pbm'),
169 "image/x-portable-graymap" : (self.__tr("Portable Grayscale Image (PGM)"), '.pgm'),
170 "image/x-portable-pixmap" : (self.__tr("Portable Color Image (PPM)"), '.ppm'),
171 "image/x-sgi-rgb" : (self.__tr("SGI RGB"), '.rgb'),
172 "image/x-xbitmap" : (self.__tr("X11 Bitmap (XBM)"), '.xbm'),
173 "image/x-xpixmap" : (self.__tr("X11 Pixmap (XPM)"), '.xpm'),
174 "image/x-sun-raster" : (self.__tr("Sun Raster Format"), '.ras'),
178 user_settings = utils.UserSettings()
179 self.cmd_fab = user_settings.cmd_fab
180 log.debug("FAB command: %s" % self.cmd_fab)
183 self.check_timer = QTimer(self, "CheckTimer")
184 self.connect(self.check_timer, SIGNAL('timeout()'), self.PeriodicCheck)
185 self.check_timer.start(3000)
188 def fillControls(self):
189 ScrollView.fillControls(self)
192 if self.addPrinterFaxList(): #faxes=True, printers=False):
194 self.addGroupHeading("files_to_fax", self.__tr("File(s) to Fax"))
197 if coverpages_enabled:
198 self.addGroupHeading("coverpage", self.__tr("Add/Edit Fax Coverpage"))
201 self.addGroupHeading("recipients", self.__tr("Recipient(s)"))
203 self.addRecipientList()
205 self.addGroupHeading("recipient_add_from_fab", self.__tr("Add Recipients from the Fax Address Book"))
207 self.addRecipientAddFromFAB()
209 self.addGroupHeading("recipient_quick_add", self.__tr("<i>Quick Add</i> an Individual Recipient"))
211 self.addRecipientQuickAdd()
213 self.addGroupHeading("space1", "")
215 self.faxButton = self.addActionButton("bottom_nav", self.__tr("Send Fax Now"),
216 self.faxButton_clicked, 'fax.png', 'fax.png',
217 self.__tr("Close"), self.funcButton_clicked)
219 self.faxButton.setEnabled(False)
221 self.updateRecipientCombos()
223 self.maximizeControl()
226 QApplication.restoreOverrideCursor()
227 self.form.FailureUI("<b>Fax is disabled.</b><p>No CUPS fax queue found for this device.")
228 self.funcButton_clicked()
231 QApplication.restoreOverrideCursor()
232 self.form.FailureUI("<b>Fax is disabled.</b><p>Python version 2.3 or greater required or fax was disabled during build.")
233 self.funcButton_clicked()
237 def onUpdate(self, cur_device=None):
238 log.debug("ScrollPrintView.onUpdate()")
239 self.updateFileList()
240 self.updateRecipientList()
243 def PeriodicCheck(self): # called by check_timer every 3 sec
246 log.debug("Checking for incoming faxes...")
248 result = list(self.service.CheckForWaitingFax(self.cur_device.device_uri,
249 prop.username, self.last_job_id))
251 fax_file = str(result[7])
255 log.debug("A new fax has arrived: %s" % fax_file)
256 job_id = int(result[4])
257 title = str(result[5])
259 if self.form.isMinimized():
260 self.form.showNormal()
262 self.check_timer.stop()
263 self.addFileFromJob(0, title, prop.username, job_id, fax_file)
264 self.check_timer.start(3000)
267 log.debug("Not found.")
269 # Check for updated FAB
270 last_db_modification = self.db.last_modification_time()
272 if last_db_modification > self.last_db_modification:
273 QApplication.setOverrideCursor(QApplication.waitCursor)
274 log.debug("FAB has been modified. Re-reading...")
275 self.last_db_modification = last_db_modification
276 self.updateRecipientCombos()
277 QApplication.restoreOverrideCursor()
280 def onPrinterChange(self, printer_name):
281 if printer_name != self.cur_printer:
283 self.lock(printer_name)
284 #utils.unlock(self.lock_file)
285 #ok, self.lock_file = utils.lock_app('hp-sendfax-%s' % printer_name, True)
287 ScrollView.onPrinterChange(self, printer_name)
290 utils.unlock(self.lock_file)
292 def lock(self, printer_name=None):
293 if printer_name is None:
294 printer_name = self.cur_printer
296 ok, self.lock_file = utils.lock_app('hp-sendfax-%s' % printer_name, True)
300 # Event handler for adding files from a external print job (not during fax send thread)
301 def addFileFromJob(self, event, title, username, job_id, fax_file):
302 QApplication.setOverrideCursor(QApplication.waitCursor)
306 f = file(fax_file, 'r')
307 header = f.read(fax.FILE_HEADER_SIZE)
309 if len(header) != fax.FILE_HEADER_SIZE:
310 log.error("Invalid fax file! (truncated header or no data)")
313 mg, version, total_pages, hort_dpi, vert_dpi, page_size, \
314 resolution, encoding, reserved1, reserved2 = \
315 struct.unpack(">8sBIHHBBBII", header[:fax.FILE_HEADER_SIZE])
317 log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
318 (mg, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
321 mime_type = job_types.get(job_id, "application/hplip-fax")
322 mime_type_desc = self.MIME_TYPES_DESC.get(mime_type, ('Unknown', 'n/a'))[0]
323 log.debug("%s (%s)" % (mime_type, mime_type_desc))
324 self.file_list.append((fax_file, mime_type, mime_type_desc, title, total_pages))
325 self.prev_selected_file_path = fax_file
327 self.form.FailureUI("<b>Render Failure:</b><p>Rendered document contains no data.")
329 self.updateFileList()
334 if self.waitdlg is not None:
339 QApplication.restoreOverrideCursor()
342 def add_fax_canceled(self):
349 def addFileList(self):
350 widget = self.getWidget()
352 layout37 = QGridLayout(widget,1,1,5,10,"layout37")
354 self.addFilePushButton = PixmapLabelButton(widget, "list_add.png",
355 "list_add.png", name='addFilePushButton')
357 layout37.addWidget(self.addFilePushButton,2,0)
359 self.removeFilePushButton = PixmapLabelButton(widget,
360 "list_remove.png", "list_remove.png", name='removeFilePushButton')
362 layout37.addWidget(self.removeFilePushButton,2,1)
364 self.moveFileUpPushButton = PixmapLabelButton(widget, "up.png",
365 "up.png", name='moveFileUpPushButton')
367 layout37.addWidget(self.moveFileUpPushButton,2,2)
369 self.moveFileDownPushButton = PixmapLabelButton(widget, "down.png",
370 "down.png", name='moveFileDownPushButton')
372 layout37.addWidget(self.moveFileDownPushButton,2,3)
374 self.showTypesPushButton = PixmapLabelButton(widget, "mimetypes.png",
375 None, name='showTypesPushButton')
377 layout37.addWidget(self.showTypesPushButton,2,5)
380 self.fileListView = QListView(widget,"fileListView")
381 self.fileListView.addColumn(self.__tr("Order"))
382 self.fileListView.addColumn(self.__tr("Name"))
383 self.fileListView.addColumn(self.__tr("Type"))
384 self.fileListView.addColumn(self.__tr("Pages"))
385 self.fileListView.addColumn(self.__tr("Path"))
386 self.fileListView.setAllColumnsShowFocus(1)
387 self.fileListView.setShowSortIndicator(1)
388 self.fileListView.setColumnWidth(0, 100) # Order
389 self.fileListView.setColumnWidth(1, 150) # Name
390 self.fileListView.setColumnWidth(2, 100) # Type
391 self.fileListView.setColumnWidth(3, 100) # Pages
392 self.fileListView.setColumnWidth(4, 300) # Path
393 self.fileListView.setItemMargin(2)
394 self.fileListView.setSorting(-1)
396 layout37.addMultiCellWidget(self.fileListView,1,1,0,5)
398 spacer26 = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
399 layout37.addItem(spacer26,2,4)
401 self.addFilePushButton.setText(self.__tr("Add File..."))
402 self.showTypesPushButton.setText(self.__tr("Show Types..."))
403 self.removeFilePushButton.setText(self.__tr("Remove File"))
404 self.moveFileDownPushButton.setText(self.__tr("Move Down"))
405 self.moveFileUpPushButton.setText(self.__tr("Move Up"))
407 self.removeFilePushButton.setEnabled(False)
408 self.moveFileDownPushButton.setEnabled(False)
409 self.moveFileUpPushButton.setEnabled(False)
410 self.connect(self.addFilePushButton, SIGNAL("clicked()"), self.addFile_clicked)
411 self.connect(self.removeFilePushButton, SIGNAL("clicked()"), self.removeFile_clicked)
412 self.connect(self.showTypesPushButton, SIGNAL("clicked()"), self.showFileTypes_clicked)
414 self.connect(self.moveFileUpPushButton, SIGNAL("clicked()"), self.moveFileUp_clicked)
415 self.connect(self.moveFileDownPushButton, SIGNAL("clicked()"), self.moveFileDown_clicked)
417 self.connect(self.fileListView,SIGNAL("rightButtonClicked(QListViewItem*,const QPoint&, int)"),self.fileListView_rightButtonClicked)
419 self.connect(self.fileListView, SIGNAL("selectionChanged(QListViewItem*)"), self.fileListView_selectionChanged)
421 self.addWidget(widget, "file_list", maximize=True)
423 def fileListView_selectionChanged(self, i):
425 self.prev_selected_file_path = i.path
426 except AttributeError:
429 flv = self.fileListView
430 selected_item = flv.selectedItem()
431 file_count = flv.childCount()
432 last_item = flv.firstChild()
433 while last_item.nextSibling():
434 last_item = last_item.nextSibling()
436 self.moveFileDownPushButton.setEnabled(file_count > 1 and selected_item is not last_item)
437 self.moveFileUpPushButton.setEnabled(file_count > 1 and selected_item is not flv.firstChild())
440 def moveFileUp_clicked(self):
442 path = self.fileListView.selectedItem().path
443 except AttributeError:
446 for i in range(1, len(self.file_list)):
447 if self.file_list[i][0] == path:
448 self.file_list[i-1],self.file_list[i] = self.file_list[i], self.file_list[i-1]
450 self.updateFileList()
452 def moveFileDown_clicked(self):
454 path = self.fileListView.selectedItem().path
455 except AttributeError:
458 for i in range(len(self.file_list) - 2, -1, -1):
459 if self.file_list[i][0] == path:
460 self.file_list[i], self.file_list[i+1] = self.file_list[i+1], self.file_list[i]
462 self.updateFileList()
465 def fileListView_rightButtonClicked(self, item, pos, col):
466 popup = QPopupMenu(self)
468 popup.insertItem(QIconSet(load_pixmap('list_add', '16x16')),
469 self.__tr("Add File..."), self.addFile_clicked)
472 popup.insertItem(QIconSet(load_pixmap('list_remove', '16x16')),
473 self.__tr("Remove File"), self.removeFile_clicked)
475 if self.fileListView.childCount() > 1:
476 last_item = self.fileListView.firstChild()
477 while last_item is not None and last_item.nextSibling():
478 last_item = last_item.nextSibling()
480 if item is not self.fileListView.firstChild():
481 popup.insertItem(QIconSet(load_pixmap('up', '16x16')),
482 self.__tr("Move Up"), self.moveFileUp_clicked)
484 if item is not last_item:
485 popup.insertItem(QIconSet(load_pixmap('down', '16x16')),
486 self.__tr("Move Down"), self.moveFileDown_clicked)
489 popup.insertSeparator(-1)
490 popup.insertItem(QIconSet(load_pixmap('mimetypes', '16x16')),
491 self.__tr("Show File Types..."), self.showFileTypes_clicked)
496 def addFile(self, path, title, mime_type, mime_type_desc, pages):
497 self.file_list.append((path, mime_type, mime_type_desc, title, pages))
498 self.prev_selected_file_path = path
500 self.updateFileList()
502 def processFile(self, path, title=''): # Process an arbitrary file ("Add file...")
503 path = os.path.realpath(path)
505 title = os.path.basename(path)
507 if os.path.exists(path) and os.access(path, os.R_OK):
508 mime_type = magic.mime_type(path)
509 mime_type_desc = mime_type
511 if mime_type == 'application/hplip-fax':
512 mime_type_desc = self.MIME_TYPES_DESC[mime_type][0]
514 fax_file_fd = file(path, 'r')
515 header = fax_file_fd.read(fax.FILE_HEADER_SIZE)
517 mg, version, pages, hort_dpi, vert_dpi, page_size, \
518 resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
521 log.error("Invalid file header. Bad magic.")
522 self.form.WarningUI(self.__tr("<b>Invalid HPLIP Fax file.</b><p>Bad magic!"))
525 self.addFile(path, title, mime_type, mime_type_desc, pages)
528 log.debug(repr(mime_type))
530 mime_type_desc = self.MIME_TYPES_DESC[mime_type][0]
532 self.form.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."))
535 log.debug("Adding file: title='%s' file=%s mime_type=%s mime_desc=%s)" % (title, path, mime_type, mime_type_desc))
544 self.cups_printers = cups.getPrinters()
546 printer_state = cups.IPP_PRINTER_STATE_STOPPED
547 for p in self.cups_printers:
548 if p.name == self.cur_printer:
549 printer_state = p.state
551 log.debug("Printer state = %d" % printer_state)
553 if printer_state == cups.IPP_PRINTER_STATE_IDLE:
554 log.debug("Printing: %s on %s" % (path, self.cur_printer))
555 sent_job_id = cups.printFile(self.cur_printer, path, os.path.basename(path))
556 self.last_job_id = sent_job_id
557 job_types[sent_job_id] = mime_type # save for later
558 log.debug("Job ID=%d" % sent_job_id)
560 QApplication.setOverrideCursor(QApplication.waitCursor)
562 self.waitdlg = WaitForm(0, self.__tr("Processing fax file..."), None, self, modal=1) # self.add_fax_canceled
566 self.form.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.cur_printer))
571 QApplication.restoreOverrideCursor()
574 self.form.FailureUI(self.__tr("<b>Unable to add file '%1' to file list (file not found or insufficient permissions).</b><p>Check the file name and try again.").arg(path))
578 def updateFileList(self):
579 self.fileListView.clear()
580 temp = self.file_list[:]
586 for path, mime_type, mime_type_desc, title, pages in temp:
587 i = FileListViewItem(self.fileListView, str(order), title, mime_type_desc, str(pages), path)
589 if not self.prev_selected_file_path or self.prev_selected_file_path == path:
590 self.fileListView.setSelected(i, True)
592 self.prev_selected_file_path = path
596 last_item = self.fileListView.firstChild()
597 while last_item is not None and last_item.nextSibling():
598 last_item = last_item.nextSibling()
600 file_count = self.fileListView.childCount()
601 self.moveFileDownPushButton.setEnabled(file_count > 1 and selected_item is not last_item)
602 self.moveFileUpPushButton.setEnabled(file_count > 1 and selected_item is not self.fileListView.firstChild())
603 self.checkSendFaxButton()
604 self.removeFilePushButton.setEnabled(file_count > 0)
608 def addFile_clicked(self):
609 dlg = QFileDialog(user_conf.workingDirectory(), QString.null, None, None, True)
611 dlg.setCaption("openfile")
612 dlg.setMode(QFileDialog.ExistingFile)
615 if dlg.exec_loop() == QDialog.Accepted:
616 results = dlg.selectedFile()
617 working_directory = unicode(dlg.dir().absPath())
618 log.debug("results: %s" % results)
619 user_conf.setWorkingDirectory(working_directory)
622 path = unicode(results)
623 self.processFile(path)
626 def removeFile_clicked(self):
628 path = self.fileListView.selectedItem().path
629 except AttributeError:
632 temp = self.file_list[:]
634 for p, t, d, x, g in temp:
636 del self.file_list[index]
638 if t == 'application/hplip-fax-coverpage':
639 self.addCoverpagePushButton.setEnabled(coverpages_enabled)
640 self.editCoverpagePushButton.setEnabled(False)
642 self.prev_selected_file_path = ''
643 self.updateFileList()
649 def showFileTypes_clicked(self):
651 for a in self.allowable_mime_types:
652 x[a] = self.MIME_TYPES_DESC.get(a, ('Unknown', 'n/a'))
655 dlg = AllowableTypesDlg(x, self)
663 def addCoverpage(self):
664 widget = self.getWidget()
666 layout14 = QGridLayout(widget,1,1,5,10,"layout14")
668 self.editCoverpagePushButton = PixmapLabelButton(widget,
669 "edit.png", "edit.png", name='')
671 layout14.addWidget(self.editCoverpagePushButton,0,1)
673 self.addCoverpagePushButton = PixmapLabelButton(widget,
674 "list_add.png", "list_add.png", name='')
676 layout14.addWidget(self.addCoverpagePushButton,0,2)
677 spacer12_2 = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
678 layout14.addItem(spacer12_2,0,0)
680 self.editCoverpagePushButton.setText(self.__tr("Edit..."))
681 self.editCoverpagePushButton.setEnabled(False)
683 self.addCoverpagePushButton.setText(self.__tr("Add..."))
685 self.connect(self.editCoverpagePushButton,SIGNAL("clicked()"),self.editCoverpagePushButton_clicked)
686 self.connect(self.addCoverpagePushButton,SIGNAL("clicked()"),self.addCoverpagePushButton_clicked)
688 self.addWidget(widget, "coverpage")
690 def editCoverpagePushButton_clicked(self):
691 self.showCoverPageDlg()
693 def addCoverpagePushButton_clicked(self):
694 if self.showCoverPageDlg():
695 self.file_list.insert(0, ('n/a', "application/hplip-fax-coverpage",
696 self.__tr("HP Fax Coverpage"), self.__tr("Cover Page"), 1))
698 self.updateFileList()
699 self.addCoverpagePushButton.setEnabled(False)
700 self.editCoverpagePushButton.setEnabled(True)
702 def showCoverPageDlg(self):
703 dlg = CoverpageForm(self.cover_page_name, self.preserve_formatting, parent=self)
704 dlg.messageTextEdit.setText(self.cover_page_message)
705 dlg.regardingTextEdit.setText(self.cover_page_re)
707 if dlg.exec_loop() == QDialog.Accepted:
709 self.cover_page_func, cover_page_png = dlg.data
710 self.cover_page_message = unicode(dlg.messageTextEdit.text())
711 self.cover_page_re = unicode(dlg.regardingTextEdit.text())
712 self.cover_page_name = dlg.coverpage_name
713 self.preserve_formatting = dlg.preserve_formatting
724 def addRecipientList(self):
725 widget = self.getWidget()
727 layout9 = QGridLayout(widget,1,1,5,10,"layout9")
729 self.moveDownPushButton = PixmapLabelButton(widget,
730 "down_user.png", "down_user.png", name='')
732 layout9.addWidget(self.moveDownPushButton,1,2)
734 self.recipientListView = QListView(widget,"recipientListView")
735 self.recipientListView.addColumn(self.__tr("Order"))
736 self.recipientListView.addColumn(self.__tr("Name"))
737 self.recipientListView.addColumn(self.__tr("Fax Number"))
738 self.recipientListView.addColumn(self.__tr("Notes"))
740 self.recipientListView.setAllColumnsShowFocus(1)
741 self.recipientListView.setShowSortIndicator(1)
742 self.recipientListView.setColumnWidth(0, 100) # Order
743 self.recipientListView.setColumnWidth(1, 150) # Name
744 self.recipientListView.setColumnWidth(2, 200) # Fax Number
745 self.recipientListView.setColumnWidth(3, 250) # Notes
746 self.recipientListView.setItemMargin(2)
747 self.recipientListView.setSorting(-1)
749 widget.setMaximumHeight(250)
751 layout9.addMultiCellWidget(self.recipientListView,0,0,0,4)
753 self.fabPushButton = PixmapLabelButton(widget,
754 "fab", None, name='')
756 layout9.addWidget(self.fabPushButton,1,4)
758 self.removeRecipientPushButton = PixmapLabelButton(widget,
759 "remove_user.png", "remove_user.png", name='')
761 self.removeRecipientPushButton.setEnabled(1)
763 layout9.addWidget(self.removeRecipientPushButton,1,0)
764 spacer10 = QSpacerItem(20,20,QSizePolicy.MinimumExpanding,QSizePolicy.Minimum)
765 layout9.addItem(spacer10,1,3)
767 self.moveUpPushButton = PixmapLabelButton(widget,
768 "up_user.png", "up_user.png", name='')
770 layout9.addWidget(self.moveUpPushButton,1,1)
772 self.moveDownPushButton.setEnabled(False)
773 self.moveUpPushButton.setEnabled(False)
774 self.removeRecipientPushButton.setEnabled(False)
776 self.moveDownPushButton.setText(self.__tr("Move Down"))
777 self.fabPushButton.setText(self.__tr("Fax Address Book..."))
778 self.removeRecipientPushButton.setText(self.__tr("Remove"))
779 self.moveUpPushButton.setText(self.__tr("Move Up"))
782 self.connect(self.recipientListView,SIGNAL("rightButtonClicked(QListViewItem*,const QPoint&,int)"),self.recipientListView_rightButtonClicked)
784 self.connect(self.removeRecipientPushButton,SIGNAL("clicked()"),self.removeRecipientPushButton_clicked)
785 self.connect(self.moveUpPushButton,SIGNAL("clicked()"),self.moveUpPushButton_clicked)
786 self.connect(self.moveDownPushButton,SIGNAL("clicked()"),self.moveDownPushButton_clicked)
787 self.connect(self.fabPushButton,SIGNAL("clicked()"),self.fabPushButton_clicked)
789 self.connect(self.recipientListView, SIGNAL("selectionChanged(QListViewItem*)"), self.recipientListView_selectionChanged)
791 self.addWidget(widget, "recipient_list", maximize=False)
794 def recipientListView_selectionChanged(self, i):
796 self.prev_selected_recipient = i.name
797 except AttributeError:
800 rlv = self.recipientListView
801 selected_item = rlv.selectedItem()
802 recipient_count = rlv.childCount()
803 last_item = rlv.firstChild()
804 while last_item.nextSibling():
805 last_item = last_item.nextSibling()
807 self.moveDownPushButton.setEnabled(recipient_count > 1 and selected_item is not last_item)
808 self.moveUpPushButton.setEnabled(recipient_count > 1 and selected_item is not rlv.firstChild())
812 def updateRecipientList(self):
813 self.recipientListView.clear()
814 temp = self.recipient_list[:]
821 entry = self.db.get(name)
822 # TODO: If entry was in list prior to name change in hp-fab,
823 # this code will remove it instead of following the name change
825 if entry is not None:
826 i = RecipientListViewItem(self.recipientListView, str(order), name, entry['fax'], entry['notes'])
828 if not self.prev_selected_recipient or self.prev_selected_recipient == name:
829 self.recipientListView.setSelected(i, True)
831 self.prev_selected_recipient = name
835 last_item = self.recipientListView.firstChild()
836 while last_item is not None and last_item.nextSibling():
837 last_item = last_item.nextSibling()
839 child_count = self.recipientListView.childCount()
840 self.removeRecipientPushButton.setEnabled(child_count > 0)
841 self.moveDownPushButton.setEnabled(child_count > 1 and selected_item is not last_item)
842 self.moveUpPushButton.setEnabled(child_count > 1 and selected_item is not self.recipientListView.firstChild())
844 self.checkSendFaxButton()
848 def recipientListView_rightButtonClicked(self, item, pos, col):
851 popup = QPopupMenu(self)
852 ind = QPopupMenu(popup)
853 grp = QPopupMenu(popup)
855 all_entries = self.db.get_all_records()
857 popup.insertItem(QIconSet(load_pixmap('add_user', '16x16')),
858 self.__tr("Add Individual"), ind)
860 for e, v in all_entries.items():
861 if not e.startswith('__'):
862 self.ind_map[ind.insertItem(QIconSet(load_pixmap('add_user', '16x16')), e, None)] = e
864 all_groups = self.db.get_all_groups()
866 popup.insertItem(QIconSet(load_pixmap('add_users', '16x16')),
867 self.__tr("Add Group"), grp)
870 self.grp_map[grp.insertItem(QIconSet(load_pixmap('add_users', '16x16')),
874 popup.insertSeparator(-1)
876 popup.insertItem(QIconSet(load_pixmap('remove_user', '16x16')),
877 self.__tr("Remove"), self.removeRecipientPushButton_clicked)
879 if self.recipientListView.childCount() > 1:
880 last_item = self.recipientListView.firstChild()
881 while last_item is not None and last_item.nextSibling():
882 last_item = last_item.nextSibling()
884 if item is not self.recipientListView.firstChild():
885 popup.insertItem(QIconSet(load_pixmap('up_user', '16x16')),
886 self.__tr("Move Up"), self.moveUpPushButton_clicked)
888 if item is not last_item:
889 popup.insertItem(QIconSet(load_pixmap('down_user', '16x16')),
890 self.__tr("Move Down"), self.moveDownPushButton_clicked)
892 popup.insertSeparator(-1)
893 popup.insertItem(QIconSet(load_pixmap('fab', '16x16')),
894 self.__tr("Fax Address Book..."), self.fabPushButton_clicked)
896 self.connect(ind, SIGNAL("activated(int)"), self.ind_popup_activated)
897 self.connect(grp, SIGNAL("activated(int)"), self.grp_popup_activated)
902 def ind_popup_activated(self, i):
903 self.addRecipient(self.ind_map[i])
905 def grp_popup_activated(self, i):
906 self.addRecipient(self.grp_map[i], True)
908 def moveUpPushButton_clicked(self):
910 name = self.recipientListView.selectedItem().name
911 except AttributeError:
914 utils.list_move_up(self.recipient_list, name)
915 self.updateRecipientList()
917 def moveDownPushButton_clicked(self):
919 name = self.recipientListView.selectedItem().name
920 except AttributeError:
923 utils.list_move_down(self.recipient_list, name)
924 self.updateRecipientList()
927 def fabPushButton_clicked(self):
928 log.debug(self.cmd_fab)
930 cmd = ''.join([self.cur_device.device_vars.get(x, x) \
931 for x in self.cmd_fab.split('%')])
934 path = cmd.split()[0]
937 self.CleanupChildren()
938 #os.spawnvp(os.P_NOWAIT, path, args)
942 self.updateRecipientList()
943 self.updateRecipientCombos()
946 def CleanupChildren(self):
947 log.debug("Cleaning up child processes.")
949 os.waitpid(-1, os.WNOHANG)
954 def removeRecipientPushButton_clicked(self):
956 name = self.recipientListView.selectedItem().name
957 except AttributeError:
960 temp = self.recipient_list[:]
964 del self.recipient_list[index]
966 self.prev_selected_recipient = ''
967 self.updateRecipientList()
975 # ADD FROM ADDRESS BOOK
978 def addRecipientAddFromFAB(self):
979 widget = self.getWidget()
981 layout13 = QGridLayout(widget,1,1,5,10,"layout13")
983 self.groupComboBox = QComboBox(0,widget,"groupComboBox")
984 self.groupComboBox.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Fixed,0,0,self.groupComboBox.sizePolicy().hasHeightForWidth()))
986 layout13.addWidget(self.groupComboBox,1,2)
987 spacer12 = QSpacerItem(20,20,QSizePolicy.Preferred,QSizePolicy.Minimum)
988 layout13.addItem(spacer12,1,1)
990 self.textLabel1 = QLabel(widget,"textLabel1")
991 self.textLabel1.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Preferred,0,0,self.textLabel1.sizePolicy().hasHeightForWidth()))
993 layout13.addWidget(self.textLabel1,0,0)
995 self.individualComboBox = QComboBox(0,widget,"individualComboBox")
996 self.individualComboBox.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Fixed,0,0,self.individualComboBox.sizePolicy().hasHeightForWidth()))
998 layout13.addWidget(self.individualComboBox,0,2)
1000 self.textLabel2 = QLabel(widget,"textLabel2")
1001 self.textLabel2.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Preferred,0,0,self.textLabel2.sizePolicy().hasHeightForWidth()))
1003 layout13.addWidget(self.textLabel2,1,0)
1004 spacer11 = QSpacerItem(30,20,QSizePolicy.Preferred,QSizePolicy.Minimum)
1005 layout13.addItem(spacer11,0,1)
1007 self.addGroupPushButton = PixmapLabelButton(widget,
1008 "add_users.png", "add_users.png", name='addGroupPushButton')
1010 layout13.addWidget(self.addGroupPushButton,1,3)
1012 self.addIndividualPushButton = PixmapLabelButton(widget,
1013 "add_user.png", "add_user.png", name='addIndividualPushButton')
1016 layout13.addWidget(self.addIndividualPushButton,0,3)
1018 self.textLabel1.setText(self.__tr("Add an <b>individual </b>from the fax address book:"))
1019 self.textLabel2.setText(self.__tr("Add a <b>group</b> from the fax address book:"))
1020 self.addGroupPushButton.setText(self.__tr("Add"))
1021 self.addIndividualPushButton.setText(self.__tr("Add"))
1023 self.connect(self.addIndividualPushButton,SIGNAL("clicked()"),self.addIndividualPushButton_clicked)
1024 self.connect(self.addGroupPushButton,SIGNAL("clicked()"),self.addGroupPushButton_clicked)
1027 self.addWidget(widget, "recipient_add_from_fab")
1029 def addIndividualPushButton_clicked(self):
1030 self.addRecipient(unicode(self.individualComboBox.currentText()))
1032 def addGroupPushButton_clicked(self):
1033 self.addRecipient(unicode(self.groupComboBox.currentText()), True)
1035 def addRecipient(self, name, is_group=False):
1037 for i in self.db.group_members(name):
1038 #for i in self.db.GroupEntries(name):
1039 if not i.startswith('__'):
1040 self.recipient_list.append(i)
1041 self.prev_selected_recipient = self.recipient_list[-1]
1043 self.recipient_list.append(name)
1044 self.prev_selected_recipient = name
1046 self.updateRecipientList()
1048 def updateRecipientCombos(self):
1050 self.individualComboBox.clear()
1051 all_entries = self.db.get_all_records()
1052 self.addIndividualPushButton.setEnabled(len(all_entries))
1054 for e, v in all_entries.items():
1055 if not e.startswith('__'):
1056 self.individualComboBox.insertItem(e)
1059 self.groupComboBox.clear()
1060 all_groups = self.db.get_all_groups()
1061 self.addGroupPushButton.setEnabled(len(all_groups))
1063 for g in all_groups:
1064 self.groupComboBox.insertItem(g)
1071 def addRecipientQuickAdd(self):
1072 widget = self.getWidget()
1074 layout12 = QGridLayout(widget,1,1,5,10,"layout12")
1075 self.quickAddFaxLineEdit = QLineEdit(widget,"quickAddFaxLineEdit")
1077 self.quickAddFaxLineEdit.setValidator(PhoneNumValidator(self.quickAddFaxLineEdit))
1078 layout12.addWidget(self.quickAddFaxLineEdit,0,3)
1080 self.quickAddNameLineEdit = QLineEdit(widget,"quickAddNameLineEdit")
1081 layout12.addWidget(self.quickAddNameLineEdit,0,1)
1083 self.textLabel4 = QLabel(widget,"textLabel4")
1084 layout12.addWidget(self.textLabel4,0,0)
1086 self.quickAddPushButton = PixmapLabelButton(widget,
1087 "add_user_quick.png", "add_user_quick.png", name='quickAddPushButton')
1089 layout12.addWidget(self.quickAddPushButton,0,4)
1091 self.textLabel5 = QLabel(widget,"textLabel5")
1092 layout12.addWidget(self.textLabel5,0,2)
1094 self.textLabel4.setText(self.__tr("Name:"))
1095 self.quickAddPushButton.setText(self.__tr("Add"))
1096 self.textLabel5.setText(self.__tr("Fax Number:"))
1098 self.quickAddPushButton.setEnabled(False)
1100 self.connect(self.quickAddPushButton,SIGNAL("clicked()"),self.quickAddPushButton_clicked)
1101 self.connect(self.quickAddNameLineEdit,SIGNAL("textChanged(const QString&)"),self.quickAddNameLineEdit_textChanged)
1102 self.connect(self.quickAddFaxLineEdit,SIGNAL("textChanged(const QString&)"),self.quickAddFaxLineEdit_textChanged)
1104 self.addWidget(widget, "recipient_quick_add")
1107 def quickAddPushButton_clicked(self):
1108 name = unicode(self.quickAddNameLineEdit.text())
1109 self.db.set(name, u'', u'', u'', unicode(self.quickAddFaxLineEdit.text()), [], self.__tr('Added with Quick Add'))
1111 self.addRecipient(name)
1113 self.quickAddNameLineEdit.setText("")
1114 self.quickAddFaxLineEdit.setText("")
1117 def enableQuickAddButton(self, name=None, fax=None):
1119 name = unicode(self.quickAddNameLineEdit.text())
1121 fax = unicode(self.quickAddFaxLineEdit.text())
1123 existing_name = False
1125 existing_name = name in self.db.get_all_names()
1129 self.quickAddNameLineEdit.setPaletteBackgroundColor(QColor("yellow"))
1130 except AttributeError:
1134 self.quickAddNameLineEdit.setPaletteBackgroundColor(QColor("white"))
1135 except AttributeError:
1138 if name and not existing_name and fax:
1139 self.quickAddPushButton.setEnabled(True)
1141 self.quickAddPushButton.setEnabled(False)
1144 def quickAddNameLineEdit_textChanged(self, name):
1145 self.enableQuickAddButton(unicode(name))
1148 def quickAddFaxLineEdit_textChanged(self, fax):
1149 self.enableQuickAddButton(None, unicode(fax))
1152 def checkSendFaxButton(self):
1153 self.faxButton.setEnabled(len(self.file_list) and len(self.recipient_list))
1155 def faxButton_clicked(self):
1156 self.check_timer.stop()
1159 log.debug("Current printer=%s" % self.cur_printer)
1160 ppd_file = cups.getPPD(self.cur_printer)
1162 if ppd_file is not None and os.path.exists(ppd_file):
1163 if file(ppd_file, 'r').read().find('HP Fax') == -1:
1164 self.form.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.cur_printer))
1167 QApplication.setOverrideCursor(QApplication.waitCursor)
1169 self.dev = fax.getFaxDevice(self.cur_device.device_uri,
1170 self.cur_printer, None,
1171 self.cur_device.mq['fax-type'])
1180 self.dev.queryDevice(quick=True)
1182 log.error("Query device error (%s)." % e.msg)
1183 self.dev.error_state = ERROR_STATE_ERROR
1187 QApplication.restoreOverrideCursor()
1189 if self.dev.error_state > ERROR_STATE_MAX_OK and \
1190 self.dev.error_state not in (ERROR_STATE_LOW_SUPPLIES, ERROR_STATE_LOW_PAPER):
1192 self.form.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.cur_device.status_code))
1195 # Check to make sure queue in CUPS is idle
1196 self.cups_printers = cups.getPrinters()
1197 for p in self.cups_printers:
1198 if p.name == self.cur_printer:
1199 if p.state == cups.IPP_PRINTER_STATE_STOPPED:
1200 self.form.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.cur_printer))
1204 log.debug("Recipient list:")
1206 for p in self.recipient_list:
1207 entry = self.db.get(p)
1208 phone_num_list.append(entry)
1209 log.debug("Name=%s Number=%s" % (entry["name"], entry["fax"]))
1211 log.debug("File list:")
1214 for f in self.file_list:
1215 log.debug(unicode(f))
1219 self.dev.sendEvent(EVENT_START_FAX_JOB, self.cur_printer, 0, '')
1221 if not self.dev.sendFaxes(phone_num_list, self.file_list, self.cover_page_message,
1222 self.cover_page_re, self.cover_page_func, self.preserve_formatting,
1223 self.cur_printer, self.update_queue, self.event_queue):
1225 self.form.FailureUI(self.__tr("<b>Send fax is active.</b><p>Please wait for operation to complete."))
1226 self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.cur_printer, 0, '')
1231 self.waitdlg = WaitForm(0, self.__tr("Initializing..."), self.send_fax_canceled, self, modal=1)
1234 self.send_fax_timer = QTimer(self, "SendFaxTimer")
1235 self.connect(self.send_fax_timer, SIGNAL('timeout()'), self.send_fax_timer_timeout)
1236 self.send_fax_timer.start(1000) # 1 sec UI updates
1238 def send_fax_canceled(self):
1239 self.event_queue.put((fax.EVENT_FAX_SEND_CANCELED, '', '', ''))
1240 self.dev.sendEvent(EVENT_FAX_JOB_CANCELED, self.cur_printer, 0, '')
1242 def send_fax_timer_timeout(self):
1243 while self.update_queue.qsize():
1245 status, page_num, arg = self.update_queue.get(0)
1249 if status == fax.STATUS_IDLE:
1251 self.send_fax_timer.stop()
1253 if self.waitdlg is not None:
1255 self.waitdlg.close()
1258 elif status == fax.STATUS_PROCESSING_FILES:
1259 self.waitdlg.setMessage(self.__tr("Processing page %1...").arg(page_num))
1261 elif status == fax.STATUS_SENDING_TO_RECIPIENT:
1262 self.waitdlg.setMessage(self.__tr("Sending fax to %1...").arg(arg))
1264 elif status == fax.STATUS_DIALING:
1265 self.waitdlg.setMessage(self.__tr("Dialing %1...").arg(arg))
1267 elif status == fax.STATUS_CONNECTING:
1268 self.waitdlg.setMessage(self.__tr("Connecting to %1...").arg(arg))
1270 elif status == fax.STATUS_SENDING:
1271 self.waitdlg.setMessage(self.__tr("Sending page %1 to %2...").arg(page_num).arg(arg))
1273 elif status == fax.STATUS_CLEANUP:
1274 self.waitdlg.setMessage(self.__tr("Cleaning up..."))
1276 elif status in (fax.STATUS_ERROR, fax.STATUS_BUSY, fax.STATUS_COMPLETED):
1278 self.send_fax_timer.stop()
1280 if self.waitdlg is not None:
1282 self.waitdlg.close()
1285 if status == fax.STATUS_ERROR:
1286 result_code, error_state = self.dev.getPML(pml.OID_FAX_DOWNLOAD_ERROR)
1287 if error_state == pml.DN_ERROR_NONE:
1288 self.form.FailureUI(self.__tr("<b>Fax send error (Possible cause: No answer or dialtone)"))
1290 self.form.FailureUI(self.__tr("<b>Fax send error (%s).</b><p>" % pml.DN_ERROR_STR.get(error_state, "Unknown error")))
1291 self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.cur_printer, 0, '')
1293 elif status == fax.STATUS_BUSY:
1294 self.form.FailureUI(self.__tr("<b>Fax device is busy.</b><p>Please try again later."))
1295 self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.cur_printer, 0, '')
1297 elif status == fax.STATUS_COMPLETED:
1298 self.dev.sendEvent(EVENT_END_FAX_JOB, self.cur_printer, 0, '')
1300 self.funcButton_clicked()
1307 self.check_timer.stop()
1309 def funcButton_clicked(self):
1313 def __tr(self,s,c = None):
1314 return qApp.translate("ScrollFaxView",s,c)