1061 lines
37 KiB
Python
1061 lines
37 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# (c) Copyright 2001-2015 HP Development Company, L.P.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
# Authors: Don Welch
|
|
#
|
|
|
|
# StdLib
|
|
import operator
|
|
import struct
|
|
from base.sixext.moves import queue
|
|
from base.sixext import to_unicode
|
|
import signal
|
|
|
|
# Local
|
|
from base.g import *
|
|
from base import device, utils, pml
|
|
from prnt import cups
|
|
from base.codes import *
|
|
from .ui_utils import *
|
|
|
|
# Qt
|
|
from PyQt5.QtCore import *
|
|
from PyQt5.QtGui import *
|
|
from PyQt5.QtWidgets import *
|
|
|
|
# Ui
|
|
from .sendfaxdialog_base import Ui_Dialog
|
|
from .filetable import FileTable, FILETABLE_TYPE_FAX
|
|
from .printernamecombobox import PrinterNameComboBox, PRINTERNAMECOMBOBOX_TYPE_FAX_ONLY
|
|
from .printsettingsdialog import PrintSettingsDialog
|
|
from .faxsetupdialog import FaxSetupDialog
|
|
|
|
|
|
PAGE_SELECT_FAX = 0
|
|
PAGE_COVERPAGE = 1
|
|
PAGE_FILES = 2
|
|
PAGE_RECIPIENTS = 3
|
|
PAGE_SEND_FAX = 4
|
|
PAGE_MAX = 4
|
|
|
|
STATUS_INFORMATION = 0
|
|
STATUS_WARNING = 1
|
|
STATUS_ERROR = 2
|
|
|
|
MIME_TYPE_COVERPAGE = "application/hplip-fax-coverpage"
|
|
|
|
fax_enabled = prop.fax_build
|
|
|
|
if fax_enabled:
|
|
try:
|
|
from fax import fax
|
|
except ImportError:
|
|
# This can fail on Python < 2.3 due to the datetime module
|
|
# or if fax was diabled during the build
|
|
fax_enabled = False
|
|
|
|
if not fax_enabled:
|
|
log.warn("Fax disabled.")
|
|
|
|
coverpages_enabled = False
|
|
if fax_enabled:
|
|
try:
|
|
import reportlab
|
|
ver = str(reportlab.Version)
|
|
|
|
if ver >= "2.0":
|
|
coverpages_enabled = True
|
|
else:
|
|
log.warn("Pre-2.0 version of Reportlab installed. Fax coverpages disabled.")
|
|
|
|
except ImportError:
|
|
log.warn("Reportlab not installed. Fax coverpages disabled.")
|
|
|
|
if not coverpages_enabled:
|
|
log.warn("Please install version 2.0+ of Reportlab for coverpage support.")
|
|
|
|
if fax_enabled:
|
|
from .fabwindow import FABWindow
|
|
|
|
if coverpages_enabled:
|
|
from fax import coverpages
|
|
|
|
|
|
class SendFaxDialog(QDialog, Ui_Dialog):
|
|
def __init__(self, parent, printer_name, device_uri=None, args=None):
|
|
QDialog.__init__(self, parent)
|
|
self.setupUi(self)
|
|
|
|
self.printer_name = printer_name
|
|
if device_uri is not None:
|
|
self.device_uri = device_uri
|
|
else:
|
|
self.device_uri = device.getDeviceURIByPrinterName(self.printer_name)
|
|
|
|
self.args = args
|
|
self.dev = None
|
|
|
|
self.dbus_avail, self.service, session_bus = device.init_dbus()
|
|
|
|
self.CheckTimer = None
|
|
self.lock_file = None
|
|
self.file_list = []
|
|
self.recipient_list = []
|
|
|
|
self.initUi()
|
|
|
|
if self.printer_name:
|
|
if coverpages_enabled:
|
|
QTimer.singleShot(0, self.displayCoverpagePage)
|
|
else:
|
|
self.lockAndLoad()
|
|
QTimer.singleShot(0, self.displayFilesPage)
|
|
else:
|
|
QTimer.singleShot(0, self.displaySelectFaxPage)
|
|
|
|
|
|
def initUi(self):
|
|
# connect signals/slots
|
|
self.CancelButton.clicked.connect(self.CancelButton_clicked)
|
|
self.BackButton.clicked.connect(self.BackButton_clicked)
|
|
self.NextButton.clicked.connect(self.NextButton_clicked)
|
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
|
|
self.initSelectFaxPage()
|
|
self.initCoverpagePage()
|
|
self.initFilesPage()
|
|
self.initRecipientsPage()
|
|
self.initSendFaxPage()
|
|
|
|
# Application icon
|
|
self.setWindowIcon(QIcon(load_pixmap('hp_logo', '128x128')))
|
|
|
|
|
|
def lockAndLoad(self):
|
|
# Start up check timer here, since the fax name is now known
|
|
if self.CheckTimer is None:
|
|
self.CheckTimer = QTimer(self)
|
|
self.CheckTimer.timeout.connect(self.CheckTimer_timeout)
|
|
self.CheckTimer.start(3000)
|
|
|
|
# Lock the app
|
|
if self.printer_name and self.lock_file is None:
|
|
ok, self.lock_file = utils.lock_app('hp-sendfax-%s' % self.printer_name, True)
|
|
|
|
if not ok:
|
|
log.error("hp-sendfax is already running for fax %s" % self.printer_name)
|
|
# TODO:
|
|
|
|
#
|
|
# Select Fax Page
|
|
#
|
|
|
|
def initSelectFaxPage(self):
|
|
self.FaxComboBox.setType(PRINTERNAMECOMBOBOX_TYPE_FAX_ONLY)
|
|
self.FaxComboBox.PrinterNameComboBox_currentChanged.connect(self.FaxComboBox_currentChanged)
|
|
self.FaxComboBox.PrinterNameComboBox_noPrinters.connect(self.FaxComboBox_noPrinters)
|
|
self.FaxOptionsButton.clicked.connect(self.FaxOptionsButton_clicked)
|
|
self.FaxSetupButton.clicked.connect(self.FaxSetupButton_clicked)
|
|
|
|
if self.printer_name is not None:
|
|
self.FaxComboBox.setInitialPrinter(self.printer_name)
|
|
|
|
|
|
def displaySelectFaxPage(self):
|
|
self.BackButton.setEnabled(False)
|
|
self.updateStepText(PAGE_SELECT_FAX)
|
|
|
|
if not fax_enabled:
|
|
FailureUI(self, self.__tr("<b>PC send fax support is not enabled.</b><p>Re-install HPLIP with fax support or use the device front panel to send a fax.</p><p>Click <i>OK</i> to exit.</p>"))
|
|
self.close()
|
|
return
|
|
|
|
if not self.dbus_avail:
|
|
FailureUI(self, self.__tr("<b>PC send fax support requires DBus and hp-systray.</b><p>Please check the HPLIP installation for proper installation of DBus and hp-systray support.</p><p>Click <i>OK</i> to exit.</p>"))
|
|
self.close()
|
|
return
|
|
|
|
self.FaxComboBox.updateUi()
|
|
self.displayPage(PAGE_SELECT_FAX)
|
|
|
|
|
|
def FaxComboBox_currentChanged(self, device_uri, printer_name):
|
|
self.printer_name = printer_name
|
|
self.device_uri = device_uri
|
|
|
|
|
|
def FaxComboBox_noPrinters(self):
|
|
FailureUI(self, self
|
|
.__tr("<b>No installed fax devices found.</b><p>Please setup a fax device and try again (try using 'hp-setup').</p><p>Click <i>OK</i> to exit.</p>"))
|
|
self.close()
|
|
|
|
|
|
def FaxOptionsButton_clicked(self):
|
|
dlg = PrintSettingsDialog(self, self.printer_name, fax_mode=True)
|
|
dlg.exec_()
|
|
|
|
|
|
def FaxSetupButton_clicked(self):
|
|
dlg = FaxSetupDialog(self, self.device_uri)
|
|
dlg.exec_()
|
|
|
|
#
|
|
# Coverpage Page
|
|
#
|
|
|
|
def initCoverpagePage(self):
|
|
self.cover_page_message = ''
|
|
self.cover_page_re = ''
|
|
self.preserve_formatting = False
|
|
self.cover_page_func, cover_page_png = None, None
|
|
self.last_job_id = 0
|
|
self.busy = False
|
|
self.PrevCoverPageButton.setIcon(QIcon(load_pixmap("prev", "16x16")))
|
|
self.NextCoverPageButton.setIcon(QIcon(load_pixmap("next", "16x16")))
|
|
|
|
if coverpages_enabled:
|
|
self.cover_page_list = list(coverpages.COVERPAGES.keys())
|
|
self.cover_page_index = self.cover_page_list.index("basic")
|
|
self.cover_page_max = len(self.cover_page_list)-1
|
|
self.cover_page_name = self.cover_page_list[self.cover_page_index]
|
|
|
|
self.PrevCoverPageButton.clicked.connect(self.PrevCoverPageButton_clicked)
|
|
self.NextCoverPageButton.clicked.connect(self.NextCoverPageButton_clicked)
|
|
self.CoverPageGroupBox.toggled[bool].connect(self.CoverPageGroupBox_toggled)
|
|
self.MessageEdit.textChanged.connect(self.MessageEdit_textChanged)
|
|
self.RegardingEdit.textChanged["const QString &"].connect(self.RegardingEdit_textChanged)
|
|
self.PreserveFormattingCheckBox.toggled[bool].connect(self.PreserveFormattingCheckBox_toggled)
|
|
else:
|
|
self.CoverPageGroupBox.setEnabled(False)
|
|
|
|
|
|
def displayCoverpagePage(self):
|
|
self.BackButton.setEnabled(False) # No going back once printer is chosen
|
|
self.NextButton.setEnabled(True)
|
|
|
|
self.lockAndLoad()
|
|
|
|
self.updateCoverpageButtons()
|
|
self.displayCoverpagePreview()
|
|
self.displayPage(PAGE_COVERPAGE)
|
|
|
|
|
|
def MessageEdit_textChanged(self):
|
|
self.cover_page_message = to_unicode(self.MessageEdit.toPlainText())
|
|
|
|
|
|
def RegardingEdit_textChanged(self, t):
|
|
self.cover_page_re = to_unicode(t)
|
|
|
|
|
|
def PreserveFormattingCheckBox_toggled(self, b):
|
|
self.preserve_formatting = b
|
|
|
|
|
|
def PrevCoverPageButton_clicked(self):
|
|
self.cover_page_index -= 1
|
|
if self.cover_page_index < 0:
|
|
self.cover_page_index = 0
|
|
else:
|
|
self.updateCoverpageButtons()
|
|
self.displayCoverpagePage()
|
|
|
|
|
|
def NextCoverPageButton_clicked(self):
|
|
self.cover_page_index += 1
|
|
if self.cover_page_index > self.cover_page_max:
|
|
self.cover_page_index = self.cover_page_max
|
|
else:
|
|
self.updateCoverpageButtons()
|
|
self.displayCoverpagePage()
|
|
|
|
|
|
def displayCoverpagePreview(self):
|
|
if coverpages_enabled:
|
|
self.cover_page_name = self.cover_page_list[self.cover_page_index]
|
|
self.cover_page_func = coverpages.COVERPAGES[self.cover_page_name][0]
|
|
self.CoverPageName.setText(str('<i>"%s"</i>'%self.cover_page_name))
|
|
self.CoverPagePreview.setPixmap(load_pixmap(coverpages.COVERPAGES[self.cover_page_name][1], 'other'))
|
|
|
|
if self.CoverPageGroupBox.isChecked():
|
|
self.addCoverPage()
|
|
else:
|
|
self.removeCoverPage()
|
|
|
|
|
|
def updateCoverpageButtons(self):
|
|
enabled = self.CoverPageGroupBox.isChecked()
|
|
self.PrevCoverPageButton.setEnabled(enabled and self.cover_page_index != 0)
|
|
self.NextCoverPageButton.setEnabled(enabled and self.cover_page_index != self.cover_page_max)
|
|
|
|
|
|
def CoverPageGroupBox_toggled(self, b):
|
|
self.updateCoverpageButtons()
|
|
if b:
|
|
self.addCoverPage()
|
|
else:
|
|
self.removeCoverPage()
|
|
|
|
|
|
def addCoverPage(self):
|
|
self.removeCoverPage()
|
|
self.FilesTable.addFile(self.cover_page_name, MIME_TYPE_COVERPAGE,
|
|
self.__tr('HP Fax Coverpage: "%s"'%self.cover_page_name),
|
|
self.__tr("Cover Page"), 1)
|
|
|
|
|
|
def removeCoverPage(self):
|
|
self.FilesTable.removeFileByMIMEType(MIME_TYPE_COVERPAGE)
|
|
|
|
|
|
def toggleCoverPage(self, b):
|
|
# XXX: qt5port: disconnect method requires attention
|
|
self.CoverPageGroupBox.toggled[bool].disconnect(self.CoverPageGroupBox_toggled)
|
|
self.CoverPageGroupBox.setChecked(b)
|
|
self.CoverPageGroupBox.toggled[bool].connect(self.CoverPageGroupBox_toggled)
|
|
|
|
|
|
#
|
|
# Files Page
|
|
#
|
|
|
|
def initFilesPage(self):
|
|
self.FilesTable.setType(FILETABLE_TYPE_FAX)
|
|
self.FilesTable.setFaxCallback(self.FileTable_callback)
|
|
self.FilesTable.isEmpty.connect(self.FilesTable_isEmpty)
|
|
self.FilesTable.isNotEmpt.connect(self.FilesTable_isNotEmpty)
|
|
self.FilesTable.fileListChanged.connect(self.FilesTable_fileListChanged)
|
|
|
|
|
|
def displayFilesPage(self):
|
|
self.FilesTable.updateUi(False)
|
|
|
|
if self.args is not None:
|
|
for a in self.args:
|
|
f = os.path.abspath(os.path.expanduser(a))
|
|
if os.path.exists(f) and os.access(f, os.R_OK):
|
|
self.renderFile(f)
|
|
|
|
self.args = None
|
|
|
|
self.restoreNextButton()
|
|
self.NextButton.setEnabled(self.FilesTable.isNotEmpty())
|
|
self.BackButton.setEnabled(coverpages_enabled)
|
|
self.FilesPageNote.setText(self.__tr("Note: You may also add files to the fax by printing from any application to the '%s' fax printer."%self.printer_name))
|
|
self.displayPage(PAGE_FILES)
|
|
|
|
|
|
def FilesTable_isEmpty(self):
|
|
if self.StackedWidget.currentIndex() == PAGE_FILES:
|
|
self.NextButton.setEnabled(False)
|
|
|
|
|
|
def FilesTable_isNotEmpty(self):
|
|
if self.StackedWidget.currentIndex() == PAGE_FILES:
|
|
self.NextButton.setEnabled(True)
|
|
|
|
|
|
def FilesTable_fileListChanged(self):
|
|
self.file_list = self.FilesTable.file_list
|
|
self.toggleCoverPage(self.FilesTable.isMIMETypeInList(MIME_TYPE_COVERPAGE))
|
|
|
|
|
|
#
|
|
# Recipients Page
|
|
#
|
|
|
|
def initRecipientsPage(self):
|
|
# setup validators
|
|
self.QuickAddFaxEdit.setValidator(PhoneNumValidator(self.QuickAddFaxEdit))
|
|
|
|
# Fax address book database
|
|
self.db = fax.FaxAddressBook()
|
|
|
|
# Fax address book window
|
|
self.fab = FABWindow(self)
|
|
self.fab.setWindowFlags(Qt.Tool) # Keeps the Fab window on top
|
|
|
|
self.fab.databaseChanged.connect(self.FABWindow_databaseChanged)
|
|
|
|
# connect signals
|
|
# self.connect(self.QuickAddFaxEdit, SIGNAL("textChanged(const QString &)"),
|
|
# self.QuickAddFaxEdit_textChanged)
|
|
self.QuickAddFaxEdit.textChanged["const QString &"].connect(self.QuickAddFaxEdit_textChanged)
|
|
# self.connect(self.QuickAddNameEdit, SIGNAL("textChanged(const QString &)"),
|
|
# self.QuickAddNameEdit_textChanged)
|
|
self.QuickAddNameEdit.textChanged["const QString &"].connect(self.QuickAddNameEdit_textChanged)
|
|
self.QuickAddButton.clicked.connect(self.QuickAddButton_clicked)
|
|
self.FABButton.clicked.connect(self.FABButton_clicked)
|
|
self.AddIndividualButton.clicked.connect(self.AddIndividualButton_clicked)
|
|
self.AddGroupButton.clicked.connect(self.AddGroupButton_clicked)
|
|
self.RemoveRecipientButton.clicked.connect(self.RemoveRecipientButton_clicked)
|
|
self.MoveRecipientUpButton.clicked.connect(self.MoveRecipientUpButton_clicked)
|
|
self.MoveRecipientDownButton.clicked.connect(self.MoveRecipientDownButton_clicked)
|
|
# self.connect(self.RecipientsTable, SIGNAL("itemSelectionChanged()"),
|
|
# self.RecipientsTable_itemSelectionChanged)
|
|
self.RecipientsTable.itemSelectionChanged.connect(self.RecipientsTable_itemSelectionChanged)
|
|
|
|
#self.connect(self.RecipientsTable, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"),
|
|
# self.RecipientsTable_itemDoubleClicked)
|
|
self.RecipientsTable.itemDoubleClicked["QTableWidgetItem *"].connect(self.RecipientsTable_itemDoubleClicked)
|
|
|
|
# setup icons
|
|
self.FABButton.setIcon(QIcon(load_pixmap("fab", "16x16")))
|
|
self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user", "16x16")))
|
|
self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users", "16x16")))
|
|
self.RemoveRecipientButton.setIcon(QIcon(load_pixmap("remove_user", "16x16")))
|
|
self.MoveRecipientUpButton.setIcon(QIcon(load_pixmap("up_user", "16x16")))
|
|
self.MoveRecipientDownButton.setIcon(QIcon(load_pixmap("down_user", "16x16")))
|
|
self.QuickAddButton.setIcon(QIcon(load_pixmap("add_user_quick", "16x16")))
|
|
|
|
# setup initial state
|
|
self.QuickAddButton.setEnabled(False)
|
|
|
|
self.recipient_headers = [self.__tr("Name"), self.__tr("Fax number"), self.__tr("Notes")]
|
|
|
|
|
|
def FABWindow_databaseChanged(self, action, s1='', s2=''):
|
|
self.db.load()
|
|
|
|
if action in (FAB_NAME_ADD, FAB_GROUP_ADD, FAB_GROUP_RENAME,
|
|
FAB_GROUP_REMOVE, FAB_GROUP_MEMBERSHIP_CHANGED):
|
|
|
|
log.debug("Fax address book has changed")
|
|
self.updateAddressBook()
|
|
|
|
elif action == FAB_NAME_REMOVE:
|
|
log.debug("Fax address book has changed: '%s' removed" % s1)
|
|
all_names = self.db.get_all_names()
|
|
self.recipient_list = [x for x in all_names if x in self.recipient_list]
|
|
self.updateAddressBook()
|
|
self.updateRecipientTable()
|
|
|
|
elif action == FAB_NAME_RENAME:
|
|
log.debug("Fax address book has changed: '%s' renamed to '%s'" % (s1, s2))
|
|
for i, n in enumerate(self.recipient_list):
|
|
if n == s1:
|
|
self.recipient_list[i] = s2
|
|
self.updateRecipientTable()
|
|
break
|
|
else:
|
|
self.updateAddressBook()
|
|
|
|
elif action == FAB_NAME_DETAILS_CHANGED:
|
|
log.debug("Fax address book has changed: '%s' details changed" % s1)
|
|
self.updateRecipientTable()
|
|
|
|
|
|
def displayRecipientsPage(self):
|
|
self.updateAddressBook()
|
|
self.updateRecipientTable()
|
|
self.enableQuickAddButton()
|
|
self.displayPage(PAGE_RECIPIENTS)
|
|
self.restoreNextButton()
|
|
self.BackButton.setEnabled(True)
|
|
|
|
|
|
def updateAddressBook(self):
|
|
names = [n for n in self.db.get_all_names() if not n.startswith('__')]
|
|
groups = self.db.get_all_groups()
|
|
self.AddIndividualComboBox.clear()
|
|
self.AddGroupComboBox.clear()
|
|
|
|
i = 0
|
|
names.sort()
|
|
for n in names:
|
|
if n not in self.recipient_list:
|
|
data = self.db.get(n)
|
|
if data['fax']:
|
|
self.AddIndividualComboBox.addItem(n)
|
|
i += 1
|
|
|
|
if i:
|
|
self.AddIndividualButton.setEnabled(True)
|
|
self.AddIndividualComboBox.setEnabled(True)
|
|
#self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user", "16x16")))
|
|
|
|
else:
|
|
self.AddIndividualButton.setEnabled(False)
|
|
self.AddIndividualComboBox.setEnabled(False)
|
|
#self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user-disabled", "16x16")))
|
|
|
|
i = 0
|
|
groups.sort()
|
|
for g in groups:
|
|
for n in self.db.group_members(g):
|
|
if not n.startswith('__') and n not in self.recipient_list:
|
|
self.AddGroupComboBox.addItem(g)
|
|
i += 1
|
|
break
|
|
|
|
if i:
|
|
self.AddGroupButton.setEnabled(True)
|
|
self.AddGroupComboBox.setEnabled(True)
|
|
#self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users", "16x16")))
|
|
|
|
else:
|
|
self.AddGroupButton.setEnabled(False)
|
|
self.AddGroupComboBox.setEnabled(False)
|
|
#self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users-disabled", "16x16")))
|
|
|
|
|
|
def updateRecipientTable(self):
|
|
try:
|
|
prev = self.getCurrentRecipient()
|
|
except (TypeError, AttributeError):
|
|
prev = None
|
|
|
|
self.RecipientsTable.clear()
|
|
self.RecipientsTable.setRowCount(0)
|
|
self.RecipientsTable.setColumnCount(0)
|
|
|
|
if self.recipient_list:
|
|
num_recipients = len(self.recipient_list)
|
|
|
|
self.RecipientsTable.setColumnCount(len(self.recipient_headers))
|
|
self.RecipientsTable.setHorizontalHeaderLabels(self.recipient_headers)
|
|
self.RecipientsTable.setRowCount(num_recipients)
|
|
flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
|
|
|
|
j = None
|
|
for row, n in enumerate(self.recipient_list):
|
|
i = QTableWidgetItem(str(n))
|
|
i.setFlags(flags)
|
|
self.RecipientsTable.setItem(row, 0, i)
|
|
if prev is not None and n == prev:
|
|
j = i
|
|
|
|
k = self.db.get(n)
|
|
if not k:
|
|
continue
|
|
|
|
i = QTableWidgetItem(str(k['fax']))
|
|
i.setFlags(flags)
|
|
self.RecipientsTable.setItem(row, 1, i)
|
|
|
|
i = QTableWidgetItem(str(k['notes']))
|
|
i.setFlags(flags)
|
|
self.RecipientsTable.setItem(row, 2, i)
|
|
|
|
self.RecipientsTable.resizeColumnsToContents()
|
|
self.RecipientsTable.resizeRowsToContents()
|
|
|
|
if j is not None:
|
|
self.RecipientsTable.setCurrentItem(j)
|
|
else:
|
|
self.RecipientsTable.setCurrentItem(self.RecipientsTable.item(0, 0))
|
|
|
|
self.NextButton.setEnabled(True)
|
|
|
|
else:
|
|
self.enableRecipientListButtons()
|
|
self.NextButton.setEnabled(False)
|
|
|
|
|
|
def RecipientsTable_itemSelectionChanged(self):
|
|
current_row = self.RecipientsTable.currentRow()
|
|
num_recipients = len(self.recipient_list)
|
|
self.enableRecipientListButtons(num_recipients > 0, # remove
|
|
num_recipients > 1 and current_row > 0, # up
|
|
num_recipients > 1 and current_row < (num_recipients-1)) # down
|
|
|
|
|
|
def enableRecipientListButtons(self, enable_remove=False, enable_up_move=False, enable_down_move=False):
|
|
if enable_remove:
|
|
self.RemoveRecipientButton.setEnabled(True)
|
|
else:
|
|
self.RemoveRecipientButton.setEnabled(False)
|
|
|
|
if enable_up_move:
|
|
self.MoveRecipientUpButton.setEnabled(True)
|
|
else:
|
|
self.MoveRecipientUpButton.setEnabled(False)
|
|
|
|
if enable_down_move:
|
|
self.MoveRecipientDownButton.setEnabled(True)
|
|
else:
|
|
self.MoveRecipientDownButton.setEnabled(False)
|
|
|
|
|
|
def QuickAddFaxEdit_textChanged(self, fax):
|
|
self.enableQuickAddButton(None, to_unicode(fax))
|
|
|
|
|
|
def QuickAddNameEdit_textChanged(self, name):
|
|
self.enableQuickAddButton(to_unicode(name))
|
|
|
|
|
|
def enableQuickAddButton(self, name=None, fax=None):
|
|
if name is None:
|
|
name = to_unicode(self.QuickAddNameEdit.text())
|
|
if fax is None:
|
|
fax = to_unicode(self.QuickAddFaxEdit.text())
|
|
|
|
existing_name = False
|
|
if name:
|
|
existing_name = name in self.db.get_all_names()
|
|
|
|
if existing_name:
|
|
try:
|
|
self.QuickAddNameEdit.setStyleSheet("background-color: yellow")
|
|
except AttributeError:
|
|
pass
|
|
else:
|
|
try:
|
|
self.QuickAddNameEdit.setStyleSheet("")
|
|
except AttributeError:
|
|
pass
|
|
|
|
if name and not existing_name and fax:
|
|
self.QuickAddButton.setEnabled(True)
|
|
else:
|
|
self.QuickAddButton.setEnabled(False)
|
|
|
|
|
|
def QuickAddButton_clicked(self):
|
|
name = to_unicode(self.QuickAddNameEdit.text())
|
|
fax = to_unicode(self.QuickAddFaxEdit.text())
|
|
self.fab.addName(name, fax)
|
|
self.addRecipient(name)
|
|
self.updateRecipientTable()
|
|
self.QuickAddNameEdit.clear()
|
|
self.QuickAddFaxEdit.clear()
|
|
self.enableQuickAddButton('', '')
|
|
|
|
|
|
def AddIndividualButton_clicked(self):
|
|
self.addRecipient(to_unicode(self.AddIndividualComboBox.currentText()))
|
|
|
|
|
|
def AddGroupButton_clicked(self):
|
|
self.addGroup(to_unicode(self.AddGroupComboBox.currentText()))
|
|
|
|
|
|
def RemoveRecipientButton_clicked(self):
|
|
name = self.getCurrentRecipient()
|
|
temp = self.recipient_list[:]
|
|
for i, n in enumerate(temp):
|
|
if name == n:
|
|
del self.recipient_list[i]
|
|
self.updateRecipientTable()
|
|
self.updateAddressBook()
|
|
break
|
|
|
|
|
|
def MoveRecipientUpButton_clicked(self):
|
|
utils.list_move_up(self.recipient_list, self.getCurrentRecipient())
|
|
self.updateRecipientTable()
|
|
|
|
|
|
def MoveRecipientDownButton_clicked(self):
|
|
utils.list_move_down(self.recipient_list, self.getCurrentRecipient())
|
|
self.updateRecipientTable()
|
|
|
|
|
|
def getCurrentRecipient(self):
|
|
item = self.RecipientsTable.item(self.RecipientsTable.currentRow(), 0)
|
|
if item is not None:
|
|
return to_unicode(item.text())
|
|
else:
|
|
return to_unicode('')
|
|
|
|
|
|
def addRecipient(self, name, update=True):
|
|
if name not in self.recipient_list and not name.startswith('__'):
|
|
self.recipient_list.append(name)
|
|
if update:
|
|
self.updateRecipientTable()
|
|
self.updateAddressBook()
|
|
|
|
|
|
def addGroup(self, group):
|
|
for n in self.db.group_members(group):
|
|
self.addRecipient(n, False)
|
|
|
|
self.updateRecipientTable()
|
|
self.updateAddressBook()
|
|
|
|
|
|
def FABButton_clicked(self):
|
|
self.fab.show()
|
|
|
|
|
|
def RecipientsTable_itemDoubleClicked(self, item):
|
|
if item is not None:
|
|
row, col = item.row(), item.column()
|
|
if col != 0:
|
|
item = self.RecipientsTable.item(row, 0)
|
|
|
|
self.fab.selectByName(to_unicode(item.text()))
|
|
self.fab.show()
|
|
|
|
|
|
#
|
|
# Send Fax Page
|
|
#
|
|
|
|
def initSendFaxPage(self):
|
|
self.info_icon = QIcon(load_pixmap("info", "16x16"))
|
|
self.warn_icon = QIcon(load_pixmap("warning", "16x16"))
|
|
self.error_icon = QIcon(load_pixmap("error", "16x16"))
|
|
self.busy_icon = QIcon(load_pixmap("busy", "16x16"))
|
|
self.update_queue = queue.Queue() # UI updates from send thread
|
|
self.event_queue = queue.Queue() # UI events (cancel) to send thread
|
|
self.send_fax_active = False
|
|
|
|
|
|
def displaySendFaxPage(self):
|
|
self.displayPage(PAGE_SEND_FAX)
|
|
self.addStatusMessage(self.__tr("Ready to send fax."), self.info_icon)
|
|
self.NextButton.setText(self.__tr("Send Fax"))
|
|
|
|
|
|
|
|
#
|
|
# Fax
|
|
#
|
|
|
|
def executeSendFax(self):
|
|
self.NextButton.setEnabled(False)
|
|
self.BackButton.setEnabled(False)
|
|
self.CheckTimer.stop()
|
|
self.busy = True
|
|
phone_num_list = []
|
|
|
|
ppd_file = cups.getPPD(self.printer_name)
|
|
|
|
if ppd_file is not None and os.path.exists(ppd_file):
|
|
if open(ppd_file, 'rb').read().find(b'HP Fax') == -1:
|
|
FailureUI(self, self.__tr("<b>Fax configuration error.</b><p>The CUPS fax queue for '%s' is incorrectly configured.<p>Please make sure that the CUPS fax queue is configured with the 'HPLIP Fax' Model/Driver."%self.printer_name))
|
|
self.close()
|
|
return
|
|
|
|
beginWaitCursor()
|
|
|
|
mq = device.queryModelByURI(self.device_uri)
|
|
|
|
self.dev = fax.getFaxDevice(self.device_uri,
|
|
self.printer_name, None,
|
|
mq['fax-type'])
|
|
|
|
try:
|
|
try:
|
|
self.dev.open()
|
|
except Error as e:
|
|
log.warn(e.msg)
|
|
|
|
try:
|
|
self.dev.queryDevice(quick=True)
|
|
except Error as e:
|
|
log.error("Query device error (%s)." % e.msg)
|
|
self.dev.error_state = ERROR_STATE_ERROR
|
|
|
|
finally:
|
|
self.dev.close()
|
|
endWaitCursor()
|
|
|
|
if self.dev.error_state > ERROR_STATE_MAX_OK and \
|
|
self.dev.error_state not in (ERROR_STATE_LOW_SUPPLIES, ERROR_STATE_LOW_PAPER):
|
|
|
|
FailureUI(self, self.__tr("<b>Device is busy or in an error state (code=%s)</b><p>Please wait for the device to become idle or clear the error and try again."%self.dev.status_code))
|
|
self.NextButton.setEnabled(True)
|
|
return
|
|
|
|
# Check to make sure queue in CUPS is idle
|
|
self.cups_printers = cups.getPrinters()
|
|
for p in self.cups_printers:
|
|
if p.name == self.printer_name:
|
|
if p.state == cups.IPP_PRINTER_STATE_STOPPED:
|
|
FailureUI(self, self.__tr("<b>The CUPS queue for '%s' is in a stopped or busy state.</b><p>Please check the queue and try again."%self.printer_name))
|
|
self.NextButton.setEnabled(False)
|
|
return
|
|
break
|
|
|
|
log.debug("Recipient list:")
|
|
|
|
for p in self.recipient_list:
|
|
entry = self.db.get(p)
|
|
phone_num_list.append(entry)
|
|
log.debug("Name=%s Number=%s" % (entry["name"], entry["fax"]))
|
|
|
|
log.debug("File list:")
|
|
|
|
for f in self.file_list:
|
|
log.debug(f)
|
|
|
|
self.dev.sendEvent(EVENT_START_FAX_JOB, self.printer_name, 0, '')
|
|
|
|
if not self.dev.sendFaxes(phone_num_list, self.file_list, self.cover_page_message,
|
|
self.cover_page_re, self.cover_page_func, self.preserve_formatting,
|
|
self.printer_name, self.update_queue, self.event_queue):
|
|
|
|
FailureUI(self, self.__tr("<b>Send fax is active.</b><p>Please wait for operation to complete."))
|
|
self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
|
|
self.busy = False
|
|
self.send_fax_active = False
|
|
#self.NextButton.setEnabled(False)
|
|
self.setCancelCloseButton()
|
|
return
|
|
|
|
self.send_fax_active = True
|
|
self.setCancelCloseButton()
|
|
self.SendFaxTimer = QTimer(self)
|
|
self.SendFaxTimer.timeout.connect(self.SendFaxTimer_timeout)
|
|
self.SendFaxTimer.start(1000) # 1 sec UI updates
|
|
|
|
|
|
def setCancelCloseButton(self):
|
|
if self.send_fax_active:
|
|
self.CancelButton.setText(self.__tr("Cancel Send"))
|
|
else:
|
|
self.CancelButton.setText(self.__tr("Close"))
|
|
|
|
|
|
def CancelButton_clicked(self):
|
|
if self.send_fax_active:
|
|
self.addStatusMessage(self.__tr("Cancelling job..."), self.warn_icon)
|
|
self.event_queue.put((fax.EVENT_FAX_SEND_CANCELED, '', '', ''))
|
|
self.dev.sendEvent(EVENT_FAX_JOB_CANCELED, self.printer_name, 0, '')
|
|
else:
|
|
self.close()
|
|
|
|
|
|
def SendFaxTimer_timeout(self):
|
|
while self.update_queue.qsize():
|
|
try:
|
|
status, page_num, arg = self.update_queue.get(0)
|
|
except queue.Empty:
|
|
break
|
|
|
|
if status == fax.STATUS_IDLE:
|
|
self.busy = False
|
|
self.send_fax_active = False
|
|
self.setCancelCloseButton()
|
|
self.SendFaxTimer.stop()
|
|
|
|
elif status == fax.STATUS_PROCESSING_FILES:
|
|
self.addStatusMessage(self.__tr("Processing page %s..."%page_num), self.busy_icon)
|
|
|
|
elif status == fax.STATUS_SENDING_TO_RECIPIENT:
|
|
self.addStatusMessage(self.__tr("Sending fax to %s..."%arg), self.busy_icon)
|
|
|
|
elif status == fax.STATUS_DIALING:
|
|
self.addStatusMessage(self.__tr("Dialing %s..."%arg), self.busy_icon)
|
|
|
|
elif status == fax.STATUS_CONNECTING:
|
|
self.addStatusMessage(self.__tr("Connecting to %s..."%arg), self.busy_icon)
|
|
|
|
elif status == fax.STATUS_SENDING:
|
|
self.addStatusMessage(self.__tr("Sending page %s to %s..."%(page_num,arg)),
|
|
self.busy_icon)
|
|
|
|
elif status == fax.STATUS_CLEANUP:
|
|
self.addStatusMessage(self.__tr("Cleaning up..."), self.busy_icon)
|
|
|
|
elif status in (fax.STATUS_ERROR, fax.STATUS_BUSY, fax.STATUS_COMPLETED, fax.STATUS_ERROR_IN_CONNECTING,
|
|
fax.STATUS_ERROR_IN_TRANSMITTING, fax.STATUS_ERROR_PROBLEM_IN_FAXLINE, fax.STATUS_JOB_CANCEL ):
|
|
self.busy = False
|
|
self.send_fax_active = False
|
|
self.setCancelCloseButton()
|
|
self.SendFaxTimer.stop()
|
|
|
|
if status == fax.STATUS_ERROR:
|
|
result_code, error_state = self.dev.getPML(pml.OID_FAX_DOWNLOAD_ERROR)
|
|
#FailureUI(self, self.__tr("<b>Fax send error (%s).</b><p>" % pml.DN_ERROR_STR.get(error_state, "Unknown error")))
|
|
if error_state == pml.DN_ERROR_NONE:
|
|
self.addStatusMessage(self.__tr("Fax send error (Possible cause: No answer or dialtone)"), self.error_icon)
|
|
else:
|
|
self.addStatusMessage(self.__tr("Fax send error (%s)"%pml.DN_ERROR_STR.get(error_state, "Unknown error")), self.error_icon)
|
|
self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
|
|
|
|
elif status == fax.STATUS_ERROR_IN_CONNECTING:
|
|
self.addStatusMessage(self.__tr("Fax send error (Error in connecting)"), self.error_icon)
|
|
self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
|
|
|
|
elif status == fax.STATUS_ERROR_IN_TRANSMITTING:
|
|
self.addStatusMessage(self.__tr("Fax send error (Error in transmitting)"), self.error_icon)
|
|
self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
|
|
|
|
elif status == fax.STATUS_ERROR_PROBLEM_IN_FAXLINE:
|
|
self.addStatusMessage(self.__tr("Fax send error (Problem with the fax line)"), self.error_icon)
|
|
self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
|
|
|
|
elif status == fax.STATUS_JOB_CANCEL:
|
|
self.addStatusMessage(self.__tr("(Fax Job Cancelled)"), self.error_icon)
|
|
self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
|
|
|
|
elif status == fax.STATUS_BUSY:
|
|
#FailureUI(self, self.__tr("<b>Fax device is busy.</b><p>Please try again later."))
|
|
self.addStatusMessage(self.__tr("Fax is busy."), self.error_icon)
|
|
self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
|
|
|
|
elif status == fax.STATUS_COMPLETED:
|
|
self.addStatusMessage(self.__tr("Send fax job complete."), self.info_icon)
|
|
|
|
self.dev.sendEvent(EVENT_END_FAX_JOB, self.printer_name, 0, '')
|
|
|
|
|
|
def addStatusMessage(self, text, icon):
|
|
log.debug(text)
|
|
#self.StatusList.addItem(QListWidgetItem(icon, text, self.StatusList))
|
|
QListWidgetItem(icon, text, self.StatusList)
|
|
|
|
#
|
|
# CheckTimer and Fax Rendering
|
|
#
|
|
|
|
def FileTable_callback(self, f):
|
|
# Called by FileTable when user adds a file using "Add file..."
|
|
log.debug("FileTable_callback(%s)" % f)
|
|
self.renderFile(f)
|
|
|
|
|
|
def renderFile(self, f):
|
|
self.busy = True
|
|
beginWaitCursor()
|
|
try:
|
|
self.last_job_id = cups.printFile(self.printer_name, f, os.path.basename(f))
|
|
finally:
|
|
self.busy = False
|
|
endWaitCursor()
|
|
|
|
|
|
def CheckTimer_timeout(self):
|
|
if not self.busy:
|
|
#log.debug("Checking for incoming faxes...")
|
|
try:
|
|
device_uri, printer_name, event_code, username, job_id, title, timedate, fax_file = \
|
|
self.service.CheckForWaitingFax(self.device_uri, prop.username, self.last_job_id)
|
|
except Exception as e:
|
|
log.debug("Exception caught in CheckTimer_timeout: %s" % e)
|
|
fax_file = None
|
|
|
|
if fax_file:
|
|
self.last_job_id = 0
|
|
log.debug("A new fax has arrived: %s (%d)" % (fax_file, job_id))
|
|
self.addFileFromJob(fax_file, title)
|
|
|
|
|
|
def addFileFromJob(self, fax_file, title):
|
|
self.busy = True
|
|
#beginWaitCursor()
|
|
try:
|
|
ok, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding = \
|
|
self.getFileInfo(fax_file)
|
|
if ok:
|
|
self.FilesTable.addFile(fax_file, 'application/hplip-fax', 'HPLIP Fax', title, num_pages)
|
|
|
|
finally:
|
|
self.busy = False
|
|
endWaitCursor()
|
|
|
|
|
|
def getFileInfo(self, fax_file):
|
|
f = open(fax_file, 'rb')
|
|
header = f.read(fax.FILE_HEADER_SIZE)
|
|
f.close()
|
|
|
|
if len(header) != fax.FILE_HEADER_SIZE:
|
|
log.error("Invalid fax file! (truncated header or no data)")
|
|
return (False, 0, 0, 0, 0, 0, 0)
|
|
|
|
mg, version, num_pages, hort_dpi, vert_dpi, page_size, \
|
|
resolution, encoding, reserved1, reserved2 = \
|
|
struct.unpack(">8sBIHHBBBII", header[:fax.FILE_HEADER_SIZE])
|
|
|
|
log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
|
|
(mg, version, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
|
|
|
|
return (True, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding)
|
|
|
|
|
|
#
|
|
# Misc
|
|
#
|
|
|
|
def closeEvent(self, e):
|
|
if self.lock_file is not None:
|
|
utils.unlock(self.lock_file)
|
|
e.accept()
|
|
|
|
|
|
def displayPage(self, page):
|
|
self.updateStepText(page)
|
|
self.StackedWidget.setCurrentIndex(page)
|
|
|
|
|
|
# def CancelButton_clicked(self):
|
|
# self.close()
|
|
|
|
|
|
def BackButton_clicked(self):
|
|
p = self.StackedWidget.currentIndex()
|
|
if p == PAGE_SELECT_FAX:
|
|
log.error("Invalid!")
|
|
|
|
elif p == PAGE_COVERPAGE:
|
|
log.error("Invalid!")
|
|
|
|
elif p == PAGE_FILES:
|
|
self.StackedWidget.setCurrentIndex(PAGE_COVERPAGE)
|
|
self.displayCoverpagePage()
|
|
|
|
elif p == PAGE_RECIPIENTS:
|
|
self.StackedWidget.setCurrentIndex(PAGE_FILES)
|
|
self.displayFilesPage()
|
|
|
|
elif p == PAGE_SEND_FAX:
|
|
self.StackedWidget.setCurrentIndex(PAGE_RECIPIENTS)
|
|
self.displayRecipientsPage()
|
|
|
|
|
|
def NextButton_clicked(self):
|
|
p = self.StackedWidget.currentIndex()
|
|
if p == PAGE_SELECT_FAX:
|
|
self.StackedWidget.setCurrentIndex(PAGE_COVERPAGE)
|
|
self.displayCoverpagePage()
|
|
|
|
elif p == PAGE_COVERPAGE:
|
|
self.StackedWidget.setCurrentIndex(PAGE_FILES)
|
|
self.displayFilesPage()
|
|
|
|
elif p == PAGE_FILES:
|
|
self.StackedWidget.setCurrentIndex(PAGE_RECIPIENTS)
|
|
self.displayRecipientsPage()
|
|
|
|
elif p == PAGE_RECIPIENTS:
|
|
self.StackedWidget.setCurrentIndex(PAGE_SEND_FAX)
|
|
self.displaySendFaxPage()
|
|
|
|
elif p == PAGE_SEND_FAX:
|
|
self.executeSendFax()
|
|
|
|
|
|
def updateStepText(self, p):
|
|
self.StepText.setText(self.__tr("Step %s of %s"%(p+1,PAGE_MAX+1)))
|
|
|
|
|
|
def restoreNextButton(self):
|
|
self.NextButton.setText(self.__tr("Next >"))
|
|
|
|
|
|
def __tr(self,s,c = None):
|
|
return qApp.translate("SendFaxDialog",s.encode('utf-8'),c)
|
|
|
|
|