1# -*- coding: utf-8 -*-
2#
3# (c) Copyright 2001-2015 HP 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, Pete Parks, Naga Samrat Chowdary Narla,
20#
21
22
23
24# Std Lib
25import sys
26import time
27import os
28import gzip
29import select
30import struct
31import threading
32import signal
33
34# Local
35from base.sixext.moves import queue
36from base.g import *
37from base import device, utils, pml, maint, pkit, os_utils
38from base.sixext import  to_unicode
39from prnt import cups
40from base.codes import *
41from .ui_utils import load_pixmap
42from installer.core_install import *
43
44# Qt
45from qt import *
46
47# Main form
48from .devmgr4_base import DevMgr4_base
49
50# Scrollviews
51from .scrollview import ScrollView
52from .scrollprintsettings import ScrollPrintSettingsView
53
54# Alignment and ColorCal forms
55from .alignform import AlignForm
56from .aligntype6form1 import AlignType6Form1
57from .aligntype6form2 import AlignType6Form2
58from .paperedgealignform import PaperEdgeAlignForm
59from .colorcalform import ColorCalForm # Type 1 color cal
60from .coloradjform import ColorAdjForm  # Type 5 and 6 color adj
61from .colorcalform2 import ColorCalForm2 # Type 2 color cal
62from .colorcal4form import ColorCal4Form # Type 4 color cal
63from .align10form import Align10Form # Type 10 and 11 alignment
64from .align13form import Align13Form # Type 13 alignment
65
66# Misc forms
67from .loadpaperform import LoadPaperForm
68from .settingsdialog import SettingsDialog
69from .aboutdlg import AboutDlg
70from .cleaningform import CleaningForm
71from .cleaningform2 import CleaningForm2
72from .waitform import WaitForm
73from .faxsettingsform import FaxSettingsForm
74from .nodevicesform import NoDevicesForm
75from .settingsdialog import SettingsDialog
76from .firmwaredialog import FirmwareDialog
77
78# all in seconds
79MIN_AUTO_REFRESH_RATE = 5
80MAX_AUTO_REFRESH_RATE = 60
81DEF_AUTO_REFRESH_RATE = 30
82
83
84devices = {}    # { Device_URI : device.Device(), ... }
85devices_lock = threading.RLock()
86
87RESPONSE_START = 1
88RESPONSE_DONE = 2
89
90# ***********************************************************************************
91#
92# LISTVIEW/UTILITY UI CLASSES
93#
94# ***********************************************************************************
95
96class IconViewToolTip(QToolTip):
97    def __init__(self, parent, tooltip_text):
98        QToolTip.__init__(self, parent.viewport())
99        self.parent = parent
100
101
102    def maybeTip(self, pos):
103        abs_coords = QPoint(pos.x() + self.parent.contentsX(),
104            pos.y() + self.parent.contentsY())
105
106        item = self.parent.findItem(abs_coords)
107
108        if item is not None and item.tooltip_text:
109            rel_coords = QRect()
110            rel_coords.setX(pos.x())
111            rel_coords.setY(pos.y())
112            i = item.rect()
113            rel_coords.setWidth(i.width())
114            rel_coords.setHeight(i.height())
115            self.tip(rel_coords, item.tooltip_text)
116
117
118
119class FuncViewItem(QIconViewItem):
120    def __init__(self, parent, text, pixmap, tooltip_text, cmd):
121        QIconViewItem.__init__(self, parent, text, pixmap)
122        self.tooltip_text = tooltip_text
123        self.cmd = cmd
124
125        self.tooltip = IconViewToolTip(parent, tooltip_text)
126
127
128
129class DeviceViewItem(QIconViewItem):
130    def __init__(self, parent, text, pixmap, device_uri, is_avail=True):
131        QIconViewItem.__init__(self, parent, text, pixmap)
132        self.device_uri = device_uri
133        self.is_avail = is_avail
134
135
136
137class SuppliesListViewItem(QListViewItem):
138    def __init__(self, parent, pixmap, desc, part_no, level_pixmap, status):
139        QListViewItem.__init__(self, parent, '', desc, part_no, '', status)
140        if pixmap is not None:
141            self.setPixmap(0, pixmap)
142        if level_pixmap is not None:
143            self.setPixmap(3, level_pixmap)
144
145    def paintCell(self, p, cg, c, w, a):
146        color = QColorGroup(cg)
147        pos = self.listView().itemPos(self)
148        h = self.totalHeight()
149
150        if (pos/h) % 2:
151            color.setColor(QColorGroup.Base,  QColor(220, 228, 249))
152
153        QListViewItem.paintCell(self, p, color, c, w, a)
154
155
156
157class PasswordDialog(QDialog):
158    def __init__(self,prompt, parent=None, name=None, modal=0, fl=0):
159        QDialog.__init__(self,parent,name,modal,fl)
160        self.prompt = prompt
161
162        if not name:
163            self.setName("PasswordDialog")
164
165        passwordDlg_baseLayout = QGridLayout(self,1,1,11,6,"passwordDlg_baseLayout")
166
167        self.promptTextLabel = QLabel(self,"promptTextLabel")
168        passwordDlg_baseLayout.addMultiCellWidget(self.promptTextLabel,0,0,0,1)
169
170        self.usernameTextLabel = QLabel(self,"usernameTextLabel")
171        passwordDlg_baseLayout.addMultiCellWidget(self.usernameTextLabel,1,1,0,1)
172
173        self.usernameLineEdit = QLineEdit(self,"usernameLineEdit")
174        self.usernameLineEdit.setEchoMode(QLineEdit.Normal)
175        passwordDlg_baseLayout.addMultiCellWidget(self.usernameLineEdit,1,1,1,2)
176
177        self.passwordTextLabel = QLabel(self,"passwordTextLabel")
178        passwordDlg_baseLayout.addMultiCellWidget(self.passwordTextLabel,2,2,0,1)
179
180        self.passwordLineEdit = QLineEdit(self,"passwordLineEdit")
181        self.passwordLineEdit.setEchoMode(QLineEdit.Password)
182        passwordDlg_baseLayout.addMultiCellWidget(self.passwordLineEdit,2,2,1,2)
183
184        self.okPushButton = QPushButton(self,"okPushButton")
185        passwordDlg_baseLayout.addWidget(self.okPushButton,3,2)
186
187        self.languageChange()
188
189        self.resize(QSize(420,163).expandedTo(self.minimumSizeHint()))
190        self.clearWState(Qt.WState_Polished)
191
192        self.connect(self.okPushButton,SIGNAL("clicked()"),self.accept)
193        self.connect(self.passwordLineEdit,SIGNAL("returnPressed()"),self.accept)
194    def getUsername(self):
195        return to_unicode(self.usernameLineEdit.text())
196
197    def getPassword(self):
198        return to_unicode(self.passwordLineEdit.text())
199
200    def languageChange(self):
201        self.setCaption(self.__tr("HP Device Manager - Enter Username/Password"))
202        self.promptTextLabel.setText(self.__tr(self.prompt))
203        self.usernameTextLabel.setText(self.__tr("Username"))
204        self.passwordTextLabel.setText(self.__tr("Password"))
205        self.okPushButton.setText(self.__tr("OK"))
206
207    def __tr(self,s,c = None):
208        return qApp.translate("PasswordDialog",s,c)
209
210
211
212class ScrollDialog(QDialog):
213    def __init__(self, scrollview_cls, cur_device, cur_printer, service,
214        parent = None, name=None, modal=0, fl=0):
215
216        QDialog.__init__(self,parent,name,modal,fl)
217
218        if not name:
219            self.setName("ScrollDialog")
220
221        self.setSizeGripEnabled(1)
222        ScrollDialogLayout = QGridLayout(self,1,1,11,6,"ScrollDialogLayout")
223        Layout1 = QHBoxLayout(None,0,6,"Layout1")
224        Horizontal_Spacing2 = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
225        Layout1.addItem(Horizontal_Spacing2)
226        self.buttonOk = QPushButton(self,"buttonOk")
227        self.buttonOk.setAutoDefault(1)
228        self.buttonOk.setDefault(1)
229        Layout1.addWidget(self.buttonOk)
230        ScrollDialogLayout.addLayout(Layout1,1,0)
231
232        self.scrollview = scrollview_cls(service, self)
233        ScrollDialogLayout.addWidget(self.scrollview,0,0)
234
235        self.scrollview.onDeviceChange(cur_device)
236        self.scrollview.onPrinterChange(cur_printer)
237        self.languageChange()
238
239        self.resize(QSize(520,457).expandedTo(self.minimumSizeHint()))
240        self.clearWState(Qt.WState_Polished)
241        self.connect(self.buttonOk,SIGNAL("clicked()"),self.accept)
242
243
244    def languageChange(self):
245        self.setCaption(self.__tr("HP Device Manager"))
246        self.buttonOk.setText(self.__tr("Close"))
247        self.buttonOk.setAccel(QKeySequence(QString.null))
248
249    def __tr(self,s,c = None):
250        return qApp.translate("ScrollDialog",s,c)
251
252
253def showPasswordUI(prompt):
254    try:
255        dlg = PasswordDialog(prompt, None)
256
257        if dlg.exec_loop() == QDialog.Accepted:
258            return (dlg.getUsername(), dlg.getPassword())
259
260    finally:
261        pass
262
263    return ("", "")
264
265
266class StatusListViewItem(QListViewItem):
267    def __init__(self, parent, pixmap, ess, tt, event_code, job_id, username):
268        QListViewItem.__init__(self, parent, '', ess, tt, event_code, job_id, username)
269        self.setPixmap(0, pixmap)
270
271    def paintCell(self, p, cg, c, w, a):
272        color = QColorGroup(cg)
273        pos = self.listView().itemPos(self)
274        h = self.totalHeight()
275        row = pos/2
276
277        if row % 2:
278            color.setColor(QColorGroup.Base,  QColor(220, 228, 249))
279
280        QListViewItem.paintCell(self, p, color, c, w, a)
281
282
283
284class JobListViewItem(QCheckListItem):
285    def __init__(self, parent, pixmap, desc, status, job_id):
286        QCheckListItem.__init__(self, parent, '', QCheckListItem.CheckBox)
287        self.job_id = job_id
288        self.setPixmap(1, pixmap)
289        self.setText(2, desc)
290        self.setText(3, status)
291        self.setText(4, job_id)
292
293    def paintCell(self, p, cg, c, w, a):
294        color = QColorGroup(cg)
295        pos = self.listView().itemPos(self)
296        h = self.totalHeight()
297
298        if (pos/h) % 2:
299            color.setColor(QColorGroup.Base,  QColor(220, 228, 249))
300
301        QCheckListItem.paintCell(self, p, color, c, w, a)
302
303
304
305class JobInfoDialog(QDialog):
306    def __init__(self, text, parent=None, name=None, modal=0, fl=0):
307        QDialog.__init__(self, parent, name, modal, fl)
308
309        if not name:
310            self.setName("JobInfoDialog")
311
312        Form1Layout = QGridLayout(self,1,1,11,6,"Form1Layout")
313        spacer6 = QSpacerItem(371,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
314        Form1Layout.addItem(spacer6,1,0)
315        self.pushButton4 = QPushButton(self,"pushButton4")
316        Form1Layout.addWidget(self.pushButton4,1,1)
317        self.textEdit = QTextEdit(self,"textEdit")
318        Form1Layout.addMultiCellWidget(self.textEdit,0,0,0,1)
319
320        self.languageChange()
321
322        self.resize(QSize(571,542).expandedTo(self.minimumSizeHint()))
323        self.clearWState(Qt.WState_Polished)
324
325        self.connect(self.pushButton4,SIGNAL("clicked()"),self.close)
326
327        self.textEdit.setText(text)
328
329
330    def languageChange(self):
331        self.setCaption(self.__tr("HP Device Manager - Job Log"))
332        self.pushButton4.setText(self.__tr("Close"))
333
334
335    def __tr(self,s,c = None):
336        return qApp.translate("JobInfoDialog",s,c)
337
338
339# ***********************************************************************************
340#
341# DEVICE UPDATE THREAD
342#
343# ***********************************************************************************
344
345
346class UpdateThread(QThread):
347    def __init__(self, response_queue=None, request_queue=None):
348        self.response_queue = response_queue # update queue -> main window
349        self.request_queue = request_queue # main window -> update queue
350
351        QThread.__init__(self)
352
353    def run(self):
354        while True:
355            dev = self.request_queue.get(True)
356
357            if dev is None:
358                log.debug("Update thread: exit")
359                break
360
361            log.debug("Update thread start: %s" % dev.device_uri)
362
363            try:
364                #print "THREAD LOCK ACQUIRE"
365                devices_lock.acquire()
366                #print "THREAD LOCK ACQUIRE - OK"
367                self.response_queue.put((RESPONSE_START, dev.device_uri))
368                log.debug(log.bold("Update: %s %s %s" % ("*"*20, dev.device_uri, "*"*20)))
369
370                if dev.supported:
371                    try:
372                        dev.open()
373                    except Error as e:
374                        log.warn(e.msg)
375
376                    time.sleep(0.1)
377
378                    if dev.device_state == DEVICE_STATE_NOT_FOUND:
379                        dev.error_state = ERROR_STATE_ERROR
380                    else:
381                        try:
382                            dev.queryDevice()
383
384                        except Error as e:
385                            log.error("Query device error (%s)." % e.msg)
386                            dev.error_state = ERROR_STATE_ERROR
387
388            finally:
389                dev.close()
390                #print "THREAD LOCK RELEASE"
391                cups.releaseCupsInstance()
392                devices_lock.release()
393
394            log.debug("Device state = %d" % dev.device_state)
395            log.debug("Status code = %d" % dev.status_code)
396            log.debug("Error state = %d" % dev.error_state)
397
398            log.debug("Update thread end: %s" % dev.device_uri)
399
400            self.response_queue.put((RESPONSE_DONE, dev.device_uri))
401
402
403# ***********************************************************************************
404#
405# MAINWINDOW
406#
407# ***********************************************************************************
408
409class DevMgr4(DevMgr4_base):
410    def __init__(self, read_pipe=None, toolbox_version='0.0',
411                 initial_device_uri=None, disable_dbus=False,
412                 parent=None, name=None, fl = 0):
413
414
415        # Distro insformation
416        core =  CoreInstall(MODE_CHECK)
417#        core.init()
418        self.Is_autoInstaller_distro = core.is_auto_installer_support()
419        self.Latest_ver= user_conf.get('upgrade', 'latest_available_version')
420        installed_version=sys_conf.get('hplip','version')
421        if utils.Is_HPLIP_older_version(installed_version, self.Latest_ver):
422            DevMgr4_base.__init__(self, parent, name, fl,self.Latest_ver,self.Is_autoInstaller_distro)
423        else:
424            self.Latest_ver = ""
425            DevMgr4_base.__init__(self, parent, name, fl,self.Latest_ver,self.Is_autoInstaller_distro)
426        log.debug("Initializing toolbox UI (Qt3)...")
427        log.debug("HPLIP Version: %s" % prop.installed_version)
428
429        self.disable_dbus = disable_dbus
430        self.toolbox_version = toolbox_version
431        self.cur_device_uri = user_conf.get('last_used', 'device_uri')
432        self.device_vars = {}
433        self.num_devices = 0
434        self.cur_device = None
435        self.rescanning = False
436        self.initial_device_uri = initial_device_uri
437
438        # dbus setup
439        if not self.disable_dbus:
440            self.dbus_avail, self.service, session_bus = device.init_dbus()
441
442            if not self.dbus_avail:
443                self.FailureUI("<b>Error</b><p>hp-systray must be running to get device status. hp-systray requires dbus support. Device status will not be available.")
444            else:
445                log.debug("dbus enabled")
446
447        else:
448            log.debug("dbus disabled")
449            self.dbus_avail, self.service = False, None
450
451
452        # Update thread setup
453        self.request_queue = queue.Queue()
454        self.response_queue = queue.Queue()
455        self.update_thread = UpdateThread(self.response_queue, self.request_queue)
456        self.update_thread.start()
457
458        # Pipe from toolbox/dbus setup
459        self.fmt = "80s80sI32sI80sf"
460        self.fmt_size = struct.calcsize(self.fmt)
461
462        if read_pipe is not None and not disable_dbus:
463            log.debug("Setting up read_pipe")
464            self.notifier = QSocketNotifier(read_pipe, QSocketNotifier.Read)
465            QObject.connect(self.notifier, SIGNAL("activated(int)"), self.notifier_activated)
466
467        # Application icon
468        self.setIcon(load_pixmap('hp_logo', '128x128'))
469
470        # User settings
471        self.user_settings = utils.UserSettings()
472        self.cmd_fab = self.user_settings.cmd_fab
473        log.debug("FAB command: %s" % self.cmd_fab)
474
475        if not self.user_settings.auto_refresh:
476            self.autoRefresh.toggle()
477
478        # Other initialization
479        self.InitPixmaps()
480        self.InitMisc()
481        self.InitUI()
482
483        cups.setPasswordCallback(showPasswordUI)
484
485        if not prop.doc_build:
486            self.helpContentsAction.setEnabled(False)
487
488        self.allow_auto_refresh = True
489        QTimer.singleShot(0, self.InitialUpdate)
490
491
492    # ***********************************************************************************
493    #
494    # INIT
495    #
496    # ***********************************************************************************
497
498    def InitPixmaps(self):
499        self.func_icons_cached = False
500        self.func_icons = {}
501        self.device_icons = {}
502
503        # TODO: Use Qt pixmap cache for all pixmaps?
504
505        # Device icon list overlays
506        self.warning_pix = load_pixmap('warning', '16x16')
507        self.error_pix = load_pixmap('error', '16x16')
508        self.ok_pix = load_pixmap('ok', '16x16')
509        self.lowink_pix = load_pixmap('inkdrop', '16x16')
510        self.lowtoner_pix = load_pixmap('toner', '16x16')
511        self.busy_pix = load_pixmap('busy', '16x16')
512        self.lowpaper_pix = load_pixmap('paper', '16x16')
513        self.refresh_pix = load_pixmap('refresh', '16x16')
514        self.refresh1_pix = load_pixmap('refresh1', '16x16')
515        self.fax_icon = load_pixmap('fax2', 'other')
516        self.idle_pix = load_pixmap('idle', '16x16')
517        self.scan_pix = load_pixmap("scan", '16x16')
518        self.print_pix = load_pixmap("print", '16x16')
519        self.sendfax_pix =load_pixmap("fax", '16x16')
520        self.pcard_pix = load_pixmap("pcard", '16x16')
521        self.makecopies_pix = load_pixmap("makecopies", '16x16')
522        self.help_pix = load_pixmap("help", '16x16')
523
524
525        # pixmaps: (inkjet, laserjet)
526        self.SMALL_ICONS = { ERROR_STATE_CLEAR : (None, None),
527            ERROR_STATE_BUSY : (self.busy_pix, self.busy_pix),
528            ERROR_STATE_ERROR : (self.error_pix, self.error_pix),
529            ERROR_STATE_LOW_SUPPLIES : (self.lowink_pix, self.lowtoner_pix),
530            ERROR_STATE_OK : (self.ok_pix, self.ok_pix),
531            ERROR_STATE_WARNING : (self.warning_pix, self.warning_pix),
532            ERROR_STATE_LOW_PAPER: (self.lowpaper_pix, self.lowpaper_pix),
533            ERROR_STATE_PRINTING : (self.busy_pix, self.busy_pix),
534            ERROR_STATE_SCANNING : (self.busy_pix, self.busy_pix),
535            ERROR_STATE_PHOTOCARD : (self.busy_pix, self.busy_pix),
536            ERROR_STATE_FAXING : (self.busy_pix, self.busy_pix),
537            ERROR_STATE_COPYING : (self.busy_pix, self.busy_pix),
538            ERROR_STATE_REFRESHING : (self.refresh1_pix, self.refresh1_pix),
539        }
540
541        self.STATUS_ICONS = { ERROR_STATE_CLEAR : (self.idle_pix, self.idle_pix),
542              ERROR_STATE_BUSY : (self.busy_pix, self.busy_pix),
543              ERROR_STATE_ERROR : (self.error_pix, self.error_pix),
544              ERROR_STATE_LOW_SUPPLIES : (self.lowink_pix, self.lowtoner_pix),
545              ERROR_STATE_OK : (self.ok_pix, self.ok_pix),
546              ERROR_STATE_WARNING : (self.warning_pix, self.warning_pix),
547              ERROR_STATE_LOW_PAPER: (self.lowpaper_pix, self.lowpaper_pix),
548              ERROR_STATE_PRINTING : (self.print_pix, self.print_pix),
549              ERROR_STATE_SCANNING : (self.scan_pix, self.scan_pix),
550              ERROR_STATE_PHOTOCARD : (self.pcard_pix, self.print_pix),
551              ERROR_STATE_FAXING : (self.sendfax_pix, self.sendfax_pix),
552              ERROR_STATE_COPYING :  (self.makecopies_pix, self.makecopies_pix),
553            }
554
555
556
557    def InitUI(self):
558        # Setup device icon list
559        self.DeviceList.setAutoArrange(True)
560        self.DeviceList.setSorting(True)
561
562        # Setup main menu
563        self.deviceRescanAction.setIconSet(QIconSet(self.refresh1_pix))
564        self.deviceRefreshAll.setIconSet(QIconSet(self.refresh_pix))
565        self.deviceInstallAction.setIconSet(QIconSet(load_pixmap('list_add', '16x16')))
566        self.deviceRemoveAction.setIconSet(QIconSet(load_pixmap('list_remove', '16x16')))
567        self.settingsConfigure.setIconSet(QIconSet(load_pixmap('settings', '16x16')))
568        self.helpContentsAction.setIconSet(QIconSet(self.help_pix))
569
570        # Setup toolbar
571        self.deviceRescanAction.addTo(self.Toolbar)
572        self.deviceRefreshAll.addTo(self.Toolbar)
573        self.Toolbar.addSeparator()
574        self.deviceInstallAction.addTo(self.Toolbar)
575        self.deviceRemoveAction.addTo(self.Toolbar)
576        self.Toolbar.addSeparator()
577        self.settingsConfigure.addTo(self.Toolbar)
578        self.helpContentsAction.addTo(self.Toolbar)
579
580        # Init tabs/controls
581        self.InitFuncsTab()
582        self.InitStatusTab()
583        self.InitSuppliesTab()
584        self.InitPrintSettingsTab()
585        self.InitPrintControlTab()
586
587        # Resize the splitter so that the device list starts as a single column
588        self.splitter2.setSizes([120, 700])
589        signal.signal(signal.SIGINT, signal.SIG_IGN)
590
591
592    def InitMisc(self):
593        self.unit_names = { "year" : (self.__tr("year"), self.__tr("years")),
594            "month" : (self.__tr("month"), self.__tr("months")),
595            "week" : (self.__tr("week"), self.__tr("weeks")),
596            "day" : (self.__tr("day"), self.__tr("days")),
597            "hour" : (self.__tr("hour"), self.__tr("hours")),
598            "minute" : (self.__tr("minute"), self.__tr("minutes")),
599            "second" : (self.__tr("second"), self.__tr("seconds")),
600            }
601
602        self.num_repr = { 1 : self.__tr("one"),
603              2 : self.__tr("two"),
604              3 : self.__tr("three"),
605              4 : self.__tr("four"),
606              5 : self.__tr("five"),
607              6 : self.__tr("six"),
608              7 : self.__tr("seven"),
609              8 : self.__tr("eight"),
610              9 : self.__tr("nine"),
611              10 : self.__tr("ten"),
612              11 : self.__tr("eleven"),
613              12 : self.__tr("twelve")
614              }
615
616        if self.Latest_ver is "":
617            self.TabIndex = { self.FunctionsTab: self.UpdateFuncsTab,
618                self.StatusTab: self.UpdateStatusTab,
619                self.SuppliesTab: self.UpdateSuppliesTab,
620                self.PrintSettingsTab: self.UpdatePrintSettingsTab,
621                self.PrintJobsTab: self.UpdatePrintControlTab,
622                }
623        else:
624            self.TabIndex = { self.FunctionsTab: self.UpdateFuncsTab,
625                self.StatusTab: self.UpdateStatusTab,
626                self.SuppliesTab: self.UpdateSuppliesTab,
627                self.PrintSettingsTab: self.UpdatePrintSettingsTab,
628                self.PrintJobsTab: self.UpdatePrintControlTab,
629                self.UpgradeTab:self.UpdateUpgradeTab,
630                }
631
632
633
634    def InitialUpdate(self):
635        self.RescanDevices()
636
637        cont = True
638        if self.initial_device_uri is not None:
639            if not self.ActivateDevice(self.initial_device_uri):
640                log.error("Device %s not found" % self.initial_device_uri)
641                cont = False
642
643        self.refresh_timer = QTimer(self, "RefreshTimer")
644        self.connect(self.refresh_timer, SIGNAL('timeout()'), self.TimedRefresh)
645
646        if MIN_AUTO_REFRESH_RATE <= self.user_settings.auto_refresh_rate <= MAX_AUTO_REFRESH_RATE:
647            self.refresh_timer.start(self.user_settings.auto_refresh_rate * 1000)
648
649        self.update_timer = QTimer(self)
650        self.connect(self.update_timer, SIGNAL("timeout()"), self.ThreadUpdate)
651        self.update_timer.start(500)
652
653
654    def ActivateDevice(self, device_uri):
655        log.debug(log.bold("Activate: %s %s %s" % ("*"*20, device_uri, "*"*20)))
656        d = self.DeviceList.firstItem()
657        found = False
658
659        while d is not None:
660            if d.device_uri == device_uri:
661                found = True
662                self.DeviceList.setSelected(d, True)
663                self.DeviceList.setCurrentItem(d)
664                break
665
666            d = d.nextItem()
667
668        return found
669
670
671
672    # ***********************************************************************************
673    #
674    # UPDATES/NOTIFICATIONS
675    #
676    # ***********************************************************************************
677
678    def notifier_activated(self, sock): # dbus message has arrived
679        m = ''
680        while True:
681            ready = select.select([sock], [], [], 0.1)
682
683            if ready[0]:
684                m = ''.join([m, os.read(sock, self.fmt_size)])
685                if len(m) == self.fmt_size:
686                    if self.cur_device is None or self.rescanning:
687                        return
688
689                    event = device.Event(*struct.unpack(self.fmt, m))
690                    desc = device.queryString(event.event_code)
691                    error_state = STATUS_TO_ERROR_STATE_MAP.get(event.event_code, ERROR_STATE_CLEAR)
692                    log.debug("Status event: %s (%d)" % (event.device_uri, event.event_code))
693
694                    if event.event_code > EVENT_MAX_USER_EVENT:
695
696                        if event.event_code == EVENT_HISTORY_UPDATE: # 9003
697                            log.debug("History update: %s" % event.device_uri)
698
699                            if not self.rescanning:
700                                dev = self.findDeviceByURI(event.device_uri)
701
702                                self.UpdateHistory(dev)
703                                self.UpdateDevice(dev)
704
705                        elif event.event_code == EVENT_CUPS_QUEUES_REMOVED or event.event_code == EVENT_CUPS_QUEUES_ADDED:
706                            pass
707
708                        elif event.event_code == EVENT_RAISE_DEVICE_MANAGER: # 9001
709                            log.debug("Raise requested")
710                            self.showNormal()
711                            self.setActiveWindow()
712                            self.raiseW()
713
714                    else:
715                        log.debug("Ignored")
716
717            else:
718                break
719
720
721    def TimedRefresh(self):
722        if not self.rescanning and self.user_settings.auto_refresh and self.allow_auto_refresh:
723            log.debug("Refresh timer...")
724            self.CleanupChildren()
725
726            if self.user_settings.auto_refresh_type == 0:
727                self.RequestDeviceUpdate()
728            else:
729                self.RescanDevices()
730
731
732    def ThreadUpdate(self): # periodically check for updates from update thread
733        if not self.response_queue.empty():
734            response_code, device_uri = self.response_queue.get()
735
736            if response_code == RESPONSE_START:
737                self.statusBar().message(self.__tr("Updating %1...").arg(device_uri))
738                qApp.processEvents()
739
740            elif response_code == RESPONSE_DONE:
741                self.statusBar().message(QString("%1 (%2)").arg(self.cur_device_uri).\
742                    arg(', '.join(self.cur_device.cups_printers)))
743
744                dev = self.findDeviceByURI(device_uri)
745
746                if dev is not None:
747                    self.UpdateHistory(dev)
748                    self.UpdateDevice(dev)
749
750                qApp.processEvents()
751
752            if self.response_queue.empty() and self.request_queue.empty():
753                self.UpdateTitle()
754                # Disable thread timer until more items placed in request queue?
755
756
757    # ***********************************************************************************
758    #
759    # TAB/DEVICE CHANGE SLOTS
760    #
761    # ***********************************************************************************
762
763    def Tabs_currentChanged(self, tab=None):
764        """ Called when the active tab changes.
765            Update newly displayed tab.
766        """
767
768        if tab is None:
769            tab = self.Tabs.currentPage()
770
771        try:
772            self.TabIndex[tab]()
773        except AttributeError:
774            pass
775
776    def Tabs_deviceChanged(self, tab=None):
777        """ Called when the device changes.
778            Update the currently displayed tab.
779        """
780        if tab is None:
781            tab = self.Tabs.currentPage()
782
783        self.TabIndex[tab]()
784
785
786    # ***********************************************************************************
787    #
788    # DEVICE ICON LIST/DEVICE UPDATE(S)
789    #
790    # ***********************************************************************************
791
792    def DeviceList_onItem(self, a0):
793        pass
794
795
796    def deviceRescanAction_activated(self):
797        self.deviceRescanAction.setEnabled(False)
798        self.RequestDeviceUpdate()
799        self.deviceRescanAction.setEnabled(True)
800
801
802    def deviceRefreshAll_activated(self):
803        self.RescanDevices()
804
805
806    def DeviceList_clicked(self,a0):
807        pass
808
809
810    def CreatePixmap(self, dev=None):
811        if dev is None:
812            dev = self.cur_device
813
814        try:
815            dev.icon
816        except AttributeError:
817            dev.icon = "default_printer"
818
819        try:
820            self.device_icons[dev.icon]
821        except:
822            self.device_icons[dev.icon] = load_pixmap(dev.icon, 'devices')
823
824        pix = self.device_icons[dev.icon]
825
826        w, h = pix.width(), pix.height()
827        error_state = dev.error_state
828        icon = QPixmap(w, h)
829        p = QPainter(icon)
830        p.eraseRect(0, 0, icon.width(), icon.height())
831        p.drawPixmap(0, 0, pix)
832
833        try:
834            tech_type = dev.tech_type
835        except AttributeError:
836            tech_type = TECH_TYPE_NONE
837
838        if dev.device_type == DEVICE_TYPE_FAX:
839            p.drawPixmap(w - self.fax_icon.width(), 0, self.fax_icon)
840
841        if error_state != ERROR_STATE_CLEAR:
842            if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
843                status_icon = self.SMALL_ICONS[error_state][0] # ink
844            else:
845                status_icon = self.SMALL_ICONS[error_state][1] # laser
846
847            if status_icon is not None:
848                p.drawPixmap(0, 0, status_icon)
849
850        p.end()
851
852        return icon
853
854
855    def DeviceListRefresh(self):
856        global devices
857        log.debug("Rescanning device list...")
858
859        if not self.rescanning:
860            self.setCaption(self.__tr("Refreshing Device List - HP Device Manager"))
861            self.statusBar().message(self.__tr("Refreshing device list..."))
862
863            self.rescanning = True
864            self.cups_devices = device.getSupportedCUPSDevices(['hp', 'hpfax'])
865
866            devices_lock.acquire()
867
868            try:
869                adds = []
870                for d in self.cups_devices:
871                    if d not in devices:
872                        adds.append(d)
873
874                log.debug("Adds: %s" % ','.join(adds))
875
876                removals = []
877                for d in devices:
878                    if d not in self.cups_devices:
879                        removals.append(d)
880
881                log.debug("Removals (1): %s" % ','.join(removals))
882
883                updates = []
884                for d in devices:
885                    if d not in adds and d not in removals:
886                        updates.append(d)
887
888                log.debug("Updates: %s" % ','.join(updates))
889
890
891                for d in adds:
892                    log.debug("adding: %s" % d)
893                    try:
894                        dev = device.Device(d, service=self.service, callback=self.callback,
895                  disable_dbus=self.disable_dbus)
896                    except Error:
897                        log.error("Unexpected error in Device class.")
898                        log.exception()
899
900                    if not dev.supported:
901                        log.debug("Unsupported model - removing device.")
902                        removals.append(d)
903                        continue
904
905                    self.CheckForDeviceSettingsUI(dev)
906                    icon = self.CreatePixmap(dev)
907
908                    if dev.device_type == DEVICE_TYPE_FAX:
909                        DeviceViewItem(self.DeviceList,  self.__tr("%1 (Fax)").arg(dev.model_ui),
910                            icon, d)
911                    else:
912                        if dev.fax_type:
913                            DeviceViewItem(self.DeviceList, self.__tr("%1 (Printer)").arg(dev.model_ui),
914                                icon, d)
915                        else:
916                            DeviceViewItem(self.DeviceList, dev.model_ui,
917                                icon, d)
918
919                    devices[d] = dev
920
921                log.debug("Removals (2): %s" % ','.join(removals))
922                removed_device=None
923                for d in removals:
924                    removed_device = d
925                    item = self.DeviceList.firstItem()
926                    log.debug("removing: %s" % d)
927
928                    try:
929                        del devices[d]
930                    except KeyError:
931                        pass
932
933
934                    while item is not None:
935                        if item.device_uri == d:
936                            self.DeviceList.takeItem(item)
937                            break
938
939                        item = item.nextItem()
940
941                    qApp.processEvents()
942
943                self.DeviceList.adjustItems()
944                self.DeviceList.updateGeometry()
945                qApp.processEvents()
946                # sending Event to remove this device from hp-systray
947                if removed_device:
948                    utils.sendEvent(EVENT_CUPS_QUEUES_REMOVED,removed_device, "")
949
950                if len(devices):
951                    for tab in self.TabIndex:
952                        self.Tabs.setTabEnabled(tab, True)
953
954                    if self.cur_device_uri:
955                        item = first_item = self.DeviceList.firstItem()
956
957                        while item is not None:
958                            qApp.processEvents()
959                            if item.device_uri == self.cur_device_uri:
960                                self.DeviceList.setCurrentItem(item)
961                                self.DeviceList.setSelected(item, True)
962                                self.statusBar().message(self.cur_device_uri)
963                                break
964
965                            item = item.nextItem()
966
967                        else:
968                            self.cur_device = None
969                            self.cur_device_uri = ''
970
971                    if self.cur_device is None:
972                        self.cur_device_uri = self.DeviceList.firstItem().device_uri
973                        self.cur_device = devices[self.cur_device_uri]
974                        self.DeviceList.setCurrentItem(self.DeviceList.firstItem())
975
976                    self.Tabs.setTabEnabled(self.SuppliesTab, self.cur_device.device_type == DEVICE_TYPE_PRINTER and
977                        self.cur_device.error_state != ERROR_STATE_ERROR)
978
979                    self.UpdatePrinterCombos()
980
981                    user_conf.set('last_used', 'device_uri', self.cur_device_uri)
982
983                    for d in updates + adds:
984                        if d not in removals:
985                            self.RequestDeviceUpdate(devices[d])
986
987                else:
988                    self.cur_device = None
989                    self.deviceRescanAction.setEnabled(False)
990                    self.deviceRemoveAction.setEnabled(False)
991                    self.rescanning = False
992                    self.statusBar().message(self.__tr("Press F6 to refresh."))
993
994                    for tab in self.TabIndex:
995                        self.Tabs.setTabEnabled(tab, False)
996
997                    dlg = NoDevicesForm(self, "", True)
998                    dlg.show()
999
1000            finally:
1001                self.rescanning = False
1002                devices_lock.release()
1003
1004            self.deviceRescanAction.setEnabled(True)
1005            self.deviceRemoveAction.setEnabled(True)
1006
1007
1008
1009    def UpdateTitle(self):
1010        if self.cur_device.device_type == DEVICE_TYPE_FAX:
1011                self.setCaption(self.__tr("HP Device Manager - %1 (Fax)").arg(self.cur_device.model_ui))
1012        else:
1013            if self.cur_device.fax_type:
1014                self.setCaption(self.__tr("HP Device Manager - %1 (Printer)").arg(self.cur_device.model_ui))
1015            else:
1016                self.setCaption(self.__tr("HP Device Manager - %1").arg(self.cur_device.model_ui))
1017
1018
1019    def UpdateDeviceByURI(self, device_uri):
1020        return self.UpdateDevice(self.findDeviceByURI(device_uri))
1021
1022
1023    def UpdateDevice(self, dev=None, update_tab=True):
1024        """ Update the device icon and currently displayed tab.
1025        """
1026        if dev is None:
1027            dev = self.cur_device
1028
1029        log.debug("UpdateDevice(%s)" % dev.device_uri)
1030
1031        item = self.findItem(dev)
1032
1033        if item is not None:
1034            item.setPixmap(self.CreatePixmap(dev))
1035
1036        if dev is self.cur_device and dev.error_state == ERROR_STATE_ERROR:
1037            self.Tabs.setCurrentPage(1)
1038
1039        if dev is self.cur_device and update_tab:
1040            self.UpdatePrinterCombos()
1041            self.TabIndex[self.Tabs.currentPage()]()
1042
1043            if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1044                self.Tabs.changeTab(self.PrintSettingsTab,self.__tr("Print Settings"))
1045                self.Tabs.changeTab(self.PrintJobsTab,self.__tr("Print Control"))
1046            else:
1047                self.Tabs.changeTab(self.PrintSettingsTab,self.__tr("Fax Settings"))
1048                self.Tabs.changeTab(self.PrintJobsTab,self.__tr("Fax Control"))
1049        qApp.processEvents()
1050
1051
1052    def DeviceList_currentChanged(self, i):
1053        if i is not None: # and not self.rescanning:
1054            self.cur_device_uri = self.DeviceList.currentItem().device_uri
1055            self.cur_device = devices[self.cur_device_uri]
1056            user_conf.set('last_used', 'device_uri', self.cur_device_uri)
1057
1058            self.Tabs.setTabEnabled(self.SuppliesTab, self.cur_device.device_type == DEVICE_TYPE_PRINTER and
1059                self.cur_device.error_state != ERROR_STATE_ERROR)
1060
1061            self.UpdateDevice()
1062            self.UpdateTitle()
1063
1064
1065    def findItem(self, dev):
1066        if dev is None:
1067            dev = self.cur_device
1068
1069        return self.findItemByURI(dev.device_uri)
1070
1071
1072    def findItemByURI(self, device_uri):
1073        item = self.DeviceList.firstItem()
1074
1075        while item is not None:
1076            if item.device_uri == device_uri:
1077                return item
1078            item = item.nextItem()
1079
1080
1081    def findDeviceByURI(self, device_uri):
1082        try:
1083            return devices[device_uri]
1084        except:
1085            return None
1086
1087
1088    def RequestDeviceUpdate(self, dev=None, item=None):
1089        """ Submit device update request to update thread. """
1090
1091        if dev is None:
1092            dev = self.cur_device
1093
1094        if dev is not None:
1095            #log.debug("RequestDeviceUpdate(%s)" % dev.device_uri)
1096            dev.error_state = ERROR_STATE_REFRESHING
1097            self.UpdateDevice(dev, update_tab=False)
1098            qApp.processEvents()
1099
1100            self.request_queue.put(dev)
1101
1102
1103    def RescanDevices(self):
1104        #log.debug("RescanDevices()")
1105        if not self.rescanning:
1106            self.deviceRefreshAll.setEnabled(False)
1107            try:
1108                self.DeviceListRefresh()
1109            finally:
1110                self.deviceRefreshAll.setEnabled(True)
1111
1112
1113    def callback(self):
1114        qApp.processEvents()
1115
1116
1117    # ***********************************************************************************
1118    #
1119    # DEVICE LIST RIGHT CLICK
1120    #
1121    # ***********************************************************************************
1122
1123    def DeviceList_rightButtonClicked(self, item, pos):
1124        popup = QPopupMenu(self)
1125
1126        if item is not None and item is self.DeviceList.currentItem():
1127            if self.cur_device.error_state != ERROR_STATE_ERROR:
1128                if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1129                    popup.insertItem(self.__tr("Print..."), self.PrintButton_clicked)
1130
1131                    if self.cur_device.scan_type:
1132                        popup.insertItem(self.__tr("Scan..."), self.ScanButton_clicked)
1133
1134                    if self.cur_device.pcard_type:
1135                        popup.insertItem(self.__tr("Access Photo Cards..."), self.PCardButton_clicked)
1136
1137                    if self.cur_device.copy_type:
1138                        popup.insertItem(self.__tr("Make Copies..."), self.MakeCopiesButton_clicked)
1139
1140                elif self.cur_device.device_type == DEVICE_TYPE_FAX:
1141                    if self.cur_device.fax_type:
1142                        popup.insertItem(self.__tr("Send Fax..."), self.SendFaxButton_clicked)
1143
1144                popup.insertSeparator()
1145
1146                if self.cur_device.device_settings_ui is not None:
1147                    popup.insertItem(self.__tr("Device Settings..."), self.deviceSettingsButton_clicked)
1148
1149            if not self.rescanning:
1150                popup.insertItem(self.__tr("Refresh Device"), self.deviceRescanAction_activated)
1151
1152        if not self.rescanning:
1153            popup.insertItem(self.__tr("Refresh All"), self.deviceRefreshAll_activated)
1154
1155        popup.popup(pos)
1156
1157
1158    # ***********************************************************************************
1159    #
1160    # PRINTER NAME COMBOS
1161    #
1162    # ***********************************************************************************
1163
1164    def updatePrinterList(self):
1165        if self.cur_device is not None and \
1166            self.cur_device.supported:
1167
1168            printers = cups.getPrinters()
1169            self.cur_device.cups_printers = []
1170
1171            for p in printers:
1172                if p.device_uri == self.cur_device_uri:
1173                    self.cur_device.cups_printers.append(p.name)
1174
1175
1176    def UpdatePrinterCombos(self):
1177        self.PrintSettingsPrinterCombo.clear()
1178        self.PrintJobPrinterCombo.clear()
1179
1180        if self.cur_device is not None and \
1181            self.cur_device.supported:
1182
1183            for c in self.cur_device.cups_printers:
1184                self.PrintSettingsPrinterCombo.insertItem(c.decode("utf-8"))
1185                self.PrintJobPrinterCombo.insertItem(c.decode("utf-8"))
1186
1187            self.cur_printer = to_unicode(self.PrintSettingsPrinterCombo.currentText())
1188
1189    def PrintSettingsPrinterCombo_activated(self, s):
1190        self.cur_printer = to_unicode(s)
1191        self.PrintJobPrinterCombo.setCurrentText(self.cur_printer.encode("latin1")) # TODO: ?
1192        return self.PrinterCombo_activated(self.cur_printer)
1193
1194    def PrintJobPrinterCombo_activated(self, s):
1195        self.cur_printer = to_unicode(s)
1196        self.PrintSettingsPrinterCombo.setCurrentText(self.cur_printer.encode("latin1")) # TODO: ?
1197        return self.PrinterCombo_activated(self.cur_printer)
1198
1199    def PrinterCombo_activated(self, printer):
1200        self.TabIndex[self.Tabs.currentPage()]()
1201        self.UpdatePrintSettingsTabPrinter()
1202
1203
1204
1205    # ***********************************************************************************
1206    #
1207    # FUNCTIONS/ACTION TAB
1208    #
1209    # ***********************************************************************************
1210
1211    def InitFuncsTab(self):
1212        self.click_lock = None
1213
1214    def UpdateFuncsTab(self):
1215        self.iconList.clear()
1216
1217        d = self.cur_device
1218
1219        if d is not None:
1220
1221            avail = d.device_state != DEVICE_STATE_NOT_FOUND and d.supported
1222            fax = d.fax_type and prop.fax_build and d.device_type == DEVICE_TYPE_FAX and \
1223                sys.hexversion >= 0x020300f0 and avail
1224            printer = d.device_type == DEVICE_TYPE_PRINTER and avail
1225            req_plugin = d.plugin == PLUGIN_REQUIRED
1226            opt_plugin = d.plugin == PLUGIN_OPTIONAL
1227
1228            hplip_conf = ConfigParser.ConfigParser()
1229            fp = open("/usr/local/etc/hp/hplip.conf", "r")
1230            hplip_conf.readfp(fp)
1231            fp.close()
1232
1233            try:
1234                plugin_installed = utils.to_bool(hplip_conf.get("hplip", "plugin"))
1235            except ConfigParser.NoOptionError:
1236                plugin_installed = False
1237
1238            if d.plugin:
1239                if req_plugin and plugin_installed:
1240                    x = self.__tr("Download and install<br>required plugin (already installed).")
1241
1242                elif req_plugin and not plugin_installed:
1243                    x = self.__tr("Download and install<br>required plugin (needs installation).")
1244
1245                elif opt_plugin and plugin_installed:
1246                    x = self.__tr("Download and install<br>optional plugin (already installed).")
1247
1248                elif opt_plugin and not plugin_installed:
1249                    x = self.__tr("Download and install<br>optional plugin (needs installation).")
1250
1251            else:
1252                x = ''
1253
1254
1255            self.ICONS = [
1256
1257                # PRINTER
1258
1259                (lambda : printer,                 # filter func
1260                self.__tr("Print"),                      # Text
1261                "print",       # Icon
1262                self.__tr("Print documents or files."),  # Tooltip
1263                self.user_settings.cmd_print),           # command/action
1264
1265                (lambda : d.scan_type and prop.scan_build and \
1266                    d.device_type == DEVICE_TYPE_PRINTER and avail,
1267                self.__tr("Scan"),
1268                "scan",
1269                self.__tr("Scan a document, image, or photograph.<br>"),
1270                self.user_settings.cmd_scan),
1271
1272                (lambda : d.copy_type and d.device_type == DEVICE_TYPE_PRINTER and avail,
1273                self.__tr("Make Copies"),
1274                "makecopies",
1275                self.__tr("Make copies on the device controlled by the PC.<br>"),
1276                self.user_settings.cmd_copy),
1277
1278                (lambda : d.pcard_type and d.device_type == DEVICE_TYPE_PRINTER and avail,
1279                self.__tr("Unload Photo Card"),
1280                "makecopies",
1281                self.__tr("Copy images from the device's photo card to the PC."),
1282                self.PCardButton_clicked),
1283
1284                # FAX
1285
1286                (lambda: fax,
1287                self.__tr("Send Fax"),
1288                "fax",
1289                self.__tr("Send a fax from the PC."),
1290                self.user_settings.cmd_fax),
1291
1292                (lambda: fax,
1293                self.__tr("Fax Setup"),
1294                "fax_setup",
1295                self.__tr("Fax support must be setup before you can send faxes."),
1296                self.faxSettingsButton_clicked),
1297
1298                (lambda: fax,
1299                self.__tr("Fax Address Book"),
1300                "fab",
1301                self.__tr("Setup fax phone numbers to use when sending faxes from the PC."),
1302                self.cmd_fab),
1303
1304                # SETTINGS/TOOLS
1305
1306                (lambda : self.cur_device.device_settings_ui is not None and avail,
1307                self.__tr("Device Settings"),
1308                "settings",
1309                self.__tr("Your device has special device settings.<br>You may alter these settings here."),
1310                self.deviceSettingsButton_clicked),
1311
1312                (lambda : printer,
1313                self.__tr("Print Test Page"),
1314                "testpage",
1315                self.__tr("Print a test page to test the setup of your printer."),
1316                self.PrintTestPageButton_clicked),
1317
1318                (lambda : True,
1319                self.__tr("View Printer (Queue) Information"),
1320                "cups",
1321                self.__tr("View the printers (queues) installed in CUPS."),
1322                self.viewPrinterInformation),
1323
1324                (lambda : True,
1325                self.__tr("View Device Information"),
1326                "info",
1327                self.__tr("This information is primarily useful for <br>debugging and troubleshooting (advanced)."),
1328                self.viewInformation),
1329
1330                (lambda: printer and d.align_type,
1331                self.__tr("Align Cartridges (Print Heads)"),
1332                "align",
1333                self.__tr("This will improve the quality of output when a new cartridge is installed."),
1334                self.AlignPensButton_clicked),
1335
1336                (lambda: printer and d.clean_type,
1337                self.__tr("Clean Printheads"),
1338                "clean",
1339                self.__tr("You only need to perform this action if you are<br>having problems with poor printout quality due to clogged ink nozzles."),
1340                self.CleanPensButton_clicked),
1341
1342                (lambda: printer and d.color_cal_type and d.color_cal_type == COLOR_CAL_TYPE_TYPHOON,
1343                self.__tr("Color Calibration"),
1344                "colorcal",
1345                self.__tr("Use this procedure to optimimize your printer's color output<br>(requires glossy photo paper)."),
1346                self.ColorCalibrationButton_clicked),
1347
1348                (lambda: printer and d.color_cal_type and d.color_cal_type != COLOR_CAL_TYPE_TYPHOON,
1349                self.__tr("Color Calibration"),
1350                "colorcal",
1351                self.__tr("Use this procedure to optimimize your printer's color output."),
1352                self.ColorCalibrationButton_clicked),
1353
1354                (lambda: printer and d.linefeed_cal_type,
1355                self.__tr("Line Feed Calibration"),
1356                "linefeed_cal",
1357                self.__tr("Use line feed calibration to optimize print quality<br>(to remove gaps in the printed output)."),
1358                self.linefeedCalibration),
1359
1360                (lambda: printer and d.pq_diag_type,
1361                self.__tr("Print Diagnostic Page"),
1362                "pq_diag",
1363                self.__tr("Your printer can print a test page <br>to help diagnose print quality problems."),
1364                self.pqDiag),
1365
1366                # FIRMWARE
1367
1368                (lambda : printer and d.fw_download,
1369                self.__tr("Download Firmware"),
1370                "firmware",
1371                self.__tr("Download firmware to your printer <br>(required on some devices after each power-up)."),
1372                self.ShowFirmwareDlg),
1373
1374                # PLUGIN
1375
1376                (lambda : req_plugin,
1377                self.__tr("Install Required Plugin"),
1378                "plugin",
1379                x, #self.__tr("Download and install the HPLIP plugin."),
1380                self.downloadPlugin),
1381
1382                (lambda : opt_plugin,
1383                self.__tr("Install Optional Plugin"),
1384                "plugin",
1385                x, #self.__tr("Download and install the HPLIP plugin."),
1386                self.downloadPlugin),
1387
1388                # HELP/WEBSITE
1389
1390                (lambda : True,
1391                self.__tr("Visit HPLIP Website"),
1392                "hp_logo",
1393                self.__tr("Visit HPLIP website."),
1394                self.viewSupport),
1395
1396                (lambda : True,
1397                self.__tr("Help"),
1398                "help",
1399                self.__tr("View HPLIP help."),
1400                self.viewHelp),
1401            ]
1402
1403            if not self.func_icons_cached:
1404                for filter, text, icon, tooltip, cmd in self.ICONS:
1405                    self.func_icons[icon] = load_pixmap(icon, '32x32')
1406                self.func_icons_cached = True
1407
1408            for filter, text, icon, tooltip, cmd in self.ICONS:
1409                if filter is not None:
1410                    if not list(filter()):
1411                        continue
1412
1413                FuncViewItem(self.iconList, text,
1414                    self.func_icons[icon],
1415                    tooltip,
1416                    cmd)
1417
1418
1419    def downloadPlugin(self):
1420        ok, sudo_ok = pkit.run_plugin_command(self.cur_device.plugin == PLUGIN_REQUIRED, self.cur_device.mq['plugin-reason'])
1421        if not sudo_ok:
1422            QMessageBox.critical(self,
1423                self.caption(),
1424                self.__tr("<b>Unable to find an appropriate su/sudo utility to run hp-plugin.</b><p>Install kdesu, gnomesu, or gksu.</p>"),
1425                QMessageBox.Ok,
1426                QMessageBox.NoButton,
1427                QMessageBox.NoButton)
1428        else:
1429            self.UpdateFuncsTab()
1430
1431
1432    def iconList_clicked(self, item):
1433        return self.RunFuncCmd(item)
1434
1435
1436    def RunFuncCmd(self, item):
1437        if item is not None and self.click_lock is not item:
1438            try:
1439                item.cmd()
1440            except TypeError:
1441                self.RunCommand(item.cmd)
1442
1443            self.click_lock = item
1444            QTimer.singleShot(1000, self.UnlockClick)
1445
1446
1447    def UnlockClick(self):
1448        self.click_lock = None
1449
1450
1451    def RunFuncCmdContext(self):
1452        return self.RunFuncCmd(self.iconList.currentItem())
1453
1454
1455    def iconList_contextMenuRequested(self, item, pos):
1456        if item is not None and item is self.iconList.currentItem():
1457            popup = QPopupMenu(self)
1458            popup.insertItem(self.__tr("Open..."), self.RunFuncCmdContext)
1459            popup.popup(pos)
1460
1461
1462    def iconList_returnPressed(self, item):
1463        return self.RunFuncCmd(item)
1464
1465
1466    def deviceSettingsButton_clicked(self):
1467        try:
1468            self.cur_device.open()
1469            self.cur_device.device_settings_ui(self.cur_device, self)
1470        finally:
1471            self.cur_device.close()
1472
1473
1474    def setupDevice_activated(self):
1475        try:
1476            self.cur_device.open()
1477            self.cur_device.device_settings_ui(self.cur_device, self)
1478        finally:
1479            self.cur_device.close()
1480
1481
1482    def PrintButton_clicked(self):
1483        self.RunCommand(self.user_settings.cmd_print)
1484
1485
1486    def ScanButton_clicked(self):
1487        self.RunCommand(self.user_settings.cmd_scan)
1488
1489
1490    def PCardButton_clicked(self):
1491        if self.cur_device.pcard_type == PCARD_TYPE_MLC:
1492            self.RunCommand(self.user_settings.cmd_pcard)
1493
1494        elif self.cur_device.pcard_type == PCARD_TYPE_USB_MASS_STORAGE:
1495            self.FailureUI(self.__tr("<p><b>Photocards on your printer are only available by mounting them as drives using USB mass storage.</b><p>Please refer to your distribution's documentation for setup and usage instructions."))
1496
1497
1498    def SendFaxButton_clicked(self):
1499        self.RunCommand(self.user_settings.cmd_fax)
1500
1501
1502    def MakeCopiesButton_clicked(self):
1503        self.RunCommand(self.user_settings.cmd_copy)
1504
1505
1506    def ConfigureFeaturesButton_clicked(self):
1507        self.settingsConfigure_activated(2)
1508
1509
1510    def viewInformation(self):
1511        dlg = ScrollDialog(ScrollDeviceInfoView, self.cur_device, self.cur_printer, self.service, self)
1512        dlg.exec_loop()
1513
1514
1515    def viewPrinterInformation(self):
1516        dlg = ScrollDialog(ScrollPrinterInfoView, self.cur_device, self.cur_printer, self.service, self)
1517        dlg.exec_loop()
1518
1519
1520    def viewHelp(self):
1521        f = "http://hplip.sf.net"
1522
1523        if prop.doc_build:
1524            g = os.path.join(sys_conf.get('dirs', 'doc'), 'index.html')
1525            if os.path.exists(g):
1526                f = "file://%s" % g
1527
1528        log.debug(f)
1529        utils.openURL(f)
1530
1531
1532    def viewSupport(self):
1533        f = "http://hplip.sf.net"
1534        log.debug(f)
1535        utils.openURL(f)
1536
1537
1538    def pqDiag(self):
1539        d = self.cur_device
1540        pq_diag = d.pq_diag_type
1541
1542        try:
1543            QApplication.setOverrideCursor(QApplication.waitCursor)
1544
1545            try:
1546                d.open()
1547            except Error:
1548                self.CheckDeviceUI()
1549            else:
1550                if d.isIdleAndNoError():
1551                    QApplication.restoreOverrideCursor()
1552
1553                    if pq_diag == 1:
1554                        maint.printQualityDiagType1(d, self.LoadPaperUI)
1555
1556                    elif pq_diag == 2:
1557                        maint.printQualityDiagType2(d, self.LoadPaperUI)
1558
1559                else:
1560                    self.CheckDeviceUI()
1561
1562        finally:
1563            d.close()
1564            QApplication.restoreOverrideCursor()
1565
1566
1567    def linefeedCalibration(self):
1568        d = self.cur_device
1569        linefeed_type = d.linefeed_cal_type
1570
1571        try:
1572            QApplication.setOverrideCursor(QApplication.waitCursor)
1573
1574            try:
1575                d.open()
1576            except Error:
1577                self.CheckDeviceUI()
1578            else:
1579                if d.isIdleAndNoError():
1580                    QApplication.restoreOverrideCursor()
1581
1582                    if linefeed_type == 1:
1583                        maint.linefeedCalType1(d, self.LoadPaperUI)
1584
1585                    elif linefeed_type == 2:
1586                        maint.linefeedCalType2(d, self.LoadPaperUI)
1587
1588                else:
1589                    self.CheckDeviceUI()
1590
1591        finally:
1592            d.close()
1593            QApplication.restoreOverrideCursor()
1594
1595
1596    def downloadFirmware(self):
1597        d = self.cur_device
1598        ok = False
1599
1600        try:
1601            QApplication.setOverrideCursor(QApplication.waitCursor)
1602            d.open()
1603
1604            if d.isIdleAndNoError():
1605                ok = d.downloadFirmware()
1606
1607        finally:
1608            d.close()
1609            QApplication.restoreOverrideCursor()
1610
1611            if not ok:
1612                self.FailureUI(self.__tr("<b>An error occured downloading firmware file.</b><p>Please check your printer and ensure that the HPLIP plugin has been installed."))
1613
1614
1615    def CheckDeviceUI(self):
1616        self.FailureUI(self.__tr("<b>Device is busy or in an error state.</b><p>Please check device and try again."))
1617
1618
1619    def LoadPaperUI(self, msg="", title=""):
1620        LPFObj = LoadPaperForm(self)
1621        if title:
1622            LPFObj.setCaption(title)
1623        if msg:
1624            LPFObj.textLabel7.setText(msg)
1625
1626        if LPFObj.exec_loop() == QDialog.Accepted:
1627            return True
1628        return False
1629
1630
1631    def AlignmentNumberUI(self, letter, hortvert, colors, line_count, choice_count):
1632        dlg = AlignForm(self, letter, hortvert, colors, line_count, choice_count)
1633        if dlg.exec_loop() == QDialog.Accepted:
1634            return True, dlg.value
1635        else:
1636            return False, 0
1637
1638
1639    def PaperEdgeUI(self, maximum):
1640        dlg = PaperEdgeAlignForm(self)
1641        if dlg.exec_loop() == QDialog.Accepted:
1642            return True, dlg.value
1643        else:
1644            return False, 0
1645
1646
1647    def BothPensRequiredUI(self):
1648        self.WarningUI(self.__tr("<p><b>Both cartridges are required for alignment.</b><p>Please install both cartridges and try again."))
1649
1650
1651    def InvalidPenUI(self):
1652        self.WarningUI(self.__tr("<p><b>One or more cartiridges are missing from the printer.</b><p>Please install cartridge(s) and try again."))
1653
1654
1655    def PhotoPenRequiredUI(self):
1656        self.WarningUI(self.__tr("<p><b>Both the photo and color cartridges must be inserted into the printer to perform color calibration.</b><p>If you are planning on printing with the photo cartridge, please insert it and try again."))
1657
1658
1659    def PhotoPenRequiredUI2(self):
1660        self.WarningUI(self.__tr("<p><b>Both the photo (regular photo or photo blue) and color cartridges must be inserted into the printer to perform color calibration.</b><p>If you are planning on printing with the photo or photo blue cartridge, please insert it and try again."))
1661
1662
1663    def NotPhotoOnlyRequired(self): # Type 11
1664        self.WarningUI(self.__tr("<p><b>Cannot align with only the photo cartridge installed.</b><p>Please install other cartridges and try again."))
1665
1666
1667    def AioUI1(self):
1668        dlg = AlignType6Form1(self)
1669        return dlg.exec_loop() == QDialog.Accepted
1670
1671
1672    def AioUI2(self):
1673        AlignType6Form2(self).exec_loop()
1674
1675
1676    def Align10and11UI(self, pattern, align_type):
1677        dlg = Align10Form(pattern, align_type, self)
1678        dlg.exec_loop()
1679        return dlg.getValues()
1680
1681
1682    def Align13UI(self):
1683        dlg = Align13Form(self)
1684        dlg.exec_loop()
1685        return True
1686
1687
1688    def AlignPensButton_clicked(self):
1689        d = self.cur_device
1690        align_type = d.align_type
1691
1692        log.debug("Align: %s %s (type=%d) %s" % ("*"*20, self.cur_device.device_uri, align_type, "*"*20))
1693
1694        try:
1695            QApplication.setOverrideCursor(QApplication.waitCursor)
1696
1697            try:
1698                d.open()
1699            except Error:
1700                self.CheckDeviceUI()
1701            else:
1702                if d.isIdleAndNoError():
1703                    QApplication.restoreOverrideCursor()
1704
1705                    if align_type == ALIGN_TYPE_AUTO:
1706                        maint.AlignType1(d, self.LoadPaperUI)
1707
1708                    elif align_type == ALIGN_TYPE_8XX:
1709                        maint.AlignType2(d, self.LoadPaperUI, self.AlignmentNumberUI,
1710               self.BothPensRequiredUI)
1711
1712                    elif align_type in (ALIGN_TYPE_9XX,ALIGN_TYPE_9XX_NO_EDGE_ALIGN):
1713                         maint.AlignType3(d, self.LoadPaperUI, self.AlignmentNumberUI,
1714                self.PaperEdgeUI, align_type)
1715
1716                    elif align_type in (ALIGN_TYPE_LIDIL_0_3_8, ALIGN_TYPE_LIDIL_0_4_3, ALIGN_TYPE_LIDIL_VIP):
1717                        maint.AlignxBow(d, align_type, self.LoadPaperUI, self.AlignmentNumberUI,
1718              self.PaperEdgeUI, self.InvalidPenUI, self.ColorAdjUI)
1719
1720                    elif align_type == ALIGN_TYPE_LIDIL_AIO:
1721                        maint.AlignType6(d, self.AioUI1, self.AioUI2, self.LoadPaperUI)
1722
1723                    elif align_type == ALIGN_TYPE_DESKJET_450:
1724                        maint.AlignType8(d, self.LoadPaperUI, self.AlignmentNumberUI)
1725
1726                    elif align_type == ALIGN_TYPE_LBOW:
1727                        maint.AlignType10(d, self.LoadPaperUI, self.Align10and11UI)
1728
1729                    elif align_type == ALIGN_TYPE_LIDIL_0_5_4:
1730                        maint.AlignType11(d, self.LoadPaperUI, self.Align10and11UI, self.NotPhotoOnlyRequired)
1731
1732                    elif align_type == ALIGN_TYPE_OJ_PRO:
1733                        maint.AlignType12(d, self.LoadPaperUI)
1734
1735                    elif align_type == ALIGN_TYPE_AIO:
1736                        maint.AlignType13(d, self.LoadPaperUI, self.Align13UI)
1737
1738                    elif align_type == ALIGN_TYPE_LEDM:
1739                        maint.AlignType15(d, self.LoadPaperUI, self.Align13UI)
1740
1741                    elif align_type == ALIGN_TYPE_LEDM_MANUAL:
1742                        maint.AlignType16(d, self.LoadPaperUI, self.AlignmentNumberUI)
1743
1744                    elif align_type == ALIGN_TYPE_LEDM_FF_CC_0:
1745                        maint.AlignType17(d, self.LoadPaperUI, self.Align13UI)
1746
1747                    elif align_type == ALIGN_TYPE_UNSUPPORTED:
1748                        self.WarningUI(self.__tr("<p><b>Alignment through HPLIP not supported for this printer. Please use the printer's front panel to perform cartridge alignment.</b>"))
1749
1750                else:
1751                    self.CheckDeviceUI()
1752
1753        finally:
1754            d.close()
1755            QApplication.restoreOverrideCursor()
1756
1757
1758    def ColorAdjUI(self, line, maximum=0):
1759        dlg = ColorAdjForm(self, line)
1760        if dlg.exec_loop() == QDialog.Accepted:
1761            return True, dlg.value
1762        else:
1763            return False, 0
1764
1765
1766    def ColorCalUI(self):
1767        dlg = ColorCalForm(self)
1768        if dlg.exec_loop() == QDialog.Accepted:
1769            return True, dlg.value
1770        else:
1771            return False, 0
1772
1773
1774    def ColorCalUI2(self):
1775        dlg = ColorCalForm2(self)
1776        if dlg.exec_loop() == QDialog.Accepted:
1777            return True, dlg.value
1778        else:
1779            return False, 0
1780
1781
1782    def ColorCalUI4(self):
1783        dlg = ColorCal4Form(self)
1784        if dlg.exec_loop() == QDialog.Accepted:
1785            return True, dlg.values
1786        else:
1787            return False, None
1788
1789
1790    def ColorCalibrationButton_clicked(self):
1791        d = self.cur_device
1792        color_cal_type = d.color_cal_type
1793        log.debug("Color-cal: %s %s (type=%d) %s" % ("*"*20, self.cur_device.device_uri, color_cal_type, "*"*20))
1794
1795        if color_cal_type == COLOR_CAL_TYPE_TYPHOON:
1796            dlg = ScrollDialog(ScrollColorCalView, self.cur_device, self.cur_printer, self.service, self)
1797            dlg.exec_loop()
1798        else:
1799            try:
1800                QApplication.setOverrideCursor(QApplication.waitCursor)
1801
1802                try:
1803                    d.open()
1804                except Error:
1805                    self.CheckDeviceUI()
1806                else:
1807                    if d.isIdleAndNoError():
1808                        QApplication.restoreOverrideCursor()
1809
1810                        if color_cal_type == COLOR_CAL_TYPE_DESKJET_450:
1811                            maint.colorCalType1(d, self.LoadPaperUI, self.ColorCalUI,
1812                                self.PhotoPenRequiredUI)
1813
1814                        elif color_cal_type == COLOR_CAL_TYPE_MALIBU_CRICK:
1815                            maint.colorCalType2(d, self.LoadPaperUI, self.ColorCalUI2,
1816                                self.InvalidPenUI)
1817
1818                        elif color_cal_type == COLOR_CAL_TYPE_STRINGRAY_LONGBOW_TORNADO:
1819                            maint.colorCalType3(d, self.LoadPaperUI, self.ColorAdjUI,
1820                                self.PhotoPenRequiredUI2)
1821
1822                        elif color_cal_type == COLOR_CAL_TYPE_CONNERY:
1823                            maint.colorCalType4(d, self.LoadPaperUI, self.ColorCalUI4,
1824                                self.WaitUI)
1825
1826                        elif color_cal_type == COLOR_CAL_TYPE_COUSTEAU:
1827                            maint.colorCalType5(d, self.LoadPaperUI)
1828
1829                        elif color_cal_type == COLOR_CAL_TYPE_CARRIER:
1830                            maint.colorCalType6(d, self.LoadPaperUI)
1831
1832                    else:
1833                        self.CheckDeviceUI()
1834
1835            finally:
1836                d.close()
1837                QApplication.restoreOverrideCursor()
1838
1839
1840    def PrintTestPageButton_clicked(self):
1841        dlg = ScrollDialog(ScrollTestpageView, self.cur_device, self.cur_printer, self.service, self)
1842        dlg.exec_loop()
1843
1844
1845    def CleanUI1(self, msg=""):
1846        CFObj = CleaningForm(self, self.cur_device, 1)
1847        if msg:
1848            CFObj.CleaningText.setText(msg)
1849        return CFObj.exec_loop() == QDialog.Accepted
1850
1851
1852    def CleanUI2(self, msg=""):
1853        CFObj = CleaningForm(self, self.cur_device, 2)
1854        if msg:
1855            CFObj.CleaningText.setText(msg)
1856        return CFObj.exec_loop() == QDialog.Accepted
1857
1858
1859    def CleanUI3(self, msg=""):
1860        CleaningForm2(self).exec_loop()
1861        return True
1862
1863
1864    def WaitUI(self, seconds):
1865        WaitForm(seconds, None, self).exec_loop()
1866
1867
1868    def CleanPensButton_clicked(self):
1869        d = self.cur_device
1870        clean_type = d.clean_type
1871        log.debug("Clean: %s %s (type=%d) %s" % ("*"*20, self.cur_device.device_uri, clean_type, "*"*20))
1872
1873        try:
1874            QApplication.setOverrideCursor(QApplication.waitCursor)
1875
1876            try:
1877                d.open()
1878            except Error:
1879                self.CheckDeviceUI()
1880            else:
1881                if d.isIdleAndNoError():
1882                    QApplication.restoreOverrideCursor()
1883
1884                    if clean_type == CLEAN_TYPE_PCL:
1885                        maint.cleaning(d, clean_type, maint.cleanType1, maint.primeType1,
1886                            maint.wipeAndSpitType1, self.LoadPaperUI,
1887                            self.CleanUI1, self.CleanUI2, self.CleanUI3,
1888                            self.WaitUI)
1889
1890                    elif clean_type == CLEAN_TYPE_LIDIL:
1891                        maint.cleaning(d, clean_type, maint.cleanType2, maint.primeType2,
1892                            maint.wipeAndSpitType2, self.LoadPaperUI,
1893                            self.CleanUI1, self.CleanUI2, self.CleanUI3,
1894                            self.WaitUI)
1895
1896                    elif clean_type == CLEAN_TYPE_PCL_WITH_PRINTOUT:
1897                        maint.cleaning(d, clean_type, maint.cleanType1, maint.primeType1,
1898                            maint.wipeAndSpitType1, self.LoadPaperUI,
1899                            self.CleanUI1, self.CleanUI2, self.CleanUI3,
1900                            self.WaitUI)
1901
1902                    elif clean_type == CLEAN_TYPE_LEDM:
1903                        maint.cleaning(d, clean_type, maint.cleanTypeLedm, maint.cleanTypeLedm1,
1904                            maint.cleanTypeLedm2, self.LoadPaperUI,
1905                            self.CleanUI1, self.CleanUI2, self.CleanUI3,
1906                            self.WaitUI, maint.isCleanTypeLedmWithPrint)
1907
1908                    elif clean_type == CLEAN_TYPE_UNSUPPORTED:
1909                        self.WarningUI(self.__tr("<p><b>Cleaning through HPLIP not supported for this printer. Please use the printer's front panel to perform cartridge cleaning.</b>"))
1910
1911                else:
1912                    self.CheckDeviceUI()
1913
1914        finally:
1915            d.close()
1916            QApplication.restoreOverrideCursor()
1917
1918
1919    def OpenEmbeddedBrowserButton_clicked(self):
1920        utils.openURL("http://%s" % self.cur_device.host)
1921
1922
1923    def faxAddressBookButton_clicked(self):
1924        self.RunCommand(self.cmd_fab)
1925
1926
1927    def faxSettingsButton_clicked(self):
1928        try:
1929            try:
1930                self.cur_device.open()
1931            except Error:
1932                self.CheckDeviceUI()
1933            else:
1934                try:
1935                    result_code, fax_num = self.cur_device.getPML(pml.OID_FAX_LOCAL_PHONE_NUM)
1936                except Error:
1937                    log.error("PML failure.")
1938                    self.FailureUI(self.__tr("<p><b>Operation failed. Device busy.</b>"))
1939                    return
1940
1941                fax_num = str(fax_num)
1942
1943                try:
1944                    result_code, name = self.cur_device.getPML(pml.OID_FAX_STATION_NAME)
1945                except Error:
1946                    log.error("PML failure.")
1947                    self.FailureUI(self.__tr("<p><b>Operation failed. Device busy.</b>"))
1948                    return
1949
1950                name = str(name)
1951
1952                dlg = FaxSettingsForm(self.cur_device, fax_num, name, self)
1953                dlg.exec_loop()
1954
1955        finally:
1956            self.cur_device.close()
1957
1958
1959    def addressBookButton_clicked(self):
1960        self.RunCommand(self.cmd_fab)
1961
1962
1963    def ShowFirmwareDlg(self):
1964        dlg = FirmwareDialog(self, self.cur_device_uri)
1965        dlg.show()
1966        return dlg.exec_loop() == QDialog.Accepted
1967
1968    # ***********************************************************************************
1969    #
1970    # STATUS TAB
1971    #
1972    # ***********************************************************************************
1973
1974    def InitStatusTab(self):
1975        self.statusListView.setSorting(-1)
1976        self.statusListView.setColumnText(0, QString(""))
1977        #self.statusListView.setColumnWidthMode(0, QListView.Manual)
1978        self.statusListView.setColumnWidth(0, 16)
1979
1980
1981    def UpdateStatusTab(self):
1982        #log.debug("UpdateStatusTab()")
1983        self.UpdateHistory()
1984        self.UpdatePanel()
1985        self.UpdateStatusList()
1986
1987
1988    def UpdatePanel(self):
1989        if self.cur_device is not None and \
1990            self.cur_device.hist and \
1991            self.cur_device.supported:
1992
1993            dq = self.cur_device.dq
1994
1995            if dq.get('panel', 0) == 1:
1996                line1 = dq.get('panel-line1', '')
1997                line2 = dq.get('panel-line2', '')
1998            else:
1999                try:
2000                    line1 = device.queryString(self.cur_device.hist[0].event_code)
2001                except (AttributeError, TypeError):
2002                    line1 = ''
2003
2004                line2 = ''
2005
2006            pm = load_pixmap('panel_lcd', 'other')
2007
2008            p = QPainter()
2009            p.begin(pm)
2010            p.setPen(QColor(0, 0, 0))
2011            p.setFont(self.font())
2012
2013            x, y_line1, y_line2 = 10, 17, 33
2014
2015            # TODO: Scroll long lines
2016            p.drawText(x, y_line1, line1)
2017            p.drawText(x, y_line2, line2)
2018            p.end()
2019
2020            self.panel.setPixmap(pm)
2021
2022        else:
2023            self.panel.setPixmap(load_pixmap('panel_lcd', 'other'))
2024
2025
2026    def UpdateHistory(self, dev=None):
2027        if self.dbus_avail:
2028            if dev is None:
2029                dev = self.cur_device
2030
2031            return dev.queryHistory()
2032
2033        self.cur_device.hist = [self.cur_device.last_event]
2034
2035
2036
2037    def UpdateStatusList(self):
2038        self.statusListView.clear()
2039        row = 0
2040        hist = self.cur_device.hist[:]
2041
2042        if hist:
2043            hist.reverse()
2044            row = len(hist)-1
2045
2046            for e in hist:
2047                if e is None:
2048                    continue
2049
2050                ess = device.queryString(e.event_code, 0)
2051                esl = device.queryString(e.event_code, 1)
2052
2053                if row == 0:
2054                    desc = self.__tr("(most recent)")
2055
2056                else:
2057                    desc = self.getTimeDeltaDesc(e.timedate)
2058
2059                dt = QDateTime()
2060                dt.setTime_t(int(e.timedate), Qt.LocalTime)
2061
2062                # TODO: In Qt4.x, use QLocale.toString(date, format)
2063                tt = QString("%1 %2").arg(dt.toString()).arg(desc)
2064
2065                if e.job_id:
2066                    job_id = to_unicode(e.job_id)
2067                else:
2068                    job_id = ''
2069
2070                error_state = STATUS_TO_ERROR_STATE_MAP.get(e.event_code, ERROR_STATE_CLEAR)
2071                tech_type = self.cur_device.tech_type
2072
2073                try:
2074                    if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
2075                        status_pix = self.STATUS_ICONS[error_state][0] # ink
2076                    else:
2077                        status_pix = self.STATUS_ICONS[error_state][1] # laser
2078                except KeyError:
2079                    status_pix = self.STATUS_ICONS[ERROR_STATE_CLEAR][0]
2080
2081                StatusListViewItem(self.statusListView, status_pix, ess, tt, to_unicode(e.event_code),
2082                    job_id, to_unicode(e.username))
2083
2084                row -= 1
2085
2086        i = self.statusListView.firstChild()
2087        if i is not None:
2088            self.statusListView.setCurrentItem(i)
2089
2090
2091    def getTimeDeltaDesc(self, past):
2092        t1 = QDateTime()
2093        t1.setTime_t(int(past))
2094        t2 = QDateTime.currentDateTime()
2095        delta = t1.secsTo(t2)
2096        return self.__tr("(about %1 ago)").arg(self.stringify(delta))
2097
2098
2099    # "Nicely readable timedelta"
2100    # Credit: Bjorn Lindqvist
2101    # ASPN Python Recipe 498062
2102    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/498062
2103    # Note: Modified from recipe
2104    def seconds_in_units(self, seconds):
2105        unit_limits = [("year", 31536000),
2106                       ("month", 2592000),
2107                       ("week", 604800),
2108                       ("day", 86400),
2109                       ("hour", 3600),
2110                       ("minute", 60)]
2111
2112        for unit_name, limit in unit_limits:
2113            if seconds >= limit:
2114                amount = int(round(float(seconds) / limit))
2115                return amount, unit_name
2116
2117        return seconds, "second"
2118
2119
2120    def stringify(self, seconds):
2121        amount, unit_name = self.seconds_in_units(seconds)
2122
2123        try:
2124            i18n_amount = self.num_repr[amount]
2125        except KeyError:
2126            i18n_amount = to_unicode(amount)
2127
2128        if amount == 1:
2129            i18n_unit = self.unit_names[unit_name][0]
2130        else:
2131            i18n_unit = self.unit_names[unit_name][1]
2132
2133        return QString("%1 %2").arg(i18n_amount).arg(i18n_unit)
2134
2135
2136
2137
2138    # ***********************************************************************************
2139    #
2140    # SUPPLIES TAB
2141    #
2142    # ***********************************************************************************
2143
2144    def InitSuppliesTab(self):
2145        self.pix_battery = load_pixmap('battery', '16x16')
2146
2147        yellow = "#ffff00"
2148        light_yellow = "#ffffcc"
2149        cyan = "#00ffff"
2150        light_cyan = "#ccffff"
2151        magenta = "#ff00ff"
2152        light_magenta = "#ffccff"
2153        black = "#000000"
2154        blue = "#0000ff"
2155        gray = "#808080"
2156        dark_gray = "#a9a9a9"
2157        light_gray = "#c0c0c0"
2158        red = "#ff0000"
2159
2160        self.TYPE_TO_PIX_MAP = {
2161                               AGENT_TYPE_UNSPECIFIED : [black],
2162                               AGENT_TYPE_BLACK: [black],
2163                               AGENT_TYPE_MATTE_BLACK : [black],
2164                               AGENT_TYPE_PHOTO_BLACK : [dark_gray],
2165                               AGENT_TYPE_BLACK_B8800: [black],
2166                               AGENT_TYPE_CMY: [cyan, magenta, yellow],
2167                               AGENT_TYPE_KCM: [light_cyan, light_magenta, light_yellow],
2168                               AGENT_TYPE_GGK: [dark_gray],
2169                               AGENT_TYPE_YELLOW: [yellow],
2170                               AGENT_TYPE_MAGENTA: [magenta],
2171                               AGENT_TYPE_CYAN : [cyan],
2172                               AGENT_TYPE_CYAN_LOW: [light_cyan],
2173                               AGENT_TYPE_YELLOW_LOW: [light_yellow],
2174                               AGENT_TYPE_MAGENTA_LOW: [light_magenta],
2175                               AGENT_TYPE_BLUE: [blue],
2176                               AGENT_TYPE_KCMY_CM: [yellow, cyan, magenta],
2177                               AGENT_TYPE_LC_LM: [light_cyan, light_magenta],
2178                               #AGENT_TYPE_Y_M: [yellow, magenta],
2179                               #AGENT_TYPE_C_K: [black, cyan],
2180                               AGENT_TYPE_LG_PK: [light_gray, dark_gray],
2181                               AGENT_TYPE_LG: [light_gray],
2182                               AGENT_TYPE_G: [gray],
2183                               AGENT_TYPE_DG: [dark_gray],
2184                               AGENT_TYPE_PG: [light_gray],
2185                               AGENT_TYPE_C_M: [cyan, magenta],
2186                               AGENT_TYPE_K_Y: [black, yellow],
2187                               AGENT_TYPE_LC: [light_cyan],
2188                               AGENT_TYPE_RED : [red],
2189                               }
2190
2191        self.suppliesList.setSorting(-1)
2192        self.suppliesList.setColumnText(0, QString(""))
2193        #self.suppliesList.setColumnWidthMode(0, QListView.Manual)
2194        self.suppliesList.setColumnWidth(0, 16)
2195        self.suppliesList.setColumnWidth(3, 100)
2196
2197
2198    def UpdateSuppliesTab(self):
2199        #log.debug("UpdateSuppliesTab()")
2200
2201        self.suppliesList.clear()
2202
2203        if self.cur_device is not None and \
2204            self.cur_device.supported and \
2205            self.cur_device.status_type != STATUS_TYPE_NONE and \
2206            self.cur_device.device_state != DEVICE_STATE_NOT_FOUND:
2207
2208            try:
2209                self.cur_device.sorted_supplies
2210            except AttributeError:
2211                self.cur_device.sorted_supplies = []
2212
2213            if not self.cur_device.sorted_supplies:
2214                a = 1
2215                while True:
2216                    try:
2217                        agent_type = int(self.cur_device.dq['agent%d-type' % a])
2218                        agent_kind = int(self.cur_device.dq['agent%d-kind' % a])
2219                        agent_sku = self.cur_device.dq['agent%d-sku' % a]
2220                    except KeyError:
2221                        break
2222                    else:
2223                        self.cur_device.sorted_supplies.append((a, agent_kind, agent_type, agent_sku))
2224
2225                    a += 1
2226
2227                self.cur_device.sorted_supplies.sort(lambda x, y: cmp(x[1], y[1]) or cmp(x[3], y[3]), reverse=True)
2228
2229
2230            for x in self.cur_device.sorted_supplies:
2231                a, agent_kind, agent_type, agent_sku = x
2232                agent_level = int(self.cur_device.dq['agent%d-level' % a])
2233                agent_desc = self.cur_device.dq['agent%d-desc' % a]
2234                agent_health_desc = self.cur_device.dq['agent%d-health-desc' % a]
2235
2236                # Bar graph level
2237                level_pixmap = None
2238                if agent_kind in (AGENT_KIND_SUPPLY,
2239                                  AGENT_KIND_HEAD,
2240                                  AGENT_KIND_HEAD_AND_SUPPLY,
2241                                  AGENT_KIND_TONER_CARTRIDGE,
2242                                  AGENT_KIND_MAINT_KIT,
2243                                  AGENT_KIND_ADF_KIT,
2244                                  AGENT_KIND_INT_BATTERY,
2245                                  AGENT_KIND_DRUM_KIT,
2246                                  ):
2247
2248                    level_pixmap = self.createBarGraph(agent_level, agent_type)
2249
2250                # Color icon
2251                pixmap = None
2252                if agent_kind in (AGENT_KIND_SUPPLY,
2253                                  AGENT_KIND_HEAD,
2254                                  AGENT_KIND_HEAD_AND_SUPPLY,
2255                                  AGENT_KIND_TONER_CARTRIDGE,
2256                                  #AGENT_KIND_MAINT_KIT,
2257                                  #AGENT_KIND_ADF_KIT,
2258                                  AGENT_KIND_INT_BATTERY,
2259                                  #AGENT_KIND_DRUM_KIT,
2260                                  ):
2261
2262                    pixmap = self.getIcon(agent_kind, agent_type)
2263
2264
2265                SuppliesListViewItem(self.suppliesList, pixmap, agent_desc,
2266                    agent_sku, level_pixmap, agent_health_desc)
2267
2268            i = self.suppliesList.firstChild()
2269            if i is not None:
2270                self.suppliesList.setCurrentItem(i)
2271
2272
2273
2274
2275    def getIcon(self, agent_kind, agent_type):
2276        if agent_kind in (AGENT_KIND_SUPPLY,
2277                          AGENT_KIND_HEAD,
2278                          AGENT_KIND_HEAD_AND_SUPPLY,
2279                          AGENT_KIND_TONER_CARTRIDGE):
2280
2281            map = self.TYPE_TO_PIX_MAP[agent_type]
2282
2283            if isinstance(map, list):
2284                map_len = len(map)
2285                pix = QPixmap(16, 16) #, -1, QPixmap.DefaultOptim)
2286                pix.fill(qApp.palette().color(QPalette.Active, QColorGroup.Background))
2287                p = QPainter()
2288                p.begin(pix)
2289                p.setBackgroundMode(Qt.OpaqueMode)
2290
2291                if map_len == 1:
2292                    p.setPen(QColor(map[0]))
2293                    p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
2294                    p.drawPie(2, 2, 10, 10, 0, 5760)
2295
2296                elif map_len == 2:
2297                    p.setPen(QColor(map[0]))
2298                    p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
2299                    p.drawPie(2, 4, 8, 8, 0, 5760)
2300
2301                    p.setPen(QColor(map[1]))
2302                    p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
2303                    p.drawPie(6, 4, 8, 8, 0, 5760)
2304
2305                elif map_len == 3:
2306                    p.setPen(QColor(map[2]))
2307                    p.setBrush(QBrush(QColor(map[2]), Qt.SolidPattern))
2308                    p.drawPie(6, 6, 8, 8, 0, 5760)
2309
2310                    p.setPen(QColor(map[1]))
2311                    p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
2312                    p.drawPie(2, 6, 8, 8, 0, 5760)
2313
2314                    p.setPen(QColor(map[0]))
2315                    p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
2316                    p.drawPie(4, 2, 8, 8, 0, 5760)
2317
2318                p.end()
2319                return pix
2320
2321            else:
2322                return map
2323
2324        elif agent_kind == AGENT_KIND_INT_BATTERY:
2325                return self.pix_battery
2326
2327
2328    def createBarGraph(self, percent, agent_type, w=100, h=18):
2329        fw = w/100*percent
2330        px = QPixmap(w, h)
2331        px.fill(qApp.palette().color(QPalette.Active, QColorGroup.Background))
2332
2333        pp = QPainter(px)
2334        pp.setPen(Qt.black)
2335        pp.setBackgroundColor(qApp.palette().color(QPalette.Active, QColorGroup.Base))
2336
2337        map = self.TYPE_TO_PIX_MAP[agent_type]
2338        map_len = len(map)
2339
2340        if map_len == 1 or map_len > 3:
2341            pp.fillRect(0, 0, fw, h, QBrush(QColor(map[0])))
2342
2343        elif map_len == 2:
2344            h2 = h / 2
2345            pp.fillRect(0, 0, fw, h2, QBrush(QColor(map[0])))
2346            pp.fillRect(0, h2, fw, h, QBrush(QColor(map[1])))
2347
2348        elif map_len == 3:
2349            h3 = h / 3
2350            h23 = 2 * h3
2351            pp.fillRect(0, 0, fw, h3, QBrush(QColor(map[0])))
2352            pp.fillRect(0, h3, fw, h23, QBrush(QColor(map[1])))
2353            pp.fillRect(0, h23, fw, h, QBrush(QColor(map[2])))
2354
2355        # draw black frame
2356        pp.drawRect(0, 0, w, h)
2357
2358        if percent > 75 and agent_type in \
2359          (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
2360            pp.setPen(Qt.white)
2361
2362        # 75% ticks
2363        w1 = 3 * w / 4
2364        h6 = h / 6
2365        pp.drawLine(w1, 0, w1, h6)
2366        pp.drawLine(w1, h, w1, h-h6)
2367
2368        if percent > 50 and agent_type in \
2369          (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
2370            pp.setPen(Qt.white)
2371
2372        # 50% ticks
2373        w2 = w / 2
2374        h4 = h / 4
2375        pp.drawLine(w2, 0, w2, h4)
2376        pp.drawLine(w2, h, w2, h-h4)
2377
2378        if percent > 25 and agent_type in \
2379          (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
2380            pp.setPen(Qt.white)
2381
2382        # 25% ticks
2383        w4 = w / 4
2384        pp.drawLine(w4, 0, w4, h6)
2385        pp.drawLine(w4, h, w4, h-h6)
2386
2387        return px
2388
2389
2390
2391    # ***********************************************************************************
2392    #
2393    # PRINTER SETTINGS TAB
2394    #
2395    # ***********************************************************************************
2396
2397    def InitPrintSettingsTab(self): # Add Scrolling Print Settings
2398        PrintJobsTabLayout = QGridLayout(self.PrintSettingsTab,1,1,11,6,"PrintJobsTabLayout")
2399
2400        self.PrintSettingsList = ScrollPrintSettingsView(self.service, self.PrintSettingsTab, "PrintSettingsView")
2401        PrintJobsTabLayout.addMultiCellWidget(self.PrintSettingsList,1,1,0,3)
2402
2403        self.PrintSettingsPrinterCombo = QComboBox(0,self.PrintSettingsTab,"comboBox5")
2404
2405        self.PrintSettingsPrinterCombo.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed,0,0,
2406            self.PrintSettingsPrinterCombo.sizePolicy().hasHeightForWidth()))
2407
2408        PrintJobsTabLayout.addWidget(self.PrintSettingsPrinterCombo, 0, 2)
2409
2410        self.settingTextLabel = QLabel(self.PrintSettingsTab,"self.settingTextLabel")
2411        PrintJobsTabLayout.addWidget(self.settingTextLabel,0,1)
2412
2413        self.settingTextLabel.setText(self.__tr("Printer Name:"))
2414
2415        spacer34 = QSpacerItem(20,20,QSizePolicy.Preferred, QSizePolicy.Minimum)
2416        PrintJobsTabLayout.addItem(spacer34,0,3)
2417
2418        spacer35 = QSpacerItem(20,20,QSizePolicy.Preferred, QSizePolicy.Minimum)
2419        PrintJobsTabLayout.addItem(spacer35,0,0)
2420
2421        self.connect(self.PrintSettingsPrinterCombo, SIGNAL("activated(const QString&)"),
2422            self.PrintSettingsPrinterCombo_activated)
2423
2424
2425    def UpdatePrintSettingsTab(self):
2426        #log.debug("UpdatePrintSettingsTab()")
2427        if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2428            self.settingTextLabel.setText(self.__tr("Printer Name:"))
2429        else:
2430            self.settingTextLabel.setText(self.__tr("Fax Name:"))
2431
2432        self.PrintSettingsList.onDeviceChange(self.cur_device)
2433
2434
2435    def UpdatePrintSettingsTabPrinter(self):
2436        self.PrintSettingsList.onPrinterChange(self.cur_printer)
2437
2438
2439    # ***********************************************************************************
2440    #
2441    # PRINTER CONTROL TAB
2442    #
2443    # ***********************************************************************************
2444
2445    def InitPrintControlTab(self):
2446        self.JOB_STATES = { cups.IPP_JOB_PENDING : self.__tr("Pending"),
2447                            cups.IPP_JOB_HELD : self.__tr("On hold"),
2448                            cups.IPP_JOB_PROCESSING : self.__tr("Printing"),
2449                            cups.IPP_JOB_STOPPED : self.__tr("Stopped"),
2450                            cups.IPP_JOB_CANCELLED : self.__tr("Canceled"),
2451                            cups.IPP_JOB_ABORTED : self.__tr("Aborted"),
2452                            cups.IPP_JOB_COMPLETED : self.__tr("Completed"),
2453                           }
2454
2455        self.cancelToolButton.setIconSet(QIconSet(load_pixmap('cancel', '16x16')))
2456        self.infoToolButton.setIconSet(QIconSet(load_pixmap('info', '16x16')))
2457
2458        self.JOB_STATE_ICONS = { cups.IPP_JOB_PENDING: self.busy_pix,
2459                                 cups.IPP_JOB_HELD : self.busy_pix,
2460                                 cups.IPP_JOB_PROCESSING : self.print_pix,
2461                                 cups.IPP_JOB_STOPPED : self.warning_pix,
2462                                 cups.IPP_JOB_CANCELLED : self.warning_pix,
2463                                 cups.IPP_JOB_ABORTED : self.error_pix,
2464                                 cups.IPP_JOB_COMPLETED : self.ok_pix,
2465                                }
2466
2467        self.jobList.setSorting(-1)
2468        self.jobList.setColumnText(0, QString(""))
2469        #self.jobList.setColumnWidthMode(0, QListView.Manual)
2470        self.jobList.setColumnWidth(0, 16)
2471        self.jobList.setColumnText(1, QString(""))
2472        #self.jobList.setColumnWidthMode(1, QListView.Manual)
2473        self.jobList.setColumnWidth(1, 16)
2474        self.jobList.setColumnWidth(2, 300)
2475        self.cancelToolButton.setEnabled(False)
2476        self.infoToolButton.setEnabled(False)
2477
2478        self.printer_state = cups.IPP_PRINTER_STATE_IDLE
2479
2480        # TODO: Check queues at startup and send events if stopped or rejecting
2481
2482
2483    def UpdatePrintControlTab(self):
2484        #log.debug("UpdatePrintControlTab()")
2485
2486        if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2487            self.printerTextLabel.setText(self.__tr("Printer Name:"))
2488            self.groupBox1.setTitle(self.__tr("Printer Queue Control"))
2489
2490        else:
2491            self.printerTextLabel.setText(self.__tr("Fax Name:"))
2492            self.groupBox1.setTitle(self.__tr("Fax Queue Control"))
2493
2494        self.jobList.clear()
2495        self.UpdatePrintController()
2496
2497        jobs = cups.getJobs()
2498        num_jobs = 0
2499        for j in jobs:
2500            if j.dest.decode('utf-8') == to_unicode(self.cur_printer):
2501                num_jobs += 1
2502
2503        for j in jobs:
2504            if j.dest == self.cur_printer:
2505                JobListViewItem(self.jobList, self.JOB_STATE_ICONS[j.state],
2506                    j.title, self.JOB_STATES[j.state], to_unicode(j.id))
2507
2508        i = self.jobList.firstChild()
2509        if i is not None:
2510            self.jobList.setCurrentItem(i)
2511
2512
2513    def jobList_clicked(self, i):
2514        num = 0
2515        item = self.jobList.firstChild()
2516        while item is not None:
2517            if item.isOn():
2518                num += 1
2519
2520            item = item.nextSibling()
2521
2522        self.cancelToolButton.setEnabled(num)
2523        self.infoToolButton.setEnabled(num == 1)
2524
2525
2526    def infoToolButton_clicked(self):
2527        item = self.jobList.firstChild()
2528        while item is not None:
2529            if item.isOn():
2530                return self.showJobInfoDialog(item)
2531
2532            item = item.nextSibling()
2533
2534
2535    def cancelToolButton_clicked(self):
2536        self.cancelCheckedJobs()
2537
2538
2539    def jobList_contextMenuRequested(self, item, pos, a2):
2540        if item is not None and item is self.jobList.currentItem():
2541            popup = QPopupMenu(self)
2542
2543            popup.insertItem(self.__tr("Cancel Job"), self.cancelJob)
2544            popup.insertSeparator()
2545            popup.insertItem(self.__tr("View Job Log (advanced)..."), self.getJobInfo)
2546
2547            popup.popup(pos)
2548
2549
2550    def cancelJob(self):
2551        item = self.jobList.currentItem()
2552
2553        if item is not None:
2554            self.cur_device.cancelJob(int(item.job_id))
2555
2556
2557    def getJobInfo(self):
2558        return self.showJobInfoDialog(self.jobList.currentItem())
2559
2560
2561    def showJobInfoDialog(self, item):
2562        if item is not None:
2563            text = cups.getPrintJobErrorLog(int(item.job_id))
2564
2565            if text:
2566                dlg = JobInfoDialog(text, self)
2567                dlg.setCaption(self.__tr("HP Device Manager - Job Log - %1 - Job %2").\
2568                    arg(self.cur_printer).arg(to_unicode(item.job_id)))
2569
2570                dlg.exec_loop()
2571
2572            else:
2573                self.FailureUI(self.__tr("<b>No log output found.</b><p>If the print job is stopped or the printer is rejecting jobs, there might not be any output. Also, you will receive more output in the CUPS LogLevel is set to 'debug'."))
2574
2575
2576    def UpdatePrintController(self):
2577        # default printer
2578        self.defaultPushButton.setText(self.__tr("Set as Default"))
2579
2580        if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2581            device_string = "Printer"
2582        else:
2583            device_string = "Fax"
2584
2585
2586        default_printer = cups.getDefaultPrinter()
2587        if default_printer is not None:
2588            default_printer = default_printer.decode('utf8')
2589
2590        if default_printer == self.cur_printer:
2591            s = self.__tr("SET AS DEFAULT")
2592            self.defaultPushButton.setEnabled(False)
2593
2594        else:
2595            s = self.__tr("NOT SET AS DEFAULT")
2596            self.defaultPushButton.setEnabled(True)
2597
2598        QToolTip.add(self.defaultPushButton, self.__tr("The %2 is currently: %1").arg(s,device_string))
2599
2600
2601        self.printer_state = cups.IPP_PRINTER_STATE_IDLE
2602
2603        cups_printers = cups.getPrinters()
2604
2605        for p in cups_printers:
2606            if p.name.decode('utf-8') == self.cur_printer:
2607                self.printer_state = p.state
2608                self.printer_accepting = p.accepting
2609                break
2610
2611        # start/stop
2612        if self.printer_state == cups.IPP_PRINTER_STATE_IDLE:
2613            s = self.__tr("IDLE")
2614            self.stopstartPushButton.setText(self.__tr("Stop %s"%device_string))
2615
2616        elif self.printer_state == cups.IPP_PRINTER_STATE_PROCESSING:
2617            s = self.__tr("PROCESSING")
2618            self.stopstartPushButton.setText(self.__tr("Stop %s"%device_string))
2619
2620        else:
2621            s = self.__tr("STOPPED")
2622            self.stopstartPushButton.setText(self.__tr("Start %s"%device_string))
2623
2624        QToolTip.add(self.stopstartPushButton, self.__tr("The %2 is currently: %1").arg(s,device_string))
2625
2626        # reject/accept
2627        if self.printer_accepting:
2628            s = self.__tr("ACCEPTING JOBS")
2629            self.rejectacceptPushButton.setText(self.__tr("Reject Jobs"))
2630
2631        else:
2632            s = self.__tr("REJECTING JOBS")
2633            self.rejectacceptPushButton.setText(self.__tr("Accept Jobs"))
2634
2635        QToolTip.add(self.rejectacceptPushButton, self.__tr("The %2 is currently: %1").arg(s,device_string))
2636
2637
2638
2639    def stopstartPushButton_clicked(self):
2640        QApplication.setOverrideCursor(QApplication.waitCursor)
2641        try:
2642            if self.printer_state in (cups.IPP_PRINTER_STATE_IDLE, cups.IPP_PRINTER_STATE_PROCESSING):
2643
2644                result, result_str = cups.cups_operation(cups.stop, GUI_MODE, 'qt3', self, self.cur_printer)
2645                if result == cups.IPP_OK:
2646                    if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2647                        e = EVENT_PRINTER_QUEUE_STOPPED
2648                    else:
2649                        e = EVENT_FAX_QUEUE_STOPPED
2650
2651            else:
2652                result, result_str = cups.cups_operation(cups.start, GUI_MODE, 'qt3', self, self.cur_printer)
2653                if result == cups.IPP_OK:
2654                    if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2655                        e = EVENT_PRINTER_QUEUE_STARTED
2656                    else:
2657                        e = EVENT_FAX_QUEUE_STARTED
2658
2659            if result == cups.IPP_OK:
2660                self.UpdatePrintController()
2661                self.cur_device.sendEvent(e, self.cur_printer)
2662            else:
2663                log.error("Start/Stop printer operation failed")
2664                self.FailureUI(self.__tr("<b>Start/Stop printer operation failed.</b><p> Error : %s"%result_str))
2665                cups.releaseCupsInstance()
2666
2667        finally:
2668            QApplication.restoreOverrideCursor()
2669
2670
2671    def rejectacceptPushButton_clicked(self):
2672        QApplication.setOverrideCursor(QApplication.waitCursor)
2673        try:
2674            if self.printer_accepting:
2675                result ,result_str = cups.cups_operation(cups.reject, GUI_MODE, 'qt3', self, self.cur_printer)
2676                if result == cups.IPP_OK:
2677                    if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2678                        e = EVENT_PRINTER_QUEUE_REJECTING_JOBS
2679                    else:
2680                        e = EVENT_FAX_QUEUE_REJECTING_JOBS
2681
2682            else:
2683                result ,result_str = cups.cups_operation(cups.accept, GUI_MODE, 'qt3', self, self.cur_printer)
2684                if result == cups.IPP_OK:
2685                    if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2686                        e = EVENT_PRINTER_QUEUE_ACCEPTING_JOBS
2687                    else:
2688                        e = EVENT_FAX_QUEUE_ACCEPTING_JOBS
2689
2690            if result == cups.IPP_OK:
2691                self.UpdatePrintController()
2692                self.cur_device.sendEvent(e, self.cur_printer)
2693            else:
2694                log.error("Reject/Accept jobs operation failed")
2695                self.FailureUI(self.__tr("<b>Accept/Reject printer operation failed.</b><p>Error : %s"%result_str))
2696                cups.releaseCupsInstance()
2697
2698        finally:
2699            QApplication.restoreOverrideCursor()
2700
2701
2702    def defaultPushButton_clicked(self):
2703        QApplication.setOverrideCursor(QApplication.waitCursor)
2704        try:
2705            result, result_str = cups.cups_operation(cups.setDefaultPrinter.encode('utf8'), GUI_MODE, 'qt3', self, self.cur_printer.encode('utf8'))
2706
2707            if result != cups.IPP_OK:
2708                log.error("Set default printer failed.")
2709                self.FailureUI(self.__tr("<b>Set default printer operation failed.</b><p>Error : %s"%result_str))
2710                cups.releaseCupsInstance()
2711            else:
2712                self.UpdatePrintController()
2713                if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2714                    e = EVENT_PRINTER_QUEUE_SET_AS_DEFAULT
2715                else:
2716                    e = EVENT_FAX_QUEUE_SET_AS_DEFAULT
2717
2718                self.cur_device.sendEvent(e, self.cur_printer)
2719
2720        finally:
2721            QApplication.restoreOverrideCursor()
2722
2723
2724    def cancelCheckedJobs(self):
2725        QApplication.setOverrideCursor(QApplication.waitCursor)
2726        try:
2727            item = self.jobList.firstChild()
2728            while item is not None:
2729                if item.isOn():
2730                    self.cur_device.cancelJob(int(item.job_id))
2731
2732                item = item.nextSibling()
2733
2734        finally:
2735            QApplication.restoreOverrideCursor()
2736
2737        self.UpdatePrintControlTab()
2738
2739    def UpdateUpgradeTab(self):
2740        log.debug("Upgrade Tab is pressed")
2741        self.InstallPushButton_lock = False
2742
2743    def InstallPushButton_clicked(self):
2744        if self.InstallPushButton_lock is True:
2745            return
2746
2747        if self.Is_autoInstaller_distro:
2748            self.InstallPushButton.setEnabled(False)
2749            terminal_cmd = utils.get_terminal()
2750            if terminal_cmd is not None and utils.which("hp-upgrade"):
2751                cmd = terminal_cmd + " 'hp-upgrade -w'"
2752                os_utils.execute(cmd)
2753            else:
2754                log.error("Failed to run hp-upgrade command from terminal =%s "%terminal_cmd)
2755            self.InstallPushButton.setEnabled(True)
2756        else:
2757            self.InstallPushButton_lock = True
2758            utils.openURL("http://hplipopensource.com/hplip-web/install/manual/index.html")
2759            QTimer.singleShot(1000, self.InstallPushButton_unlock)
2760
2761    def InstallPushButton_unlock(self):
2762        self.InstallPushButton_lock = False
2763
2764    # ***********************************************************************************
2765    #
2766    # EXIT/CHILD CLEANUP
2767    #
2768    # ***********************************************************************************
2769
2770    def closeEvent(self, event):
2771        self.Cleanup()
2772        self.request_queue.put(None)
2773        event.accept()
2774
2775
2776    def Cleanup(self):
2777        self.request_queue.put(None)
2778        self.CleanupChildren()
2779        if not self.update_thread.wait(5000):
2780            self.update_thread.terminate()
2781
2782
2783    def CleanupChildren(self):
2784        log.debug("Cleaning up child processes.")
2785        try:
2786            os.waitpid(-1, os.WNOHANG)
2787        except OSError:
2788            pass
2789
2790
2791    # ***********************************************************************************
2792    #
2793    # DEVICE SETTINGS PLUGIN
2794    #
2795    # ***********************************************************************************
2796
2797    def CheckForDeviceSettingsUI(self, dev):
2798        dev.device_settings_ui = None
2799        name = '.'.join(['plugins', dev.model])
2800        log.debug("Attempting to load plugin: %s" % name)
2801        try:
2802            mod = __import__(name, globals(), locals(), [])
2803        except ImportError:
2804            log.debug("No plugin found.")
2805            return
2806        else:
2807            components = name.split('.')
2808            for c in components[1:]:
2809                mod = getattr(mod, c)
2810            log.debug("Loaded: %s" % repr(mod))
2811            dev.device_settings_ui = mod.settingsUI
2812
2813
2814    # ***********************************************************************************
2815    #
2816    # SETTINGS DIALOG
2817    #
2818    # ***********************************************************************************
2819
2820    def settingsConfigure_activated(self, tab_to_show=0):
2821        dlg = SettingsDialog(self)
2822        dlg.TabWidget.setCurrentPage(tab_to_show)
2823
2824        if dlg.exec_loop() == QDialog.Accepted:
2825            old_auto_refresh = self.user_settings.auto_refresh_rate
2826            self.user_settings.load()
2827
2828            if self.user_settings.auto_refresh and old_auto_refresh != self.user_settings.auto_refresh_rate:
2829                self.refresh_timer.changeInterval(self.user_settings.auto_refresh_rate * 1000)
2830
2831            if old_auto_refresh != self.user_settings.auto_refresh:
2832                self.autoRefresh.toggle()
2833
2834
2835    # ***********************************************************************************
2836    #
2837    # SETUP/REMOVE
2838    #
2839    # ***********************************************************************************
2840
2841    def deviceInstallAction_activated(self):
2842        if utils.which('hp-setup'):
2843            cmd = 'hp-setup -u'
2844        else:
2845            cmd = 'python ./setup.py --gui'
2846
2847        log.debug(cmd)
2848        utils.run(cmd)
2849        self.RescanDevices()
2850
2851
2852    def deviceRemoveAction_activated(self):
2853        if self.cur_device is not None:
2854            x = QMessageBox.critical(self,
2855           self.caption(),
2856           self.__tr("<b>Annoying Confirmation: Are you sure you want to remove this device?</b>"),
2857            QMessageBox.Yes,
2858            QMessageBox.No | QMessageBox.Default,
2859            QMessageBox.NoButton)
2860            if x == QMessageBox.Yes:
2861                QApplication.setOverrideCursor(QApplication.waitCursor)
2862                print_uri = self.cur_device.device_uri
2863                fax_uri = print_uri.replace('hp:', 'hpfax:')
2864
2865                log.debug(print_uri)
2866                log.debug(fax_uri)
2867
2868                self.cups_devices = device.getSupportedCUPSDevices(['hp', 'hpfax'])
2869
2870                for d in self.cups_devices:
2871                    if d in (print_uri, fax_uri):
2872                        for p in self.cups_devices[d]:
2873                            log.debug("Removing %s" % p)
2874                            r, result_str = cups.cups_operation(cups.delPrinter, GUI_MODE, 'qt3', self, p)
2875
2876                            if r != cups.IPP_OK:
2877                                self.FailureUI(self.__tr("<p><b>Delete printer queue fails.</b><p>Error : %s"%result_str))
2878                                print_uri =""   # Ignoring further devices delete operation, as authentication is failed or cancelled.
2879                                fax_uri = ""
2880
2881                self.cur_device = None
2882                self.cur_device_uri = ''
2883                user_conf.set('last_used', 'device_uri', '')
2884                QApplication.restoreOverrideCursor()
2885
2886                self.RescanDevices()
2887
2888
2889    # ***********************************************************************************
2890    #
2891    # MISC
2892    #
2893    # ***********************************************************************************
2894
2895
2896    def RunCommand(self, cmd, macro_char='%'):
2897        QApplication.setOverrideCursor(QApplication.waitCursor)
2898
2899        try:
2900            if len(cmd) == 0:
2901                self.FailureUI(self.__tr("<p><b>Unable to run command. No command specified.</b><p>Use <pre>Configure...</pre> to specify a command to run."))
2902                log.error("No command specified. Use settings to configure commands.")
2903            else:
2904                log.debug("Run: %s %s (%s) %s" % ("*"*20, cmd, self.cur_device_uri, "*"*20))
2905                log.debug(cmd)
2906
2907                try:
2908                    cmd = ''.join([self.cur_device.device_vars.get(x, x) \
2909           for x in cmd.split(macro_char)])
2910                except AttributeError:
2911                    pass
2912
2913                log.debug(cmd)
2914
2915                path = cmd.split()[0]
2916                args = cmd.split()
2917
2918                log.debug(path)
2919                log.debug(args)
2920
2921                self.CleanupChildren()
2922                os.spawnvp(os.P_NOWAIT, path, args)
2923                qApp.processEvents()
2924
2925        finally:
2926            QApplication.restoreOverrideCursor()
2927
2928
2929    def helpContents(self):
2930        f = "http://hplip.sf.net"
2931
2932        if prop.doc_build:
2933            g = os.path.join(sys_conf.get('dirs', 'doc'), 'index.html')
2934            if os.path.exists(g):
2935                f = "file://%s" % g
2936
2937        log.debug(f)
2938        utils.openURL(f)
2939
2940
2941    def helpAbout(self):
2942        dlg = AboutDlg(self)
2943        dlg.VersionText.setText(prop.version)
2944        dlg.ToolboxVersionText.setText(self.toolbox_version + " (Qt3)")
2945        dlg.exec_loop()
2946
2947
2948    def FailureUI(self, error_text):
2949        QMessageBox.critical(self,
2950            self.caption(),
2951            error_text,
2952            QMessageBox.Ok,
2953            QMessageBox.NoButton,
2954            QMessageBox.NoButton)
2955
2956
2957    def WarningUI(self, msg):
2958        QMessageBox.warning(self,
2959            self.caption(),
2960            msg,
2961            QMessageBox.Ok,
2962            QMessageBox.NoButton,
2963            QMessageBox.NoButton)
2964
2965
2966    def __tr(self,s,c = None):
2967        return qApp.translate("DevMgr4",s,c)
2968
2969
2970
2971# ***********************************************************************************
2972#
2973# ScrollDeviceInfoView (View Device Information)
2974#
2975# ***********************************************************************************
2976
2977class ScrollDeviceInfoView(ScrollView):
2978    def __init__(self, service, parent=None, form=None, name=None, fl=0):
2979        ScrollView.__init__(self, service, parent, name, fl)
2980
2981
2982    def fillControls(self):
2983        ScrollView.fillControls(self)
2984        self.addDeviceInfo()
2985        self.maximizeControl()
2986
2987
2988    def addDeviceInfo(self):
2989        self.addGroupHeading("info_title", self.__tr("Device Information"))
2990
2991        widget = self.getWidget()
2992
2993        layout37 = QGridLayout(widget,1,1,5,10,"layout37")
2994
2995        self.infoListView = QListView(widget,"fileListView")
2996        self.infoListView.addColumn(self.__tr("Static/Dynamic"))
2997        self.infoListView.addColumn(self.__tr("Key"))
2998        self.infoListView.addColumn(self.__tr("Value"))
2999        self.infoListView.setAllColumnsShowFocus(1)
3000        self.infoListView.setShowSortIndicator(1)
3001        self.infoListView.setColumnWidth(0, 50)
3002        self.infoListView.setColumnWidth(1, 150)
3003        self.infoListView.setColumnWidth(2, 300)
3004        self.infoListView.setItemMargin(2)
3005        self.infoListView.setSorting(-1)
3006
3007        layout37.addMultiCellWidget(self.infoListView,1,1,0,3)
3008
3009        mq_keys = list(self.cur_device.mq.keys())
3010        mq_keys.sort()
3011        mq_keys.reverse()
3012        for key,i in zip(mq_keys, list(range(len(mq_keys)))):
3013            QListViewItem(self.infoListView, self.__tr("Static"), key, str(self.cur_device.mq[key]))
3014
3015        dq_keys = list(self.cur_device.dq.keys())
3016        dq_keys.sort()
3017        dq_keys.reverse()
3018        for key,i in zip(dq_keys, list(range(len(dq_keys)))):
3019            QListViewItem(self.infoListView, self.__tr("Dynamic"), key, str(self.cur_device.dq[key]))
3020
3021        self.addWidget(widget, "file_list", maximize=True)
3022
3023
3024    def __tr(self,s,c = None):
3025        return qApp.translate("ScrollDeviceInfoView",s,c)
3026
3027
3028
3029# ***********************************************************************************
3030#
3031# ScrollTestpageView (Print Test Page)
3032#
3033# ***********************************************************************************
3034
3035class ScrollTestpageView(ScrollView):
3036    def __init__(self, service, parent=None, form=None, name=None, fl=0):
3037        ScrollView.__init__(self, service, parent, name, fl)
3038        self.dialog = parent
3039
3040
3041    def fillControls(self):
3042        ScrollView.fillControls(self)
3043
3044        if self.addPrinterFaxList():
3045            self.addTestpageType()
3046
3047            self.addLoadPaper()
3048
3049            self.printButton = self.addActionButton("bottom_nav", self.__tr("Print Test Page"),
3050                self.printButton_clicked, 'print.png', None)
3051
3052
3053    def addTestpageType(self):
3054        self.addGroupHeading("testpage_type", self.__tr("Test Page Type"))
3055        widget = self.getWidget()
3056
3057        Form4Layout = QGridLayout(widget,1,1,5,10,"Form4Layout")
3058
3059        self.buttonGroup3 = QButtonGroup(widget,"buttonGroup3")
3060        self.buttonGroup3.setLineWidth(0)
3061        self.buttonGroup3.setColumnLayout(0,Qt.Vertical)
3062        self.buttonGroup3.layout().setSpacing(5)
3063        self.buttonGroup3.layout().setMargin(10)
3064
3065        buttonGroup3Layout = QGridLayout(self.buttonGroup3.layout())
3066        buttonGroup3Layout.setAlignment(Qt.AlignTop)
3067
3068        self.radioButton6 = QRadioButton(self.buttonGroup3,"radioButton6")
3069        self.radioButton6.setEnabled(False)
3070        buttonGroup3Layout.addWidget(self.radioButton6,1,0)
3071
3072        self.radioButton5 = QRadioButton(self.buttonGroup3,"radioButton5")
3073        self.radioButton5.setChecked(1)
3074        buttonGroup3Layout.addWidget(self.radioButton5,0,0)
3075
3076        Form4Layout.addWidget(self.buttonGroup3,0,0)
3077
3078        self.radioButton6.setText(self.__tr("Printer diagnostic page (does not test print driver)"))
3079        self.radioButton5.setText(self.__tr("HPLIP test page (tests print driver)"))
3080
3081        self.addWidget(widget, "page_type")
3082
3083
3084    def printButton_clicked(self):
3085        d = self.cur_device
3086        printer_name = self.cur_printer
3087        printed = False
3088
3089        try:
3090            QApplication.setOverrideCursor(QApplication.waitCursor)
3091
3092            try:
3093                d.open()
3094            except Error:
3095                self.CheckDeviceUI()
3096            else:
3097                try:
3098                    if d.isIdleAndNoError():
3099                        QApplication.restoreOverrideCursor()
3100                        d.close()
3101
3102                        d.printTestPage(printer_name)
3103                        printed = True
3104
3105                    else:
3106                        d.close()
3107                        self.CheckDeviceUI()
3108                except Error:
3109                    self.CheckDeviceUI()
3110
3111        finally:
3112            QApplication.restoreOverrideCursor()
3113
3114        if printed:
3115            QMessageBox.information(self,
3116                self.caption(),
3117                self.__tr("<p><b>A test page should be printing on your printer.</b><p>If the page fails to print, please visit http://hplip.sourceforge.net for troubleshooting and support."),
3118                QMessageBox.Ok,
3119                QMessageBox.NoButton,
3120                QMessageBox.NoButton)
3121
3122        self.dialog.accept()
3123
3124
3125    def CheckDeviceUI(self):
3126            self.FailureUI(self.__tr("<b>Device is busy or in an error state.</b><p>Please check device and try again."))
3127
3128
3129    def FailureUI(self, error_text):
3130        QMessageBox.critical(self,
3131            self.caption(),
3132            error_text,
3133            QMessageBox.Ok,
3134            QMessageBox.NoButton,
3135            QMessageBox.NoButton)
3136
3137
3138    def __tr(self,s,c = None):
3139        return qApp.translate("ScrollTestpageView",s,c)
3140
3141# ***********************************************************************************
3142#
3143# ScrollPrinterInfoView (View Device Information)
3144#
3145# ***********************************************************************************
3146
3147class ScrollPrinterInfoView(ScrollView):
3148    def __init__(self, service, parent = None, form=None, name = None,fl = 0):
3149        ScrollView.__init__(self, service, parent, name, fl)
3150
3151
3152    def fillControls(self):
3153        ScrollView.fillControls(self)
3154
3155        printers = []
3156        for p in self.printers:
3157            if p.device_uri == self.cur_device.device_uri:
3158                printers.append(p)
3159
3160        if not printers:
3161            self.addGroupHeading("error_title", self.__tr("No printers found for this device."))
3162        else:
3163            for p in printers:
3164                self.addPrinterInfo(p)
3165
3166        self.maximizeControl()
3167
3168
3169    def addPrinterInfo(self, p):
3170        self.addGroupHeading(p.name, p.name)
3171        widget = self.getWidget()
3172
3173        layout1 = QVBoxLayout(widget,5,10,"layout1")
3174
3175        textLabel2 = QLabel(widget,"textLabel2")
3176
3177        if p.device_uri.startswith("hpfax:"):
3178            s = self.__tr("Fax")
3179        else:
3180            s = self.__tr("Printer")
3181
3182        textLabel2.setText(self.__tr("Type: %1").arg(s))
3183        layout1.addWidget(textLabel2)
3184
3185        textLabel3 = QLabel(widget,"textLabel3")
3186        textLabel3.setText(self.__tr("Location: %1").arg(p.location))
3187        layout1.addWidget(textLabel3)
3188
3189        textLabel4 = QLabel(widget,"textLabel4")
3190        textLabel4.setText(self.__tr("Description/Info: %1").arg(p.info))
3191        layout1.addWidget(textLabel4)
3192
3193        textLabel5 = QLabel(widget,"textLabel5")
3194
3195        if p.state == cups.IPP_PRINTER_STATE_IDLE:
3196            s = self.__tr("Idle")
3197        elif p.state == cups.IPP_PRINTER_STATE_PROCESSING:
3198            s = self.__tr("Processing")
3199        elif p.state == cups.IPP_PRINTER_STATE_STOPPED:
3200            s = self.__tr("Stopped")
3201        else:
3202            s = self.__tr("Unknown")
3203
3204        textLabel5.setText(self.__tr("State: %1").arg(s))
3205        layout1.addWidget(textLabel5)
3206
3207        textLabel6 = QLabel(widget,"textLabel6")
3208        textLabel6.setText(self.__tr("PPD/Driver: %1").arg(p.makemodel))
3209        layout1.addWidget(textLabel6)
3210
3211        textLabel7 = QLabel(widget,"textLabel7")
3212        textLabel7.setText(self.__tr("CUPS/IPP Printer URI: %1").arg(p.printer_uri))
3213        layout1.addWidget(textLabel7)
3214
3215        self.addWidget(widget, p.name)
3216
3217
3218    def __tr(self,s,c = None):
3219        return qApp.translate("ScrollPrinterInfoView",s,c)
3220
3221
3222
3223
3224# ***********************************************************************************
3225#
3226# Color cal type 7
3227#
3228# ***********************************************************************************
3229
3230class ScrollColorCalView(ScrollView):
3231    def __init__(self, service, parent = None, form=None, name = None,fl = 0):
3232        ScrollView.__init__(self, service, parent, name, fl)
3233        self.dialog = parent
3234
3235
3236    def fillControls(self):
3237        ScrollView.fillControls(self)
3238        self.addLoadPaper(PAPER_TYPE_HP_ADV_PHOTO)
3239
3240        self.printButton = self.addActionButton("bottom_nav", self.__tr("Perform Color Calibration"),
3241            self.colorcalButton_clicked, 'print.png', None)
3242
3243
3244    def colorcalButton_clicked(self):
3245        d = self.cur_device
3246        printer_name = self.cur_printer
3247        printed = False
3248
3249        try:
3250            QApplication.setOverrideCursor(QApplication.waitCursor)
3251
3252            try:
3253                d.open()
3254            except Error:
3255                self.CheckDeviceUI()
3256            else:
3257                if d.isIdleAndNoError():
3258                    QApplication.restoreOverrideCursor()
3259                    d.close()
3260
3261                    d.setPML(pml.OID_PRINT_INTERNAL_PAGE, pml.PRINT_INTERNAL_PAGE_AUTOMATIC_COLOR_CALIBRATION)
3262                    printed = True
3263
3264                else:
3265                    d.close()
3266                    self.CheckDeviceUI()
3267
3268        finally:
3269            QApplication.restoreOverrideCursor()
3270
3271        if printed:
3272            QMessageBox.information(self,
3273                self.caption(),
3274                self.__tr("<p><b>A test page should be printing on your printer.</b><p>If the page fails to print, please visit http://hplip.sourceforge.net for troubleshooting and support."),
3275                QMessageBox.Ok,
3276                QMessageBox.NoButton,
3277                QMessageBox.NoButton)
3278
3279        self.dialog.accept()
3280
3281
3282    def CheckDeviceUI(self):
3283            self.FailureUI(self.__tr("<b>Device is busy or in an error state.</b><p>Please check device and try again."))
3284
3285
3286    def FailureUI(self, error_text):
3287        QMessageBox.critical(self,
3288            self.caption(),
3289            error_text,
3290            QMessageBox.Ok,
3291            QMessageBox.NoButton,
3292            QMessageBox.NoButton)
3293
3294
3295    def __tr(self,s,c = None):
3296        return qApp.translate("ScrollColorCalView",s,c)
3297