Tizen 2.1 base
[platform/upstream/hplip.git] / ui4 / sendfaxdialog.py
1 # -*- coding: utf-8 -*-
2 #
3 # (c) Copyright 2001-2009 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 # Authors: Don Welch
20 #
21
22 # StdLib
23 import operator
24 import struct
25 import Queue
26
27 # Local
28 from base.g import *
29 from base import device, utils, pml
30 from prnt import cups
31 from base.codes import *
32 from ui_utils import *
33
34 # Qt
35 from PyQt4.QtCore import *
36 from PyQt4.QtGui import *
37
38 # Ui
39 from sendfaxdialog_base import Ui_Dialog
40 from filetable import FileTable, FILETABLE_TYPE_FAX
41 from printernamecombobox import PrinterNameComboBox, PRINTERNAMECOMBOBOX_TYPE_FAX_ONLY
42 from printsettingsdialog import PrintSettingsDialog
43 from faxsetupdialog import FaxSetupDialog
44
45
46 PAGE_SELECT_FAX = 0
47 PAGE_COVERPAGE = 1
48 PAGE_FILES = 2
49 PAGE_RECIPIENTS = 3
50 PAGE_SEND_FAX = 4
51 PAGE_MAX = 4
52
53 STATUS_INFORMATION = 0
54 STATUS_WARNING = 1
55 STATUS_ERROR = 2
56
57 MIME_TYPE_COVERPAGE = "application/hplip-fax-coverpage"
58
59 fax_enabled = prop.fax_build
60
61 if fax_enabled:
62     try:
63         from fax import fax
64     except ImportError:
65         # This can fail on Python < 2.3 due to the datetime module
66         # or if fax was diabled during the build
67         fax_enabled = False
68
69 if not fax_enabled:
70     log.warn("Fax disabled.")
71
72 coverpages_enabled = False
73 if fax_enabled:
74     try:
75         import reportlab
76         ver = reportlab.Version
77         try:
78             ver_f = float(ver)
79         except ValueError:
80             ver_f = 0.0
81
82         if ver_f >= 2.0:
83             coverpages_enabled = True
84         else:
85             log.warn("Pre-2.0 version of Reportlab installed. Fax coverpages disabled.")
86
87     except ImportError:
88         log.warn("Reportlab not installed. Fax coverpages disabled.")
89
90 if not coverpages_enabled:
91     log.warn("Please install version 2.0+ of Reportlab for coverpage support.")
92
93 if fax_enabled:
94     from fabwindow import FABWindow
95
96 if coverpages_enabled:
97     from fax import coverpages
98
99
100 class SendFaxDialog(QDialog, Ui_Dialog):
101     def __init__(self, parent, printer_name, device_uri=None, args=None):
102         QDialog.__init__(self, parent)
103         self.setupUi(self)
104
105         self.printer_name = printer_name
106         if device_uri is not None:
107             self.device_uri = device_uri
108         else:
109             self.device_uri = device.getDeviceURIByPrinterName(self.printer_name)
110
111         self.args = args
112         self.dev = None
113
114         self.dbus_avail, self.service, session_bus = device.init_dbus()
115
116         self.CheckTimer = None
117         self.lock_file = None
118         self.file_list = []
119         self.recipient_list = []
120
121         self.initUi()
122
123         if self.printer_name:
124             if coverpages_enabled:
125                 QTimer.singleShot(0, self.displayCoverpagePage)
126             else:
127                 self.lockAndLoad()
128                 QTimer.singleShot(0, self.displayFilesPage)
129         else:
130             QTimer.singleShot(0, self.displaySelectFaxPage)
131
132
133     def initUi(self):
134         # connect signals/slots
135         self.connect(self.CancelButton, SIGNAL("clicked()"), self.CancelButton_clicked)
136         self.connect(self.BackButton, SIGNAL("clicked()"), self.BackButton_clicked)
137         self.connect(self.NextButton, SIGNAL("clicked()"), self.NextButton_clicked)
138
139         self.initSelectFaxPage()
140         self.initCoverpagePage()
141         self.initFilesPage()
142         self.initRecipientsPage()
143         self.initSendFaxPage()
144
145         # Application icon
146         self.setWindowIcon(QIcon(load_pixmap('hp_logo', '128x128')))
147
148
149     def lockAndLoad(self):
150         # Start up check timer here, since the fax name is now known
151         if self.CheckTimer is None:
152             self.CheckTimer = QTimer(self)
153             self.connect(self.CheckTimer, SIGNAL("timeout()"), self.CheckTimer_timeout)
154             self.CheckTimer.start(3000)
155
156         # Lock the app
157         if self.printer_name and self.lock_file is None:
158             ok, self.lock_file = utils.lock_app('hp-sendfax-%s' % self.printer_name, True)
159
160             if not ok:
161                 log.error("hp-sendfax is already running for fax %s" % self.printer_name)
162                 # TODO:
163
164     #
165     # Select Fax Page
166     #
167
168     def initSelectFaxPage(self):
169         self.FaxComboBox.setType(PRINTERNAMECOMBOBOX_TYPE_FAX_ONLY)
170         self.connect(self.FaxComboBox, SIGNAL("PrinterNameComboBox_currentChanged"), self.FaxComboBox_currentChanged)
171         self.connect(self.FaxComboBox, SIGNAL("PrinterNameComboBox_noPrinters"), self.FaxComboBox_noPrinters)
172         self.connect(self.FaxOptionsButton, SIGNAL("clicked()"), self.FaxOptionsButton_clicked)
173         self.connect(self.FaxSetupButton, SIGNAL("clicked()"), self.FaxSetupButton_clicked)
174
175         if self.printer_name is not None:
176             self.FaxComboBox.setInitialPrinter(self.printer_name)
177
178
179     def displaySelectFaxPage(self):
180         self.BackButton.setEnabled(False)
181         self.updateStepText(PAGE_SELECT_FAX)
182
183         if not fax_enabled:
184             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>"))
185             self.close()
186             return
187
188         if not self.dbus_avail:
189             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>"))
190             self.close()
191             return
192
193         self.FaxComboBox.updateUi()
194         self.displayPage(PAGE_SELECT_FAX)
195
196
197     def FaxComboBox_currentChanged(self, device_uri, printer_name):
198         self.printer_name = printer_name
199         self.device_uri = device_uri
200
201
202     def FaxComboBox_noPrinters(self):
203         FailureUI(self, self
204                   .__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>"))
205         self.close()
206
207
208     def FaxOptionsButton_clicked(self):
209         dlg = PrintSettingsDialog(self, self.printer_name, fax_mode=True)
210         dlg.exec_()
211
212
213     def FaxSetupButton_clicked(self):
214         dlg = FaxSetupDialog(self, self.device_uri)
215         dlg.exec_()
216
217     #
218     # Coverpage Page
219     #
220
221     def initCoverpagePage(self):
222         self.cover_page_message = ''
223         self.cover_page_re = ''
224         self.preserve_formatting = False
225         self.cover_page_func, cover_page_png = None, None
226         self.last_job_id = 0
227         self.busy = False
228         self.PrevCoverPageButton.setIcon(QIcon(load_pixmap("prev", "16x16")))
229         self.NextCoverPageButton.setIcon(QIcon(load_pixmap("next", "16x16")))
230
231         if coverpages_enabled:
232             self.cover_page_list = coverpages.COVERPAGES.keys()
233             self.cover_page_index = self.cover_page_list.index("basic")
234             self.cover_page_max = len(self.cover_page_list)-1
235             self.cover_page_name = self.cover_page_list[self.cover_page_index]
236
237             self.connect(self.PrevCoverPageButton, SIGNAL("clicked()"), self.PrevCoverPageButton_clicked)
238             self.connect(self.NextCoverPageButton, SIGNAL("clicked()"), self.NextCoverPageButton_clicked)
239             self.connect(self.CoverPageGroupBox, SIGNAL("toggled(bool)"), self.CoverPageGroupBox_toggled)
240             self.connect(self.MessageEdit, SIGNAL("textChanged()"), self.MessageEdit_textChanged)
241             self.connect(self.RegardingEdit, SIGNAL("textChanged(const QString &)"), self.RegardingEdit_textChanged)
242             self.connect(self.PreserveFormattingCheckBox, SIGNAL("toggled(bool)"),
243                         self.PreserveFormattingCheckBox_toggled)
244         else:
245             self.CoverPageGroupBox.setEnabled(False)
246
247
248     def displayCoverpagePage(self):
249         self.BackButton.setEnabled(False) # No going back once printer is chosen
250         self.NextButton.setEnabled(True)
251
252         self.lockAndLoad()
253
254         self.updateCoverpageButtons()
255         self.displayCoverpagePreview()
256         self.displayPage(PAGE_COVERPAGE)
257
258
259     def MessageEdit_textChanged(self):
260         self.cover_page_message = unicode(self.MessageEdit.toPlainText())
261
262
263     def RegardingEdit_textChanged(self, t):
264         self.cover_page_re = unicode(t)
265
266
267     def PreserveFormattingCheckBox_toggled(self, b):
268         self.preserve_formatting = b
269
270
271     def PrevCoverPageButton_clicked(self):
272         self.cover_page_index -= 1
273         if self.cover_page_index < 0:
274             self.cover_page_index = 0
275         else:
276             self.updateCoverpageButtons()
277             self.displayCoverpagePage()
278
279
280     def NextCoverPageButton_clicked(self):
281         self.cover_page_index += 1
282         if self.cover_page_index > self.cover_page_max:
283             self.cover_page_index = self.cover_page_max
284         else:
285             self.updateCoverpageButtons()
286             self.displayCoverpagePage()
287
288
289     def displayCoverpagePreview(self):
290         self.cover_page_name = self.cover_page_list[self.cover_page_index]
291         self.cover_page_func = coverpages.COVERPAGES[self.cover_page_name][0]
292         self.CoverPageName.setText(QString('<i>"%1"</i>').arg(self.cover_page_name))
293         self.CoverPagePreview.setPixmap(load_pixmap(coverpages.COVERPAGES[self.cover_page_name][1], 'other'))
294
295         if self.CoverPageGroupBox.isChecked():
296             self.addCoverPage()
297         else:
298             self.removeCoverPage()
299
300
301     def updateCoverpageButtons(self):
302         enabled = self.CoverPageGroupBox.isChecked()
303         self.PrevCoverPageButton.setEnabled(enabled and self.cover_page_index != 0)
304         self.NextCoverPageButton.setEnabled(enabled and self.cover_page_index != self.cover_page_max)
305
306
307     def CoverPageGroupBox_toggled(self, b):
308         self.updateCoverpageButtons()
309         if b:
310             self.addCoverPage()
311         else:
312             self.removeCoverPage()
313
314
315     def addCoverPage(self):
316         self.removeCoverPage()
317         self.FilesTable.addFile(self.cover_page_name, MIME_TYPE_COVERPAGE,
318                                 self.__tr('HP Fax Coverpage: "%1"').arg(self.cover_page_name),
319                                 self.__tr("Cover Page"), 1)
320
321
322     def removeCoverPage(self):
323         self.FilesTable.removeFileByMIMEType(MIME_TYPE_COVERPAGE)
324
325
326     def toggleCoverPage(self, b):
327         self.disconnect(self.CoverPageGroupBox, SIGNAL("toggled(bool)"), self.CoverPageGroupBox_toggled)
328         self.CoverPageGroupBox.setChecked(b)
329         self.connect(self.CoverPageGroupBox, SIGNAL("toggled(bool)"), self.CoverPageGroupBox_toggled)
330
331
332     #
333     # Files Page
334     #
335
336     def initFilesPage(self):
337         self.FilesTable.setType(FILETABLE_TYPE_FAX)
338         self.FilesTable.setFaxCallback(self.FileTable_callback)
339         self.connect(self.FilesTable, SIGNAL("isEmpty"), self.FilesTable_isEmpty)
340         self.connect(self.FilesTable, SIGNAL("isNotEmpty"), self.FilesTable_isNotEmpty)
341         self.connect(self.FilesTable, SIGNAL("fileListChanged"), self.FilesTable_fileListChanged)
342
343
344     def displayFilesPage(self):
345         self.FilesTable.updateUi(False)
346
347         if self.args is not None:
348             for a in self.args:
349                 f = os.path.abspath(os.path.expanduser(a))
350                 if os.path.exists(f) and os.access(f, os.R_OK):
351                     self.renderFile(f)
352
353             self.args = None
354
355         self.restoreNextButton()
356         self.NextButton.setEnabled(self.FilesTable.isNotEmpty())
357         self.BackButton.setEnabled(coverpages_enabled)
358         self.FilesPageNote.setText(self.__tr("Note: You may also add files to the fax by printing from any application to the '%1' fax printer.").arg(self.printer_name))
359         self.displayPage(PAGE_FILES)
360
361
362     def FilesTable_isEmpty(self):
363         if self.StackedWidget.currentIndex() == PAGE_FILES:
364             self.NextButton.setEnabled(False)
365
366
367     def FilesTable_isNotEmpty(self):
368         if self.StackedWidget.currentIndex() == PAGE_FILES:
369             self.NextButton.setEnabled(True)
370
371
372     def FilesTable_fileListChanged(self):
373         self.file_list = self.FilesTable.file_list
374         self.toggleCoverPage(self.FilesTable.isMIMETypeInList(MIME_TYPE_COVERPAGE))
375
376
377     #
378     # Recipients Page
379     #
380
381     def initRecipientsPage(self):
382         # setup validators
383         self.QuickAddFaxEdit.setValidator(PhoneNumValidator(self.QuickAddFaxEdit))
384
385         # Fax address book database
386         self.db = fax.FaxAddressBook()
387
388         # Fax address book window
389         self.fab = FABWindow(self)
390         self.fab.setWindowFlags(Qt.Tool) # Keeps the Fab window on top
391
392         self.connect(self.fab, SIGNAL("databaseChanged"), self.FABWindow_databaseChanged)
393
394         # connect signals
395         self.connect(self.QuickAddFaxEdit, SIGNAL("textChanged(const QString &)"),
396                     self.QuickAddFaxEdit_textChanged)
397         self.connect(self.QuickAddNameEdit, SIGNAL("textChanged(const QString &)"),
398                     self.QuickAddNameEdit_textChanged)
399         self.connect(self.QuickAddButton, SIGNAL("clicked()"), self.QuickAddButton_clicked)
400         self.connect(self.FABButton, SIGNAL("clicked()"), self.FABButton_clicked)
401         self.connect(self.AddIndividualButton, SIGNAL("clicked()"), self.AddIndividualButton_clicked)
402         self.connect(self.AddGroupButton, SIGNAL("clicked()"), self.AddGroupButton_clicked)
403         self.connect(self.RemoveRecipientButton, SIGNAL("clicked()"), self.RemoveRecipientButton_clicked)
404         self.connect(self.MoveRecipientUpButton, SIGNAL("clicked()"), self.MoveRecipientUpButton_clicked)
405         self.connect(self.MoveRecipientDownButton, SIGNAL("clicked()"), self.MoveRecipientDownButton_clicked)
406         self.connect(self.RecipientsTable, SIGNAL("itemSelectionChanged()"),
407                     self.RecipientsTable_itemSelectionChanged)
408         self.connect(self.RecipientsTable, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"),
409                     self.RecipientsTable_itemDoubleClicked)
410
411         # setup icons
412         self.FABButton.setIcon(QIcon(load_pixmap("fab", "16x16")))
413         self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user", "16x16")))
414         self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users", "16x16")))
415         self.RemoveRecipientButton.setIcon(QIcon(load_pixmap("remove_user", "16x16")))
416         self.MoveRecipientUpButton.setIcon(QIcon(load_pixmap("up_user", "16x16")))
417         self.MoveRecipientDownButton.setIcon(QIcon(load_pixmap("down_user", "16x16")))
418         self.QuickAddButton.setIcon(QIcon(load_pixmap("add_user_quick", "16x16")))
419
420         # setup initial state
421         self.QuickAddButton.setEnabled(False)
422
423         self.recipient_headers = [self.__tr("Name"), self.__tr("Fax number"), self.__tr("Notes")]
424
425
426     def FABWindow_databaseChanged(self, action, s1='', s2=''):
427         self.db.load()
428
429         if action in (FAB_NAME_ADD, FAB_GROUP_ADD, FAB_GROUP_RENAME,
430                       FAB_GROUP_REMOVE, FAB_GROUP_MEMBERSHIP_CHANGED):
431
432             log.debug("Fax address book has changed")
433             self.updateAddressBook()
434
435         elif action == FAB_NAME_REMOVE:
436             log.debug("Fax address book has changed: '%s' removed" % s1)
437             all_names = self.db.get_all_names()
438             self.recipient_list = filter(lambda x: x in self.recipient_list, all_names)
439             self.updateAddressBook()
440             self.updateRecipientTable()
441
442         elif action == FAB_NAME_RENAME:
443             log.debug("Fax address book has changed: '%s' renamed to '%s'" % (s1, s2))
444             for i, n in enumerate(self.recipient_list):
445                 if n == s1:
446                     self.recipient_list[i] = s2
447                     self.updateRecipientTable()
448                     break
449             else:
450                 self.updateAddressBook()
451
452         elif action == FAB_NAME_DETAILS_CHANGED:
453             log.debug("Fax address book has changed: '%s' details changed" % s1)
454             self.updateRecipientTable()
455
456
457     def displayRecipientsPage(self):
458         self.updateAddressBook()
459         self.updateRecipientTable()
460         self.enableQuickAddButton()
461         self.displayPage(PAGE_RECIPIENTS)
462         self.restoreNextButton()
463         self.BackButton.setEnabled(True)
464
465
466     def updateAddressBook(self):
467         names = [n for n in self.db.get_all_names() if not n.startswith('__')]
468         groups = self.db.get_all_groups()
469         self.AddIndividualComboBox.clear()
470         self.AddGroupComboBox.clear()
471
472         i = 0
473         names.sort()
474         for n in names:
475             if n not in self.recipient_list:
476                 data = self.db.get(n)
477                 if data['fax']:
478                     self.AddIndividualComboBox.addItem(n)
479                     i += 1
480
481         if i:
482             self.AddIndividualButton.setEnabled(True)
483             self.AddIndividualComboBox.setEnabled(True)
484             #self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user", "16x16")))
485
486         else:
487             self.AddIndividualButton.setEnabled(False)
488             self.AddIndividualComboBox.setEnabled(False)
489             #self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user-disabled", "16x16")))
490
491         i = 0
492         groups.sort()
493         for g in groups:
494             for n in self.db.group_members(g):
495                 if not n.startswith('__') and n not in self.recipient_list:
496                     self.AddGroupComboBox.addItem(g)
497                     i += 1
498                     break
499
500         if i:
501             self.AddGroupButton.setEnabled(True)
502             self.AddGroupComboBox.setEnabled(True)
503             #self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users", "16x16")))
504
505         else:
506             self.AddGroupButton.setEnabled(False)
507             self.AddGroupComboBox.setEnabled(False)
508             #self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users-disabled", "16x16")))
509
510
511     def updateRecipientTable(self):
512         try:
513             prev = self.getCurrentRecipient()
514         except (TypeError, AttributeError):
515             prev = None
516
517         self.RecipientsTable.clear()
518         self.RecipientsTable.setRowCount(0)
519         self.RecipientsTable.setColumnCount(0)
520
521         if self.recipient_list:
522             num_recipients = len(self.recipient_list)
523
524             self.RecipientsTable.setColumnCount(len(self.recipient_headers))
525             self.RecipientsTable.setHorizontalHeaderLabels(self.recipient_headers)
526             self.RecipientsTable.setRowCount(num_recipients)
527             flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
528
529             j = None
530             for row, n in enumerate(self.recipient_list):
531                 i = QTableWidgetItem(QString(n))
532                 i.setFlags(flags)
533                 self.RecipientsTable.setItem(row, 0, i)
534                 if prev is not None and n == prev:
535                     j = i
536
537                 k = self.db.get(n)
538                 if not k:
539                     continue
540
541                 i = QTableWidgetItem(QString(k['fax']))
542                 i.setFlags(flags)
543                 self.RecipientsTable.setItem(row, 1, i)
544
545                 i = QTableWidgetItem(QString(k['notes']))
546                 i.setFlags(flags)
547                 self.RecipientsTable.setItem(row, 2, i)
548
549             self.RecipientsTable.resizeColumnsToContents()
550             self.RecipientsTable.resizeRowsToContents()
551
552             if j is not None:
553                 self.RecipientsTable.setCurrentItem(j)
554             else:
555                 self.RecipientsTable.setCurrentItem(self.RecipientsTable.item(0, 0))
556
557             self.NextButton.setEnabled(True)
558
559         else:
560             self.enableRecipientListButtons()
561             self.NextButton.setEnabled(False)
562
563
564     def RecipientsTable_itemSelectionChanged(self):
565         current_row = self.RecipientsTable.currentRow()
566         num_recipients = len(self.recipient_list)
567         self.enableRecipientListButtons(num_recipients > 0,  # remove
568                                             num_recipients > 1 and current_row > 0, # up
569                                             num_recipients > 1 and current_row < (num_recipients-1)) # down
570
571
572     def enableRecipientListButtons(self, enable_remove=False, enable_up_move=False, enable_down_move=False):
573         if enable_remove:
574             self.RemoveRecipientButton.setEnabled(True)
575         else:
576             self.RemoveRecipientButton.setEnabled(False)
577
578         if enable_up_move:
579             self.MoveRecipientUpButton.setEnabled(True)
580         else:
581             self.MoveRecipientUpButton.setEnabled(False)
582
583         if enable_down_move:
584             self.MoveRecipientDownButton.setEnabled(True)
585         else:
586             self.MoveRecipientDownButton.setEnabled(False)
587
588
589     def QuickAddFaxEdit_textChanged(self, fax):
590         self.enableQuickAddButton(None, unicode(fax))
591
592
593     def QuickAddNameEdit_textChanged(self, name):
594         self.enableQuickAddButton(unicode(name))
595
596
597     def enableQuickAddButton(self, name=None, fax=None):
598         if name is None:
599             name = unicode(self.QuickAddNameEdit.text())
600         if fax is None:
601             fax = unicode(self.QuickAddFaxEdit.text())
602
603         existing_name = False
604         if name:
605             existing_name = name in self.db.get_all_names()
606
607         if existing_name:
608             try:
609                 self.QuickAddNameEdit.setStyleSheet("background-color: yellow")
610             except AttributeError:
611                 pass
612         else:
613             try:
614                 self.QuickAddNameEdit.setStyleSheet("")
615             except AttributeError:
616                 pass
617
618         if name and not existing_name and fax:
619             self.QuickAddButton.setEnabled(True)
620         else:
621             self.QuickAddButton.setEnabled(False)
622
623
624     def QuickAddButton_clicked(self):
625         name = unicode(self.QuickAddNameEdit.text())
626         fax = unicode(self.QuickAddFaxEdit.text())
627         self.fab.addName(name, fax)
628         self.addRecipient(name)
629         self.updateRecipientTable()
630         self.QuickAddNameEdit.clear()
631         self.QuickAddFaxEdit.clear()
632         self.enableQuickAddButton('', '')
633
634
635     def AddIndividualButton_clicked(self):
636         self.addRecipient(unicode(self.AddIndividualComboBox.currentText()))
637
638
639     def AddGroupButton_clicked(self):
640         self.addGroup(unicode(self.AddGroupComboBox.currentText()))
641
642
643     def RemoveRecipientButton_clicked(self):
644         name = self.getCurrentRecipient()
645         temp = self.recipient_list[:]
646         for i, n in enumerate(temp):
647             if name == n:
648                 del self.recipient_list[i]
649                 self.updateRecipientTable()
650                 self.updateAddressBook()
651                 break
652
653
654     def MoveRecipientUpButton_clicked(self):
655         utils.list_move_up(self.recipient_list, self.getCurrentRecipient())
656         self.updateRecipientTable()
657
658
659     def MoveRecipientDownButton_clicked(self):
660         utils.list_move_down(self.recipient_list, self.getCurrentRecipient())
661         self.updateRecipientTable()
662
663
664     def getCurrentRecipient(self):
665         item = self.RecipientsTable.item(self.RecipientsTable.currentRow(), 0)
666         if item is not None:
667             return unicode(item.text())
668         else:
669             return u''
670
671
672     def addRecipient(self, name, update=True):
673         if name not in self.recipient_list and not name.startswith('__'):
674             self.recipient_list.append(name)
675             if update:
676                 self.updateRecipientTable()
677                 self.updateAddressBook()
678
679
680     def addGroup(self, group):
681         for n in self.db.group_members(group):
682             self.addRecipient(n, False)
683
684         self.updateRecipientTable()
685         self.updateAddressBook()
686
687
688     def FABButton_clicked(self):
689         self.fab.show()
690
691
692     def RecipientsTable_itemDoubleClicked(self, item):
693         if item is not None:
694             row, col = item.row(), item.column()
695             if col != 0:
696                 item = self.RecipientsTable.item(row, 0)
697
698             self.fab.selectByName(unicode(item.text()))
699             self.fab.show()
700
701
702     #
703     # Send Fax Page
704     #
705
706     def initSendFaxPage(self):
707         self.info_icon = QIcon(load_pixmap("info", "16x16"))
708         self.warn_icon = QIcon(load_pixmap("warning", "16x16"))
709         self.error_icon = QIcon(load_pixmap("error", "16x16"))
710         self.busy_icon = QIcon(load_pixmap("busy", "16x16"))
711         self.update_queue = Queue.Queue() # UI updates from send thread
712         self.event_queue = Queue.Queue() # UI events (cancel) to send thread
713         self.send_fax_active = False
714
715
716     def displaySendFaxPage(self):
717         self.displayPage(PAGE_SEND_FAX)
718         self.addStatusMessage(self.__tr("Ready to send fax."), self.info_icon)
719         self.NextButton.setText(self.__tr("Send Fax"))
720
721
722
723     #
724     # Fax
725     #
726
727     def executeSendFax(self):
728         self.NextButton.setEnabled(False)
729         self.BackButton.setEnabled(False)
730         self.CheckTimer.stop()
731         self.busy = True
732         phone_num_list = []
733
734         ppd_file = cups.getPPD(self.printer_name)
735
736         if ppd_file is not None and os.path.exists(ppd_file):
737             if file(ppd_file, 'r').read().find('HP Fax') == -1:
738                 FailureUI(self, 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.printer_name))
739                 self.close()
740                 return
741
742         beginWaitCursor()
743
744         mq = device.queryModelByURI(self.device_uri)
745
746         self.dev = fax.getFaxDevice(self.device_uri,
747                                    self.printer_name, None,
748                                    mq['fax-type'])
749
750         try:
751             try:
752                 self.dev.open()
753             except Error, e:
754                 log.warn(e.msg)
755
756             try:
757                 self.dev.queryDevice(quick=True)
758             except Error, e:
759                 log.error("Query device error (%s)." % e.msg)
760                 self.dev.error_state = ERROR_STATE_ERROR
761
762         finally:
763             self.dev.close()
764             endWaitCursor()
765
766         if self.dev.error_state > ERROR_STATE_MAX_OK and \
767             self.dev.error_state not in (ERROR_STATE_LOW_SUPPLIES, ERROR_STATE_LOW_PAPER):
768
769             FailureUI(self, 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))
770             self.NextButton.setEnabled(True)
771             return
772
773         # Check to make sure queue in CUPS is idle
774         self.cups_printers = cups.getPrinters()
775         for p in self.cups_printers:
776             if p.name == self.printer_name:
777                 if p.state == cups.IPP_PRINTER_STATE_STOPPED:
778                     FailureUI(self, 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.printer_name))
779                     self.NextButton.setEnabled(False)
780                     return
781                 break
782
783         log.debug("Recipient list:")
784
785         for p in self.recipient_list:
786             entry = self.db.get(p)
787             phone_num_list.append(entry)
788             log.debug("Name=%s Number=%s" % (entry["name"], entry["fax"]))
789
790         log.debug("File list:")
791
792         for f in self.file_list:
793             log.debug(f)
794
795         self.dev.sendEvent(EVENT_START_FAX_JOB, self.printer_name, 0, '')
796
797         if not self.dev.sendFaxes(phone_num_list, self.file_list, self.cover_page_message,
798                                   self.cover_page_re, self.cover_page_func, self.preserve_formatting,
799                                   self.printer_name, self.update_queue, self.event_queue):
800
801             FailureUI(self, self.__tr("<b>Send fax is active.</b><p>Please wait for operation to complete."))
802             self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
803             self.busy = False
804             self.send_fax_active = False
805             #self.NextButton.setEnabled(False)
806             self.setCancelCloseButton()
807             return
808
809         self.send_fax_active = True
810         self.setCancelCloseButton()
811         self.SendFaxTimer = QTimer(self)
812         self.connect(self.SendFaxTimer, SIGNAL('timeout()'), self.SendFaxTimer_timeout)
813         self.SendFaxTimer.start(1000) # 1 sec UI updates
814
815
816     def setCancelCloseButton(self):
817         if self.send_fax_active:
818             self.CancelButton.setText(self.__tr("Cancel Send"))
819         else:
820             self.CancelButton.setText(self.__tr("Close"))
821
822
823     def CancelButton_clicked(self):
824         if self.send_fax_active:
825             self.addStatusMessage(self.__tr("Cancelling job..."), self.warn_icon)
826             self.event_queue.put((fax.EVENT_FAX_SEND_CANCELED, '', '', ''))
827             self.dev.sendEvent(EVENT_FAX_JOB_CANCELED, self.printer_name, 0, '')
828         else:
829             self.close()
830
831
832     def SendFaxTimer_timeout(self):
833         while self.update_queue.qsize():
834             try:
835                 status, page_num, arg = self.update_queue.get(0)
836             except Queue.Empty:
837                 break
838
839             if status == fax.STATUS_IDLE:
840                 self.busy = False
841                 self.send_fax_active = False
842                 self.setCancelCloseButton()
843                 self.SendFaxTimer.stop()
844
845             elif status == fax.STATUS_PROCESSING_FILES:
846                 self.addStatusMessage(self.__tr("Processing page %1...").arg(page_num), self.busy_icon)
847
848             elif status == fax.STATUS_SENDING_TO_RECIPIENT:
849                 self.addStatusMessage(self.__tr("Sending fax to %1...").arg(arg), self.busy_icon)
850
851             elif status == fax.STATUS_DIALING:
852                 self.addStatusMessage(self.__tr("Dialing %1...").arg(arg), self.busy_icon)
853
854             elif status == fax.STATUS_CONNECTING:
855                 self.addStatusMessage(self.__tr("Connecting to %1...").arg(arg), self.busy_icon)
856
857             elif status == fax.STATUS_SENDING:
858                 self.addStatusMessage(self.__tr("Sending page %1 to %2...").arg(page_num).arg(arg),
859                                       self.busy_icon)
860
861             elif status == fax.STATUS_CLEANUP:
862                 self.addStatusMessage(self.__tr("Cleaning up..."), self.busy_icon)
863
864             elif status in (fax.STATUS_ERROR, fax.STATUS_BUSY, fax.STATUS_COMPLETED, fax.STATUS_ERROR_IN_CONNECTING, 
865                 fax.STATUS_ERROR_IN_TRANSMITTING, fax.STATUS_ERROR_PROBLEM_IN_FAXLINE, fax.STATUS_JOB_CANCEL ):
866                 self.busy = False
867                 self.send_fax_active = False
868                 self.setCancelCloseButton()
869                 self.SendFaxTimer.stop()
870
871                 if status == fax.STATUS_ERROR:
872                     result_code, error_state = self.dev.getPML(pml.OID_FAX_DOWNLOAD_ERROR)
873                     #FailureUI(self, self.__tr("<b>Fax send error (%s).</b><p>" % pml.DN_ERROR_STR.get(error_state, "Unknown error")))
874                     if error_state == pml.DN_ERROR_NONE:
875                         self.addStatusMessage(self.__tr("Fax send error (Possible cause: No answer or dialtone)"), self.error_icon)
876                     else:
877                         self.addStatusMessage(self.__tr("Fax send error (%1)").arg(pml.DN_ERROR_STR.get(error_state, "Unknown error")), self.error_icon)
878                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
879
880                 elif status == fax.STATUS_ERROR_IN_CONNECTING:
881                     self.addStatusMessage(self.__tr("Fax send error (Error in connecting)"), self.error_icon)
882                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
883
884                 elif status == fax.STATUS_ERROR_IN_TRANSMITTING:
885                     self.addStatusMessage(self.__tr("Fax send error (Error in transmitting)"), self.error_icon)
886                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
887
888                 elif status == fax.STATUS_ERROR_PROBLEM_IN_FAXLINE:
889                     self.addStatusMessage(self.__tr("Fax send error (Problem with the fax line)"), self.error_icon)
890                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
891
892                 elif status == fax.STATUS_JOB_CANCEL:
893                     self.addStatusMessage(self.__tr("(Fax Job Cancelled)"), self.error_icon)
894                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')  
895
896                 elif status == fax.STATUS_BUSY:
897                     #FailureUI(self, self.__tr("<b>Fax device is busy.</b><p>Please try again later."))
898                     self.addStatusMessage(self.__tr("Fax is busy."), self.error_icon)
899                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
900
901                 elif status == fax.STATUS_COMPLETED:
902                     self.addStatusMessage(self.__tr("Send fax job complete."), self.info_icon)
903
904                     self.dev.sendEvent(EVENT_END_FAX_JOB, self.printer_name, 0, '')
905
906
907     def addStatusMessage(self, text, icon):
908         log.debug(text)
909         #self.StatusList.addItem(QListWidgetItem(icon, text, self.StatusList))
910         QListWidgetItem(icon, text, self.StatusList)
911
912     #
913     # CheckTimer and Fax Rendering
914     #
915
916     def FileTable_callback(self, f):
917         # Called by FileTable when user adds a file using "Add file..."
918         log.debug("FileTable_callback(%s)" % f)
919         self.renderFile(f)
920
921
922     def renderFile(self, f):
923         self.busy = True
924         beginWaitCursor()
925         try:
926             self.last_job_id = cups.printFile(self.printer_name, f, os.path.basename(f))
927         finally:
928             self.busy = False
929             endWaitCursor()
930
931
932     def CheckTimer_timeout(self):
933         if not self.busy:
934             #log.debug("Checking for incoming faxes...")
935             device_uri, printer_name, event_code, username, job_id, title, timedate, fax_file = \
936                 self.service.CheckForWaitingFax(self.device_uri, prop.username, self.last_job_id)
937
938             if fax_file:
939                 self.last_job_id = 0
940                 log.debug("A new fax has arrived: %s (%d)" % (fax_file, job_id))
941                 self.addFileFromJob(fax_file, title)
942
943
944     def addFileFromJob(self, fax_file, title):
945         self.busy = True
946         #beginWaitCursor()
947         try:
948             ok, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding = \
949                 self.getFileInfo(fax_file)
950             if ok:
951                 self.FilesTable.addFile(fax_file, 'application/hplip-fax', 'HPLIP Fax', title, num_pages)
952
953         finally:
954             self.busy = False
955             endWaitCursor()
956
957
958     def getFileInfo(self, fax_file):
959         f = file(fax_file, 'r')
960         header = f.read(fax.FILE_HEADER_SIZE)
961         f.close()
962
963         if len(header) != fax.FILE_HEADER_SIZE:
964             log.error("Invalid fax file! (truncated header or no data)")
965             return (False, 0, 0, 0, 0, 0, 0)
966
967         mg, version, num_pages, hort_dpi, vert_dpi, page_size, \
968             resolution, encoding, reserved1, reserved2 = \
969             struct.unpack(">8sBIHHBBBII", header[:fax.FILE_HEADER_SIZE])
970
971         log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
972                   (mg, version, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
973
974         return (True, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding)
975
976
977     #
978     # Misc
979     #
980
981     def closeEvent(self, e):
982         if self.lock_file is not None:
983             utils.unlock(self.lock_file)
984         e.accept()
985
986
987     def displayPage(self, page):
988         self.updateStepText(page)
989         self.StackedWidget.setCurrentIndex(page)
990
991
992 #    def CancelButton_clicked(self):
993 #        self.close()
994
995
996     def BackButton_clicked(self):
997         p = self.StackedWidget.currentIndex()
998         if p == PAGE_SELECT_FAX:
999             log.error("Invalid!")
1000
1001         elif p == PAGE_COVERPAGE:
1002             log.error("Invalid!")
1003
1004         elif p == PAGE_FILES:
1005             self.StackedWidget.setCurrentIndex(PAGE_COVERPAGE)
1006             self.displayCoverpagePage()
1007
1008         elif p == PAGE_RECIPIENTS:
1009             self.StackedWidget.setCurrentIndex(PAGE_FILES)
1010             self.displayFilesPage()
1011
1012         elif p == PAGE_SEND_FAX:
1013             self.StackedWidget.setCurrentIndex(PAGE_RECIPIENTS)
1014             self.displayRecipientsPage()
1015
1016
1017     def NextButton_clicked(self):
1018         p = self.StackedWidget.currentIndex()
1019         if p == PAGE_SELECT_FAX:
1020             self.StackedWidget.setCurrentIndex(PAGE_COVERPAGE)
1021             self.displayCoverpagePage()
1022
1023         elif p == PAGE_COVERPAGE:
1024             self.StackedWidget.setCurrentIndex(PAGE_FILES)
1025             self.displayFilesPage()
1026
1027         elif p == PAGE_FILES:
1028             self.StackedWidget.setCurrentIndex(PAGE_RECIPIENTS)
1029             self.displayRecipientsPage()
1030
1031         elif p == PAGE_RECIPIENTS:
1032             self.StackedWidget.setCurrentIndex(PAGE_SEND_FAX)
1033             self.displaySendFaxPage()
1034
1035         elif p == PAGE_SEND_FAX:
1036             self.executeSendFax()
1037
1038
1039     def updateStepText(self, p):
1040         self.StepText.setText(self.__tr("Step %1 of %2").arg(p+1).arg(PAGE_MAX+1))
1041
1042
1043     def restoreNextButton(self):
1044         self.NextButton.setText(self.__tr("Next >"))
1045
1046
1047     def __tr(self,s,c = None):
1048         return qApp.translate("SendFaxDialog",s,c)
1049
1050