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, Naga Samrat Chowdary Narla
20#
21
22#from __future__ import generators
23
24# Std Lib
25import sys
26import time
27import os
28import gzip
29import select
30import struct
31import signal
32from base.sixext.moves import configparser
33# Local
34from base.g import *
35from base import device, utils, pml, maint, models, pkit, os_utils
36from prnt import cups
37from base.sixext import PY3
38from base.codes import *
39from .ui_utils import *
40import hpmudext
41from installer.core_install import *
42# Qt
43from PyQt5.QtCore import *
44from PyQt5.QtGui import *
45from PyQt5.QtWidgets import *
46import collections
47
48# dbus
49try:
50    import dbus
51    from dbus.mainloop.glib import DBusGMainLoop
52    from dbus import lowlevel
53except ImportError:
54    log.error("Unable to load DBus libraries. Please check your installation and try again.")
55    if PY3:                        # Workaround due to incomplete Python3 support in Linux distros.
56        log.error("Please upgrade your python installation to the latest available version.")
57    sys.exit(1)
58
59import warnings
60# Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
61# (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
62warnings.simplefilter("ignore", DeprecationWarning)
63
64
65# Main form
66from .devmgr5_base import Ui_MainWindow
67from .devmgr_ext import Ui_MainWindow_Derived
68# Aux. dialogs
69from .faxsetupdialog import FaxSetupDialog
70from .plugindialog import PluginDialog
71from .firmwaredialog import FirmwareDialog
72from .aligndialog import AlignDialog
73from .printdialog import PrintDialog
74from .makecopiesdialog import MakeCopiesDialog
75from .sendfaxdialog import SendFaxDialog
76from .fabwindow import FABWindow
77from .devicesetupdialog import DeviceSetupDialog
78from .printtestpagedialog import PrintTestPageDialog
79from .infodialog import InfoDialog
80from .cleandialog import CleanDialog
81from .colorcaldialog import ColorCalDialog
82from .linefeedcaldialog import LineFeedCalDialog
83from .pqdiagdialog import PQDiagDialog
84from .nodevicesdialog import NoDevicesDialog
85from .aboutdialog import AboutDialog
86
87# Other forms and controls
88from .settingsdialog import SettingsDialog
89from .printsettingstoolbox import PrintSettingsToolbox
90
91
92from base import os_utils
93
94# all in seconds
95MIN_AUTO_REFRESH_RATE = 5
96MAX_AUTO_REFRESH_RATE = 60
97DEF_AUTO_REFRESH_RATE = 30
98
99
100device_list = {}    # { Device_URI : device.Device(), ... }
101model_obj = models.ModelData() # Used to convert dbus xformed data back to plain Python types
102
103
104# ***********************************************************************************
105#
106# ITEM/UTILITY UI CLASSES
107#
108# ***********************************************************************************
109
110
111class FuncViewItem(QListWidgetItem):
112    def __init__(self, parent, text, pixmap, tooltip_text, cmd):
113        QListWidgetItem.__init__(self, QIcon(pixmap), text, parent)
114        self.tooltip_text = tooltip_text
115        self.cmd = cmd
116
117# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
118
119class DeviceViewItem(QListWidgetItem):
120    def __init__(self, parent, text, pixmap, device_uri, is_avail=True):
121        QListWidgetItem.__init__(self, QIcon(pixmap), text, parent)
122        self.device_uri = device_uri
123        self.is_avail = is_avail
124        self.setTextAlignment(Qt.AlignHCenter)
125
126# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
127
128class PluginInstall(QObject):
129    def __init__(self, parent, plugin_type, plugin_installed):
130        self.parent = parent
131        self.plugin_type = plugin_type
132        self.plugin_installed = plugin_installed
133
134
135    def exec_(self):
136        install_plugin = True
137
138        if self.plugin_installed:
139            install_plugin = QMessageBox.warning(self.parent,
140                                self.parent.windowTitle(),
141                                self.__tr("<b>The HPLIP plugin is already installed.</b><p>Do you want to continue and re-install it?"),
142                                QMessageBox.Yes,
143                                QMessageBox.No,
144                                QMessageBox.NoButton) == QMessageBox.Yes
145
146        if install_plugin:
147            ok, sudo_ok = pkit.run_plugin_command(self.plugin_type == PLUGIN_REQUIRED, self.parent.cur_device.mq['plugin-reason'])
148            if not sudo_ok:
149                QMessageBox.critical(self.parent,
150                    self.parent.windowTitle(),
151                    self.__tr("<b>Unable to find an appropriate su/sudo utility to run hp-plugin.</b><p>Install kdesu, gnomesu, or gksu.</p>"),
152                    QMessageBox.Ok,
153                    QMessageBox.NoButton,
154                    QMessageBox.NoButton)
155
156
157    def __tr(self,s,c = None):
158        return qApp.translate("DevMgr5",s,c)
159
160
161
162# ***********************************************************************************
163#
164# MAINWINDOW
165#
166# ***********************************************************************************
167
168'''
169class Ui_MainWindow_Derived(Ui_MainWindow):
170    def setupUi(self, MainWindow, latest_available_version, Is_autoInstaller_distro):
171        super().setupUi(MainWindow)
172        self.DiagnoseQueueAction = QAction(MainWindow)
173        self.DiagnoseQueueAction.setObjectName("DiagnoseQueueAction")
174        self.DiagnoseHPLIPAction = QAction(MainWindow)
175        self.DiagnoseHPLIPAction.setObjectName("DiagnoseHPLIPAction")
176
177        self.latest_available_version = latest_available_version
178        self.Is_autoInstaller_distro = Is_autoInstaller_distro
179        if self.latest_available_version is not "":
180            self.tab_3 = QWidget()
181            self.tab_3.setObjectName("tab_3")
182            self.label = QLabel(self.tab_3)
183            self.label.setGeometry(QRect(30, 45, 300, 17))
184            self.label.setObjectName("label")
185            if self.Is_autoInstaller_distro:
186                self.InstallLatestButton = QPushButton(self.tab_3)
187                self.InstallLatestButton.setGeometry(QRect(351, 40, 96, 27))
188                self.InstallLatestButton.setObjectName("pushButton")
189            else:
190                self.ManualInstalllabel = QLabel(self.tab_3)
191                self.ManualInstalllabel.setGeometry(QRect(30, 70,300, 45))
192                self.ManualInstalllabel.setObjectName("label")
193                self.InstallLatestButton = QPushButton(self.tab_3)
194                self.InstallLatestButton.setGeometry(QRect(295, 80, 110, 25))
195                self.InstallLatestButton.setObjectName("pushButton")
196            self.Tabs.addTab(self.tab_3, "")
197        # super().setupUi(MainWindow)
198
199    def retranslateUi(self, MainWindow):
200        super().retranslateUi(MainWindow)
201        if self.latest_available_version is not "":
202            self.label.setText(QtGui.QApplication.translate("MainWindow", "New version of HPLIP-%s is available"%self.latest_available_version, None))
203            self.Tabs.setTabText(self.Tabs.indexOf(self.tab_3), QtGui.QApplication.translate("MainWindow", "Upgrade", None))
204            if self.Is_autoInstaller_distro:
205                self.InstallLatestButton.setText(QtGui.QApplication.translate("MainWindow", "Install now", None))
206            else:
207                msg="Please install manually as mentioned in "
208                self.ManualInstalllabel.setText(QtGui.QApplication.translate("MainWindow", msg, None))
209                self.InstallLatestButton.setText(QtGui.QApplication.translate("MainWindow", "HPLIP website", None))
210'''
211
212class DevMgr5(Ui_MainWindow_Derived, Ui_MainWindow, QMainWindow):
213    def __init__(self,  toolbox_version, initial_device_uri=None,
214                 dbus_loop=None, parent=None, name=None, fl=0):
215
216        # QMainWindow.__init__(self, parent)
217        super(DevMgr5, self).__init__(parent)
218
219        log.debug("Initializing toolbox UI (Qt5)...")
220        log.debug("HPLIP Version: %s" % prop.installed_version)
221
222
223        self.toolbox_version = toolbox_version
224        self.initial_device_uri = initial_device_uri
225        self.device_vars = {}
226        self.num_devices = 0
227        self.cur_device = None
228        self.cur_printer = None
229        self.updating = False
230        self.init_failed = False
231        self.service = None
232        self.Is_autoInstaller_distro = False            # True-->tier1(supports auto installation). False--> tier2(manual installation)
233
234        # Distro insformation
235        core =  CoreInstall(MODE_CHECK)
236#        core.init()
237        self.Is_autoInstaller_distro = core.is_auto_installer_support()
238        # User settings
239        self.user_settings = UserSettings()
240        self.user_settings.load()
241        self.user_settings.debug()
242        self.cur_device_uri = self.user_settings.last_used_device_uri
243        installed_version=sys_conf.get('hplip','version')
244        if not utils.Is_HPLIP_older_version( installed_version,  self.user_settings.latest_available_version):
245            self.setupUi(self,"",self.Is_autoInstaller_distro)
246        else:
247            self.setupUi(self, self.user_settings.latest_available_version,self.Is_autoInstaller_distro)
248
249        # Other initialization
250        self.initDBus()
251        self.initPixmaps()
252        self.initMisc()
253        self.initUI()
254
255        cups.setPasswordCallback(showPasswordUI)
256
257        if not prop.doc_build:
258            self.ContentsAction.setEnabled(False)
259
260        self.allow_auto_refresh = True
261        QTimer.singleShot(0, self.initalUpdate)
262
263
264    # ***********************************************************************************
265    #
266    # INIT
267    #
268    # ***********************************************************************************
269
270    # TODO: Make sbus init mandatory success, else exit
271    def initDBus(self):
272        self.dbus_loop = DBusGMainLoop(set_as_default=True)
273        self.dbus_avail, self.service, self.session_bus = device.init_dbus(self.dbus_loop)
274
275        if not self.dbus_avail:
276            log.error("dBus initialization error. Exiting.")
277            self.init_failed = True
278            return
279
280        # Receive events from the session bus
281        self.session_bus.add_signal_receiver(self.handleSessionSignal, sender_keyword='sender',
282            destination_keyword='dest', interface_keyword='interface',
283            member_keyword='member', path_keyword='path')
284
285
286    def initPixmaps(self):
287        self.func_icons_cached = False
288        self.func_icons = {}
289        self.device_icons = {}
290
291         # Application icon
292        self.setWindowIcon(QIcon(load_pixmap('hp_logo', '128x128')))
293
294        self.fax_icon = load_pixmap("fax2", "other")
295
296
297    def initUI(self):
298        # Setup device icon list
299        self.DeviceList.setSortingEnabled(True)
300        self.DeviceList.setContextMenuPolicy(Qt.CustomContextMenu)
301        self.setDeviceListViewMode(QListView.IconMode)
302
303        self.ViewAsIconsAction.triggered.connect(lambda: self.setDeviceListViewMode(QListView.IconMode))
304        self.ViewAsListAction.triggered.connect(lambda: self.setDeviceListViewMode(QListView.ListMode))
305
306        self.DeviceList.customContextMenuRequested["const QPoint &"].connect(self.DeviceList_customContextMenuRequested)
307
308        # Setup main menu
309        self.DeviceRefreshAction.setIcon(QIcon(load_pixmap("refresh1", "16x16")))
310        self.DeviceRefreshAction.triggered.connect(self.DeviceRefreshAction_activated)
311
312        self.RefreshAllAction.setIcon(QIcon(load_pixmap("refresh", "16x16")))
313        self.RefreshAllAction.triggered.connect(self.RefreshAllAction_activated)
314
315        self.SetupDeviceAction.setIcon(QIcon(load_pixmap('list_add', '16x16')))
316        self.SetupDeviceAction.triggered.connect(self.SetupDeviceAction_activated)
317
318        self.RemoveDeviceAction.setIcon(QIcon(load_pixmap('list_remove', '16x16')))
319        self.RemoveDeviceAction.triggered.connect(self.RemoveDeviceAction_activated)
320
321        self.PreferencesAction.setIcon(QIcon(load_pixmap('settings', '16x16')))
322        self.PreferencesAction.triggered.connect(self.PreferencesAction_activated)
323
324        self.DiagnoseQueueAction.setIcon(QIcon(load_pixmap('warning', '16x16')))
325        self.DiagnoseQueueAction.triggered.connect(self.DiagnoseQueueAction_activated)
326
327        self.DiagnoseHPLIPAction.setIcon(QIcon(load_pixmap('troubleshoot', '16x16')))
328        self.DiagnoseHPLIPAction.triggered.connect(self.DiagnoseHPLIP_activated)
329
330        self.ContentsAction.setIcon(QIcon(load_pixmap("help", "16x16")))
331        self.ContentsAction.triggered.connect(self.helpContents)
332
333        self.QuitAction.setIcon(QIcon(load_pixmap("quit", "16x16")))
334        self.QuitAction.triggered.connect(self.quit)
335
336        self.AboutAction.triggered.connect(self.helpAbout)
337
338        self.PrintControlPrinterNameCombo.activated["const QString &"].connect(self.PrintControlPrinterNameCombo_activated)
339        self.PrintSettingsPrinterNameCombo.activated["const QString &"].connect(self.PrintSettingsPrinterNameCombo_activated)
340        signal.signal(signal.SIGINT, signal.SIG_IGN)
341
342
343         # Init tabs/controls
344        self.initActionsTab()
345        self.initStatusTab()
346        self.initSuppliesTab()
347        self.initPrintSettingsTab()
348        self.initPrintControlTab()
349
350
351        self.Tabs.currentChanged[int].connect(self.Tabs_currentChanged)
352
353        # Resize the splitter so that the device list starts as a single column
354        self.splitter.setSizes([80, 600])
355
356        # Setup the Device List
357        self.DeviceList.setIconSize(QSize(60, 60))
358        self.DeviceList.currentItemChanged["QListWidgetItem *", "QListWidgetItem *"].connect(self.DeviceList_currentChanged)
359
360
361    def initMisc(self):
362        self.TabIndex = { 0: self.updateActionsTab,
363                          1: self.updateStatusTab,
364                          2: self.updateSuppliesTab,
365                          3: self.updatePrintSettingsTab,
366                          4: self.updatePrintControlTab,
367                          5:self.updateHPLIPupgrade,
368                        }
369
370        # docs
371        self.docs = "http://hplip.sf.net"
372
373        if prop.doc_build:
374            g = os.path.join(sys_conf.get('dirs', 'doc'), 'index.html')
375            if os.path.exists(g):
376                self.docs = "file://%s" % g
377
378        # support
379        self.support = "https://launchpad.net/hplip"
380
381
382
383    def initalUpdate(self):
384        if self.init_failed:
385            self.close()
386            return
387
388        self.rescanDevices()
389
390        cont = True
391        if self.initial_device_uri is not None:
392            if not self.activateDevice(self.initial_device_uri):
393                log.error("Device %s not found" % self.initial_device_uri)
394                cont = False
395
396        if self.cur_printer:
397            self.getPrinterState()
398
399            if self.printer_state == cups.IPP_PRINTER_STATE_STOPPED:
400                self.cur_device.sendEvent(EVENT_PRINTER_QUEUE_STOPPED, self.cur_printer)
401
402            if not self.printer_accepting:
403                self.cur_device.sendEvent(EVENT_PRINTER_QUEUE_REJECTING_JOBS, self.cur_printer)
404
405
406    def activateDevice(self, device_uri):
407        log.debug(log.bold("Activate: %s %s %s" % ("*"*20, device_uri, "*"*20)))
408        index = 0
409        d = self.DeviceList.item(index) #firstItem()
410        found = False
411
412        while d is not None:
413            if d.device_uri == device_uri:
414                found = True
415                self.DeviceList.setSelected(d, True)
416                self.DeviceList.setCurrentItem(d)
417                break
418
419            index += 1
420            d = self.DeviceList.item(index)
421
422        return found
423
424
425
426    # ***********************************************************************************
427    #
428    # UPDATES/NOTIFICATIONS
429    #
430    # ***********************************************************************************
431
432    def handleSessionSignal(self, *args, **kwds):
433        if kwds['interface'] == 'com.hplip.Toolbox' and \
434            kwds['member'] == 'Event':
435
436            log.debug("Handling event...")
437            event = device.Event(*args[:6])
438            event.debug()
439
440            if event.event_code < EVENT_MIN_USER_EVENT:
441                pass
442
443            elif event.event_code == EVENT_DEVICE_UPDATE_REPLY:
444                log.debug("EVENT_DEVICE_UPDATE_REPLY (%s)" % event.device_uri)
445                dev = self.findDeviceByURI(event.device_uri)
446
447                if dev is not None:
448                    try:
449                        self.service.GetStatus(event.device_uri, reply_handler=self.handleStatusReply,
450                            error_handler=self.handleStatusError)
451
452                    except dbus.exceptions.DBusException as e:
453                        log.error("dbus call to GetStatus() failed.")
454
455            elif event.event_code == EVENT_USER_CONFIGURATION_CHANGED:
456                log.debug("EVENT_USER_CONFIGURATION_CHANGED")
457                self.user_settings.load()
458
459            elif event.event_code == EVENT_HISTORY_UPDATE:
460                log.debug("EVENT_HISTORY_UPDATE (%s)" % event.device_uri)
461                dev = self.findDeviceByURI(event.device_uri)
462                if dev is not None:
463                    self.updateHistory(dev)
464
465            elif event.event_code == EVENT_SYSTEMTRAY_EXIT:
466                log.debug("EVENT_SYSTEMTRAY_EXIT")
467                log.warn("HPLIP Status Service was closed. HPLIP Device Manager will now exit.")
468                cups.releaseCupsInstance()
469                self.close()
470
471            elif event.event_code == EVENT_RAISE_DEVICE_MANAGER:
472                log.debug("EVENT_RAISE_DEVICE_MANAGER")
473                self.showNormal()
474                self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive)
475                self.raise_()
476
477            elif event.event_code in (EVENT_DEVICE_START_POLLING,
478                                      EVENT_DEVICE_STOP_POLLING,
479                                      EVENT_POLLING_REQUEST):
480                pass
481
482            else:
483                log.error("Unhandled event: %d" % event.event_code)
484
485
486    def handleStatusReply(self, device_uri, data):
487        dev = self.findDeviceByURI(device_uri)
488        if dev is not None:
489            t = {}
490            for key in data:
491                value = model_obj.convert_data(str(key), str(data[key]))
492                t.setdefault(key, value)
493
494            dev.dq = t.copy()
495            for d in dev.dq:
496                dev.__dict__[d.replace('-','_')] = dev.dq[d]
497
498            self.updateDevice(dev)
499
500
501    def handleStatusError(self, e):
502        log.error(str(e))
503
504
505    def updateHistory(self, dev=None):
506        if dev is None:
507            dev = self.cur_device
508
509        try:
510            self.service.GetHistory(dev.device_uri, reply_handler=self.handleHistoryReply,
511                                    error_handler=self.handleHistoryError)
512        except dbus.exceptions.DBusException as e:
513            log.error("dbus call to GetHistory() failed.")
514
515
516    def handleHistoryReply(self, device_uri, history):
517        dev = self.findDeviceByURI(device_uri)
518        if dev is not None:
519            result = []
520            history.reverse()
521
522            for h in history:
523                result.append(device.Event(*tuple(h)))
524
525            try:
526                self.error_code = result[0].event_code
527            except IndexError:
528                self.error_code = STATUS_UNKNOWN
529
530            dev.error_state = STATUS_TO_ERROR_STATE_MAP.get(self.error_code, ERROR_STATE_CLEAR)
531            dev.hist = result
532
533            self.updateDevice(dev)
534
535
536    def handleHistoryError(self, e):
537        log.error(str(e))
538
539
540    def sendMessage(self, device_uri, printer_name, event_code, username=prop.username,
541                    job_id=0, title=''):
542
543        device.Event(device_uri, printer_name, event_code, username,
544                    job_id, title).send_via_dbus(self.session_bus)
545
546
547    def timedRefresh(self):
548        if not self.updating and self.user_settings.auto_refresh and self.allow_auto_refresh:
549            log.debug("Refresh timer...")
550            self.cleanupChildren()
551
552            if self.user_settings.auto_refresh_type == 0:
553                self.requestDeviceUpdate()
554            else:
555                self.rescanDevices()
556
557
558    # ***********************************************************************************
559    #
560    # TAB/DEVICE CHANGE SLOTS
561    #
562    # ***********************************************************************************
563
564    def Tabs_currentChanged(self, tab=0):
565        """ Called when the active tab changes.
566            Update newly displayed tab.
567        """
568        if self.cur_device is not None:
569            self.TabIndex[tab]()
570
571    def updateAllTabs(self):
572        for tab in self.TabIndex:
573            self.TabIndex[tab]()
574
575
576    def updateCurrentTab(self):
577        log.debug("updateCurrentTab()")
578        self.TabIndex[self.Tabs.currentIndex()]()
579
580
581
582    # ***********************************************************************************
583    #
584    # DEVICE ICON LIST/DEVICE UPDATE(S)
585    #
586    # ***********************************************************************************
587
588
589    def DeviceRefreshAction_activated(self):
590        self.DeviceRefreshAction.setEnabled(False)
591        self.requestDeviceUpdate()
592        self.DeviceRefreshAction.setEnabled(True)
593
594
595    def RefreshAllAction_activated(self):
596        self.rescanDevices()
597
598
599    def setDeviceListViewMode(self, mode):
600        if mode == QListView.ListMode:
601            self.DeviceList.setViewMode(QListView.ListMode)
602            self.ViewAsListAction.setEnabled(False)
603            self.ViewAsIconsAction.setEnabled(True)
604        else:
605            self.DeviceList.setViewMode(QListView.IconMode)
606            self.ViewAsListAction.setEnabled(True)
607            self.ViewAsIconsAction.setEnabled(False)
608
609
610    def createDeviceIcon(self, dev=None):
611        if dev is None:
612            dev = self.cur_device
613
614        try:
615            dev.icon
616        except AttributeError:
617            dev.icon = "default_printer"
618
619        try:
620            self.device_icons[dev.icon]
621        except:
622            self.device_icons[dev.icon] = load_pixmap(dev.icon, 'devices')
623
624        pix = self.device_icons[dev.icon]
625
626        w, h = pix.width(), pix.height()
627        error_state = dev.error_state
628        icon = QPixmap(w, h)
629        p = QPainter(icon)
630        p.eraseRect(0, 0, icon.width(), icon.height())
631        p.drawPixmap(0, 0, pix)
632
633        try:
634            tech_type = dev.tech_type
635        except AttributeError:
636            tech_type = TECH_TYPE_NONE
637
638        if dev.device_type == DEVICE_TYPE_FAX:
639            p.drawPixmap(w - self.fax_icon.width(), 0, self.fax_icon)
640
641        if error_state != ERROR_STATE_CLEAR:
642            if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
643                status_icon = getStatusOverlayIcon(error_state)[0] # ink
644            else:
645                status_icon = getStatusOverlayIcon(error_state)[1] # laser
646
647            if status_icon is not None:
648                p.drawPixmap(0, 0, status_icon)
649
650        p.end()
651        return icon
652
653
654    def refreshDeviceList(self):
655        global devices
656        log.debug("Rescanning device list...")
657
658        if 1:
659            beginWaitCursor()
660            self.updating = True
661
662            self.setWindowTitle(self.__tr("Refreshing Device List - HP Device Manager"))
663            self.statusBar().showMessage(self.__tr("Refreshing device list..."))
664
665            self.cups_devices = device.getSupportedCUPSDevices(['hp', 'hpfax'])
666
667            current = None
668
669            try:
670                adds = []
671                for d in self.cups_devices:
672                    if d not in device_list:
673                        adds.append(d)
674
675                log.debug("Adds: %s" % ','.join(adds))
676
677                removals = []
678                for d in device_list:
679                    if d not in self.cups_devices:
680                        removals.append(d)
681
682                log.debug("Removals (1): %s" % ','.join(removals))
683
684                updates = []
685                for d in device_list:
686                    if d not in adds and d not in removals:
687                        updates.append(d)
688
689                log.debug("Updates: %s" % ','.join(updates))
690
691                for d in adds:
692                    log.debug("adding: %s" % d)
693                    # Note: Do not perform any I/O with this device.
694                    dev = device.Device(d, service=self.service, disable_dbus=False)
695
696                    if not dev.supported:
697                        log.debug("Unsupported model - removing device.")
698                        removals.append(d)
699                        continue
700
701                    icon = self.createDeviceIcon(dev)
702
703                    if dev.device_type == DEVICE_TYPE_FAX:
704                        DeviceViewItem(self.DeviceList,  self.__tr("%s (Fax)"%dev.model_ui),
705                            icon, d)
706                    else:
707                        if dev.fax_type:
708                            DeviceViewItem(self.DeviceList, self.__tr("%s (Printer)"%dev.model_ui),
709                                icon, d)
710                        else:
711                            DeviceViewItem(self.DeviceList, dev.model_ui,
712                                icon, d)
713
714                    device_list[d] = dev
715
716                log.debug("Removals (2): %s" % ','.join(removals))
717                removed_device=None
718                for d in removals:
719                    removed_device = d
720                    index = self.DeviceList.count()-1
721                    item = self.DeviceList.item(index)
722                    log.debug("removing: %s" % d)
723
724                    try:
725                        del device_list[d]
726                    except KeyError:
727                        pass
728
729                    while index >= 0 and item is not None:
730                        if item.device_uri == d:
731                            self.DeviceList.takeItem(index)
732                            break
733
734                        index -= 1
735                        item = self.DeviceList.item(index)
736
737                    qApp.processEvents()
738
739                self.DeviceList.updateGeometry()
740                qApp.processEvents()
741
742                if len(device_list):
743                    for tab in self.TabIndex:
744                        self.Tabs.setTabEnabled(tab, True)
745
746                    if self.cur_device_uri:
747                        index = 0
748                        item = first_item = self.DeviceList.item(index)
749
750                        while item is not None:
751                            qApp.processEvents()
752                            if item.device_uri == self.cur_device_uri:
753                                current = item
754                                self.statusBar().showMessage(self.cur_device_uri)
755                                break
756
757                            index += 1
758                            item = self.DeviceList.item(index)
759
760                        else:
761                            self.cur_device = None
762                            self.cur_device_uri = ''
763
764                    if self.cur_device is None:
765                        i = self.DeviceList.item(0)
766                        if i is not None:
767                            self.cur_device_uri = i.device_uri
768                            self.cur_device = device_list[self.cur_device_uri]
769                            current = i
770
771                    self.updatePrinterCombos()
772
773                    if self.cur_device_uri:
774                        #user_conf.set('last_used', 'device_uri',self.cur_device_uri)
775                        self.user_settings.last_used_device_uri = self.cur_device_uri
776                        self.user_settings.save()
777
778                    for d in updates + adds:
779                        if d not in removals:
780                            self.requestDeviceUpdate(device_list[d])
781
782                else: # no devices
783                    self.cur_device = None
784                    self.DeviceRefreshAction.setEnabled(False)
785                    self.RemoveDeviceAction.setEnabled(False)
786                    #self.DiagnoseQueueAction.setEnabled(False)
787                    self.updating = False
788                    self.statusBar().showMessage(self.__tr("Press F6 to refresh."))
789
790                    for tab in self.TabIndex:
791                        self.Tabs.setTabEnabled(tab, False)
792
793                    endWaitCursor()
794
795                    dlg = NoDevicesDialog(self)
796                    dlg.exec_()
797
798            finally:
799                self.updating = False
800                endWaitCursor()
801
802            if current is not None:
803                self.DeviceList.setCurrentItem(current)
804
805            self.DeviceRefreshAction.setEnabled(True)
806
807            if self.cur_device is not None:
808                self.RemoveDeviceAction.setEnabled(True)
809                #self.DiagnoseQueueAction.setEnabled(True)
810
811                self.statusBar().showMessage(self.cur_device_uri)
812                self.updateWindowTitle()
813
814
815    def updateWindowTitle(self):
816        if self.cur_device.device_type == DEVICE_TYPE_FAX:
817                self.setWindowTitle(self.__tr("HP Device Manager - %s (Fax)"%self.cur_device.model_ui))
818        else:
819            if self.cur_device.fax_type:
820                self.setWindowTitle(self.__tr("HP Device Manager - %s (Printer)"%self.cur_device.model_ui))
821            else:
822                self.setWindowTitle(self.__tr("HP Device Manager - %s"%self.cur_device.model_ui))
823
824        self.statusBar().showMessage(self.cur_device_uri)
825
826
827    def updateDeviceByURI(self, device_uri):
828        return self.updateDevice(self.findDeviceByURI(device_uri))
829
830
831    def updateDevice(self, dev=None, update_tab=True):
832
833        """ Update the device icon and currently displayed tab.
834        """
835        if dev is None:
836            dev = self.cur_device
837
838        log.debug("updateDevice(%s)" % dev.device_uri)
839
840        item = self.findItem(dev)
841
842        if item is not None:
843            item.setIcon(QIcon(self.createDeviceIcon(dev)))
844
845        if dev is self.cur_device and update_tab:
846            self.updatePrinterCombos()
847            self.updateCurrentTab()
848            self.statusBar().showMessage(self.cur_device_uri)
849            if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
850                self.Tabs.setTabText(self.Tabs.indexOf(self.Settings), QApplication.translate("MainWindow", "Print Settings", None))
851                self.Tabs.setTabText(self.Tabs.indexOf(self.Control), QApplication.translate("MainWindow", "Printer Control", None))
852            else:
853                self.Tabs.setTabText(self.Tabs.indexOf(self.Settings), QApplication.translate("MainWindow", "Fax Settings", None))
854                self.Tabs.setTabText(self.Tabs.indexOf(self.Control), QApplication.translate("MainWindow", "Fax Control", None))
855
856
857    def DeviceList_currentChanged(self, i,  j):
858        if i is not None and not self.updating:
859            self.cur_device_uri = self.DeviceList.currentItem().device_uri
860            self.cur_device = device_list[self.cur_device_uri]
861            #user_conf.set('last_used', 'device_uri', self.cur_device_uri)
862            self.user_settings.last_used_device_uri = self.cur_device_uri
863            self.user_settings.save()
864
865            self.updateDevice()
866            self.updateWindowTitle()
867
868
869    def findItem(self, dev):
870        if dev is None:
871            dev = self.cur_device
872
873        return self.findItemByURI(dev.device_uri)
874
875
876    def findItemByURI(self, device_uri):
877        index = 0
878        item = self.DeviceList.item(index)
879
880        while item is not None:
881            if item.device_uri == device_uri:
882                return item
883
884            index += 1
885            item = self.DeviceList.item(index)
886
887
888    def findDeviceByURI(self, device_uri):
889        try:
890            return device_list[device_uri]
891        except:
892            return None
893
894
895    def requestDeviceUpdate(self, dev=None, item=None):
896        """ Submit device update request to update thread. """
897
898        if dev is None:
899            dev = self.cur_device
900
901        if dev is not None:
902            dev.error_state = ERROR_STATE_REFRESHING
903            self.updateDevice(dev, update_tab=False)
904
905            self.sendMessage(dev.device_uri, '', EVENT_DEVICE_UPDATE_REQUESTED)
906
907
908    def rescanDevices(self):
909        """ Rescan and update all devices. """
910        if not self.updating:
911            self.RefreshAllAction.setEnabled(False)
912            try:
913                self.refreshDeviceList()
914            finally:
915                self.RefreshAllAction.setEnabled(True)
916
917
918    def callback(self):
919        qApp.processEvents()
920
921
922    # ***********************************************************************************
923    #
924    # DEVICE LIST RIGHT CLICK
925    #
926    # ***********************************************************************************
927
928    def DeviceList_customContextMenuRequested(self, p):
929        d = self.cur_device
930
931        if d is not None:
932            avail = d.device_state != DEVICE_STATE_NOT_FOUND and d.supported
933            printer = d.device_type == DEVICE_TYPE_PRINTER and avail
934
935            fax = d.fax_type > FAX_TYPE_NONE and prop.fax_build and d.device_type == DEVICE_TYPE_FAX and \
936                sys.hexversion >= 0x020300f0 and avail
937
938            scan = d.scan_type > SCAN_TYPE_NONE and prop.scan_build and \
939                            printer and self.user_settings.cmd_scan
940
941            cpy = d.copy_type > COPY_TYPE_NONE and printer
942
943            popup = QMenu(self)
944
945            item = self.DeviceList.currentItem()
946            if item is not None:
947                if self.cur_device.error_state != ERROR_STATE_ERROR:
948                    if printer:
949                        popup.addAction(self.__tr("Print..."), lambda: self.contextMenuFunc(PrintDialog(self, self.cur_printer)))
950
951                        if scan:
952                            popup.addAction(self.__tr("Scan..."),  lambda: self.contextMenuFunc(self.user_settings.cmd_scan)) #self.ScanButton_clicked)
953
954                        if cpy:
955                            popup.addAction(self.__tr("Make Copies..."),  lambda: MakeCopiesDialog(self, self.cur_device_uri)) #self.MakeCopiesButton_clicked)
956
957                    else: # self.cur_device.device_type == DEVICE_TYPE_FAX:
958                        if fax:
959                            popup.addAction(self.__tr("Send Fax..."),  lambda: self.contextMenuFunc(SendFaxDialog(self, self.cur_printer, self.cur_device_uri))) #self.SendFaxButton_clicked)
960
961                    popup.addSeparator()
962
963                if not self.updating:
964                    popup.addAction(self.__tr("Refresh Device"),  self.requestDeviceUpdate) #self.DeviceRefreshAction_activated)
965
966            if not self.updating:
967                popup.addAction(self.__tr("Refresh All"),  self.rescanDevices) #self.RefreshAllAction_activated)
968
969            popup.addSeparator()
970
971            if self.DeviceList.viewMode() == QListView.IconMode:
972                popup.addAction(self.__tr("View as List"), lambda: self.setDeviceListViewMode(QListView.ListMode))
973            else:
974                popup.addAction(self.__tr("View as Icons"), lambda: self.setDeviceListViewMode(QListView.IconMode))
975
976            popup.exec_(self.DeviceList.mapToGlobal(p))
977
978
979    def contextMenuFunc(self, f):
980        self.sendMessage('', '', EVENT_DEVICE_STOP_POLLING)
981        try:
982            try:
983                f.exec_() # Dialog
984            except AttributeError:
985                beginWaitCursor()
986
987                if f.split(':')[0] in ('http', 'https', 'file'):
988                    log.debug("Opening browser to: %s" % f)
989                    utils.openURL(f)
990                else:
991                    self.runExternalCommand(f)
992
993                QTimer.singleShot(1000, self.unlockClick)
994        finally:
995            self.sendMessage('', '', EVENT_DEVICE_START_POLLING)
996
997
998
999    # ***********************************************************************************
1000    #
1001    # PRINTER NAME COMBOS
1002    #
1003    # ***********************************************************************************
1004
1005
1006    def updatePrinterCombos(self):
1007        self.PrintSettingsPrinterNameCombo.clear()
1008        self.PrintControlPrinterNameCombo.clear()
1009
1010        if self.cur_device is not None and \
1011            self.cur_device.supported:
1012
1013            self.cur_device.updateCUPSPrinters()
1014
1015            for c in self.cur_device.cups_printers:
1016                self.PrintSettingsPrinterNameCombo.insertItem(0, c)
1017                self.PrintControlPrinterNameCombo.insertItem(0, c)
1018
1019            self.cur_printer = to_unicode(self.PrintSettingsPrinterNameCombo.currentText())
1020
1021
1022    def PrintSettingsPrinterNameCombo_activated(self, s):
1023        self.cur_printer = to_unicode(s)
1024        self.updateCurrentTab()
1025
1026
1027    def PrintControlPrinterNameCombo_activated(self, s):
1028        self.cur_printer = to_unicode(s)
1029        self.updateCurrentTab()
1030
1031
1032
1033    # ***********************************************************************************
1034    #
1035    # FUNCTIONS/ACTION TAB
1036    #
1037    # ***********************************************************************************
1038
1039    def initActionsTab(self):
1040        self.click_lock = None
1041        self.ActionsList.setIconSize(QSize(32, 32))
1042        self.ActionsList.itemClicked["QListWidgetItem *"].connect(self.ActionsList_clicked)
1043        self.ActionsList.itemDoubleClicked["QListWidgetItem *"].connect(self.ActionsList_clicked)
1044
1045
1046    def updateActionsTab(self):
1047        beginWaitCursor()
1048        try:
1049            self.ActionsList.clear()
1050
1051            d = self.cur_device
1052
1053            if d is not None:
1054                avail = d.device_state != DEVICE_STATE_NOT_FOUND and d.supported
1055                fax = d.fax_type > FAX_TYPE_NONE and prop.fax_build and d.device_type == DEVICE_TYPE_FAX and \
1056                    sys.hexversion >= 0x020300f0 and avail
1057                printer = d.device_type == DEVICE_TYPE_PRINTER and avail
1058                scan = d.scan_type > SCAN_TYPE_NONE and prop.scan_build and \
1059                        printer and self.user_settings.cmd_scan
1060                cpy = d.copy_type > COPY_TYPE_NONE and printer
1061                req_plugin = d.plugin == PLUGIN_REQUIRED
1062                opt_plugin = d.plugin == PLUGIN_OPTIONAL
1063
1064                try:
1065                    back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
1066                        device.parseDeviceURI(self.cur_device_uri)
1067                except Error:
1068                    return
1069
1070                hplip_conf = configparser.ConfigParser()
1071                fp = open("/usr/local/etc/hp/hplip.conf", "r")
1072                hplip_conf.readfp(fp)
1073                fp.close()
1074
1075                try:
1076                    plugin_installed = utils.to_bool(hplip_conf.get("hplip", "plugin"))
1077                except configparser.NoOptionError:
1078                    plugin_installed = False
1079
1080                if d.plugin != PLUGIN_NONE:
1081                    if req_plugin and plugin_installed:
1082                        x = self.__tr("Download and install<br>required plugin (already installed).")
1083
1084                    elif req_plugin and not plugin_installed:
1085                        x = self.__tr("Download and install<br>required plugin (needs installation).")
1086
1087                    elif opt_plugin and plugin_installed:
1088                        x = self.__tr("Download and install<br>optional plugin (already installed).")
1089
1090                    elif opt_plugin and not plugin_installed:
1091                        x = self.__tr("Download and install<br>optional plugin (needs installation).")
1092
1093                else:
1094                    x = ''
1095
1096                # TODO: Cache this data structure
1097                #       -- add a field that specifies if the icon should always show, or only when device is avail.
1098                # TODO: Tooltips
1099                # TODO: Right-click icon/list view menu
1100
1101                self.ICONS = [
1102
1103                    # PRINTER
1104
1105                    (lambda : printer,
1106                    self.__tr("Print"),                        # Text
1107                    "print",                                   # Icon
1108                    self.__tr("Print documents or files."),    # Tooltip
1109                    lambda : PrintDialog(self, self.cur_printer)),  # command/action
1110
1111                    (lambda :scan,
1112                    self.__tr("Scan"),
1113                    "scan",
1114                    self.__tr("Scan a document, image, or photograph.<br>"),
1115                    self.user_settings.cmd_scan),
1116
1117                    (lambda : cpy,
1118                    self.__tr("Make Copies"),
1119                    "makecopies",
1120                    self.__tr("Make copies on the device controlled by the PC.<br>"),
1121                    lambda : MakeCopiesDialog(self, self.cur_device_uri)),
1122
1123                    # FAX
1124
1125                    (lambda: fax,
1126                    self.__tr("Send Fax"),
1127                    "fax",
1128                    self.__tr("Send a fax from the PC."),
1129                    lambda : SendFaxDialog(self, self.cur_printer, self.cur_device_uri)),
1130
1131                    (lambda: fax,
1132                    self.__tr("Fax Setup"),
1133                    "fax_setup",
1134                    self.__tr("Fax support must be setup before you can send faxes."),
1135                    lambda : FaxSetupDialog(self, self.cur_device_uri)),
1136
1137                    (lambda: fax and self.user_settings.cmd_fab,
1138                    self.__tr("Fax Address Book"),
1139                    "fab",
1140                    self.__tr("Setup fax phone numbers to use when sending faxes from the PC."),
1141                    self.user_settings.cmd_fab),
1142
1143                    # SETTINGS/TOOLS
1144
1145                    (lambda : d.power_settings != POWER_SETTINGS_NONE and avail,
1146                    self.__tr("Device Settings"),
1147                    "settings",
1148                    self.__tr("Your device has special device settings.<br>You may alter these settings here."),
1149                    lambda : DeviceSetupDialog(self, self.cur_device_uri)),
1150
1151                    (lambda : printer,
1152                    self.__tr("Print Test Page"),
1153                    "testpage",
1154                    self.__tr("Print a test page to test the setup of your printer."),
1155                    lambda : PrintTestPageDialog(self, self.cur_printer)),
1156
1157                     (lambda : True,
1158                    self.__tr("View Printer and Device Information"),
1159                    "cups",
1160                    self.__tr("View information about the device and all its CUPS queues."),
1161                    lambda : InfoDialog(self, self.cur_device_uri)),
1162
1163                    (lambda: printer and d.align_type != ALIGN_TYPE_NONE,
1164                    self.__tr("Align Cartridges (Print Heads)"),
1165                    "align",
1166                    self.__tr("This will improve the quality of output when a new cartridge is installed."),
1167                    lambda : AlignDialog(self, self.cur_device_uri)),
1168
1169                    (lambda: printer and d.clean_type != CLEAN_TYPE_NONE,
1170                    self.__tr("Clean Printheads"),
1171                    "clean",
1172                    self.__tr("You only need to perform this action if you are<br>having problems with poor printout quality due to clogged ink nozzles."),
1173                    lambda : CleanDialog(self, self.cur_device_uri)),
1174
1175                    (lambda: printer and d.color_cal_type != COLOR_CAL_TYPE_NONE and d.color_cal_type == COLOR_CAL_TYPE_TYPHOON,
1176                    self.__tr("Color Calibration"),
1177                    "colorcal",
1178                    self.__tr("Use this procedure to optimimize your printer's color output<br>(requires glossy photo paper)."),
1179                    lambda : ColorCalDialog(self, self.cur_device_uri)),
1180
1181                    (lambda: printer and d.color_cal_type != COLOR_CAL_TYPE_NONE and d.color_cal_type != COLOR_CAL_TYPE_TYPHOON,
1182                    self.__tr("Color Calibration"),
1183                    "colorcal",
1184                    self.__tr("Use this procedure to optimimize your printer's color output."),
1185                    lambda : ColorCalDialog(self, self.cur_device_uri)),
1186
1187                    (lambda: printer and d.linefeed_cal_type != LINEFEED_CAL_TYPE_NONE,
1188                    self.__tr("Line Feed Calibration"),
1189                    "linefeed_cal",
1190                    self.__tr("Use line feed calibration to optimize print quality<br>(to remove gaps in the printed output)."),
1191                    lambda : LineFeedCalDialog(self, self.cur_device_uri)),
1192
1193                    (lambda: printer and d.pq_diag_type != PQ_DIAG_TYPE_NONE,
1194                    self.__tr("Print Diagnostic Page"),
1195                    "pq_diag",
1196                    self.__tr("Your printer can print a test page <br>to help diagnose print quality problems."),
1197                    lambda : PQDiagDialog(self, self.cur_device_uri)),
1198
1199                    (lambda: printer and d.wifi_config >= WIFI_CONFIG_USB_XML and bus == 'usb',
1200                     self.__tr("Wireless/wifi setup using USB"),
1201                     "wireless",
1202                     self.__tr("Configure your wireless capable printer using a temporary USB connection."),
1203                     'hp-wificonfig -d %s' % self.cur_device_uri),
1204
1205                    # FIRMWARE
1206
1207                    (lambda : printer and d.fw_download ,
1208                    self.__tr("Download Firmware"),
1209                    "firmware",
1210                    self.__tr("Download firmware to your printer <br>(required on some devices after each power-up)."),
1211                    lambda : FirmwareDialog(self, self.cur_device_uri)),
1212
1213                    # PLUGIN
1214
1215                    (lambda : printer and req_plugin,
1216                    self.__tr("Install Required Plugin"),
1217                    "plugin",
1218                    x,
1219                    lambda : PluginInstall(self, d.plugin, plugin_installed)),
1220
1221                    (lambda : printer and opt_plugin,
1222                    self.__tr("Install Optional Plugin"),
1223                    "plugin",
1224                    x,
1225                    lambda : PluginInstall(self, d.plugin, plugin_installed)),
1226
1227                    # EWS
1228
1229                    (lambda : printer and d.embedded_server_type > EWS_NONE and bus == 'net',
1230                     self.__tr("Open printer's web page in a browser"),
1231                     "ews",
1232                     self.__tr("The printer's web page has supply, status, and other information."),
1233                     openEWS(host, zc)),
1234
1235                    # HELP/WEBSITE
1236
1237                    (lambda : True,
1238                    self.__tr("Visit HPLIP Support Website"),
1239                    "hp_logo",
1240                    self.__tr("Visit HPLIP Support Website."),
1241                    self.support),
1242
1243                    (lambda : True,
1244                    self.__tr("Help"),
1245                    "help",
1246                    self.__tr("View HPLIP help."),
1247                    self.docs),
1248
1249                ]
1250
1251                if not self.func_icons_cached:
1252                    for filte, text, icon, tooltip, cmd in self.ICONS:
1253                        self.func_icons[icon] = load_pixmap(icon, '32x32')
1254                    self.func_icons_cached = True
1255
1256                for fltr, text, icon, tooltip, cmd in self.ICONS:
1257                    if fltr is not None:
1258                        if not fltr():
1259                            continue
1260
1261                    FuncViewItem(self.ActionsList, text,
1262                        self.func_icons[icon],
1263                        tooltip,
1264                        cmd)
1265        finally:
1266            endWaitCursor()
1267
1268
1269    def ActionsList_clicked(self, item):
1270        if item is not None and self.click_lock is not item:
1271            self.click_lock = item
1272            if item.cmd and isinstance(item.cmd, collections.Callable):
1273                dlg = item.cmd()
1274                self.sendMessage('', '', EVENT_DEVICE_STOP_POLLING)
1275                try:
1276                    dlg.exec_()
1277                finally:
1278                    self.sendMessage('', '', EVENT_DEVICE_START_POLLING)
1279
1280            else:
1281                beginWaitCursor()
1282                if item.cmd.split(':')[0] in ('http', 'https', 'file'):
1283                    log.debug("Opening browser to: %s" % item.cmd)
1284                    utils.openURL(item.cmd)
1285                else:
1286                    self.runExternalCommand(str(item.cmd))
1287
1288            QTimer.singleShot(1000, self.unlockClick)
1289
1290
1291    def unlockClick(self):
1292        self.click_lock = None
1293        endWaitCursor()
1294
1295
1296    def ActionsList_customContextMenuRequested(self, p):
1297        print(p)
1298        #pass
1299
1300
1301    # ***********************************************************************************
1302    #
1303    # STATUS TAB
1304    #
1305    # ***********************************************************************************
1306
1307    def initStatusTab(self):
1308        self.StatusTable.setColumnCount(0)
1309        self.status_headers = [self.__tr(""), self.__tr("Status"), self.__tr("Date and Time"),
1310                               self.__tr("Code"), self.__tr("Job ID"), self.__tr("Description")]
1311
1312
1313    def updateStatusTab(self):
1314        self.updateStatusLCD()
1315        self.updateStatusTable()
1316
1317
1318    def updateStatusLCD(self):
1319        if self.cur_device is not None and \
1320            self.cur_device.hist and \
1321            self.cur_device.supported:
1322
1323            dq = self.cur_device.dq
1324
1325            if dq.get('panel', 0) == 1:
1326                line1 = dq.get('panel-line1', '')
1327                line2 = dq.get('panel-line2', '')
1328            else:
1329                try:
1330                    line1 = device.queryString(self.cur_device.hist[0].event_code)
1331                except (AttributeError, TypeError):
1332                    line1 = ''
1333
1334                line2 = ''
1335
1336            self.drawStatusLCD(line1, line2)
1337
1338        else:
1339            if self.cur_device.status_type == STATUS_TYPE_NONE:
1340                self.drawStatusLCD(self.__tr("Status information not"), self.__tr("available for this device."))
1341
1342            elif not self.cur_device.supported:
1343                self.drawStatusLCD(self.__tr("Device not supported."))
1344
1345            elif not self.cur_device.hist:
1346                self.drawStatusLCD(self.__tr("No status history available."))
1347
1348            else:
1349                self.drawStatusLCD()
1350
1351
1352    def drawStatusLCD(self, line1='', line2=''):
1353        pm = load_pixmap('panel_lcd', 'other')
1354
1355        p = QPainter()
1356        p.begin(pm)
1357        p.setPen(QColor(0, 0, 0))
1358        p.setFont(self.font())
1359
1360        x, y_line1, y_line2 = 10, 17, 33
1361
1362        # TODO: Scroll long lines
1363        if line1:
1364            p.drawText(x, y_line1, line1)
1365
1366        if line2:
1367            p.drawText(x, y_line2, line2)
1368
1369        p.end()
1370
1371        self.LCD.setPixmap(pm)
1372
1373
1374
1375    def updateStatusTable(self):
1376        self.StatusTable.clear()
1377        flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
1378
1379        row = 0
1380        hist = self.cur_device.hist[:]
1381
1382        if hist:
1383            self.StatusTable.setRowCount(len(hist))
1384            self.StatusTable.setColumnCount(len(self.status_headers))
1385            self.StatusTable.setHorizontalHeaderLabels(self.status_headers)
1386            self.StatusTable.verticalHeader().hide()
1387            self.StatusTable.horizontalHeader().show()
1388
1389            hist.reverse()
1390            row = len(hist)-1
1391
1392            for e in hist:
1393                if e is None:
1394                    continue
1395
1396                ess = device.queryString(e.event_code, 0)
1397                esl = device.queryString(e.event_code, 1)
1398
1399                if row == 0:
1400                    desc = self.__tr("(most recent)")
1401
1402                else:
1403                    desc = getTimeDeltaDesc(e.timedate)
1404
1405                dt = QDateTime()
1406                dt.setTime_t(int(e.timedate)) #, Qt.LocalTime)
1407
1408                # TODO: In Qt4.x, use QLocale.toString(date, format)
1409                tt = str("%s %s"%(dt.toString(),desc))
1410
1411                if e.job_id:
1412                    job_id = to_unicode(e.job_id)
1413                else:
1414                    job_id = to_unicode('')
1415
1416                error_state = STATUS_TO_ERROR_STATE_MAP.get(e.event_code, ERROR_STATE_CLEAR)
1417                tech_type = self.cur_device.tech_type
1418
1419                if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
1420                    status_pix = getStatusListIcon(error_state)[0] # ink
1421                else:
1422                    status_pix = getStatusListIcon(error_state)[1] # laser
1423
1424                event_code = to_unicode(e.event_code)
1425
1426                i = QTableWidgetItem(QIcon(status_pix), self.__tr(""))
1427                i.setFlags(flags)
1428                self.StatusTable.setItem(row, 0, i)
1429
1430                for col, t in [(1, ess), (2, tt), (3, event_code), (4, job_id), (5, esl)]:
1431                    i = QTableWidgetItem(str(t))
1432                    i.setFlags(flags)
1433
1434                    self.StatusTable.setItem(row, col, i)
1435
1436                row -= 1
1437
1438            self.StatusTable.resizeColumnsToContents()
1439            self.StatusTable.setColumnWidth(0, 24)
1440
1441        else:
1442            self.StatusTable.setRowCount(1)
1443            self.StatusTable.setColumnCount(2)
1444            self.StatusTable.setHorizontalHeaderLabels(["", ""])
1445            self.StatusTable.verticalHeader().hide()
1446            self.StatusTable.horizontalHeader().hide()
1447
1448            flags = Qt.ItemIsEnabled
1449
1450            pixmap = getStatusListIcon(ERROR_STATE_ERROR)[0]
1451            i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
1452            i.setFlags(flags)
1453            self.StatusTable.setItem(row, 0, i)
1454
1455            i = QTableWidgetItem(self.__tr("Status information not available for this device."))
1456            i.setFlags(flags)
1457            self.StatusTable.setItem(0, 1, i)
1458
1459            self.StatusTable.resizeColumnsToContents()
1460            self.StatusTable.setColumnWidth(0, 24)
1461
1462
1463    # ***********************************************************************************
1464    #
1465    # SUPPLIES TAB
1466    #
1467    # ***********************************************************************************
1468
1469    def initSuppliesTab(self):
1470        self.pix_battery = load_pixmap('battery', '16x16')
1471
1472        yellow = "#ffff00"
1473        light_yellow = "#ffffcc"
1474        cyan = "#00ffff"
1475        light_cyan = "#ccffff"
1476        magenta = "#ff00ff"
1477        light_magenta = "#ffccff"
1478        black = "#000000"
1479        blue = "#0000ff"
1480        gray = "#808080"
1481        dark_gray = "#a9a9a9"
1482        light_gray = "#c0c0c0"
1483        red = "#ff0000"
1484
1485        self.TYPE_TO_PIX_MAP = {
1486                               AGENT_TYPE_UNSPECIFIED : [black],
1487                               AGENT_TYPE_BLACK: [black],
1488                               AGENT_TYPE_MATTE_BLACK : [black],
1489                               AGENT_TYPE_PHOTO_BLACK : [dark_gray],
1490                               AGENT_TYPE_BLACK_B8800: [black],
1491                               AGENT_TYPE_CMY: [cyan, magenta, yellow],
1492                               AGENT_TYPE_KCM: [light_cyan, light_magenta, light_yellow],
1493                               AGENT_TYPE_GGK: [dark_gray],
1494                               AGENT_TYPE_YELLOW: [yellow],
1495                               AGENT_TYPE_MAGENTA: [magenta],
1496                               AGENT_TYPE_CYAN : [cyan],
1497                               AGENT_TYPE_CYAN_LOW: [light_cyan],
1498                               AGENT_TYPE_YELLOW_LOW: [light_yellow],
1499                               AGENT_TYPE_MAGENTA_LOW: [light_magenta],
1500                               AGENT_TYPE_BLUE: [blue],
1501                               AGENT_TYPE_KCMY_CM: [yellow, cyan, magenta],
1502                               AGENT_TYPE_LC_LM: [light_cyan, light_magenta],
1503                               #AGENT_TYPE_Y_M: [yellow, magenta],
1504                               #AGENT_TYPE_C_K: [black, cyan],
1505                               AGENT_TYPE_LG_PK: [light_gray, dark_gray],
1506                               AGENT_TYPE_LG: [light_gray],
1507                               AGENT_TYPE_G: [gray],
1508                               AGENT_TYPE_DG: [dark_gray],
1509                               AGENT_TYPE_PG: [light_gray],
1510                               AGENT_TYPE_C_M: [cyan, magenta],
1511                               AGENT_TYPE_K_Y: [black, yellow],
1512                               AGENT_TYPE_LC: [light_cyan],
1513                               AGENT_TYPE_RED : [red],
1514                               }
1515
1516        self.supplies_headers = [self.__tr(""), self.__tr("Description"),
1517                                 self.__tr("HP Part No."), self.__tr("Approx. Level"),
1518                                 self.__tr("Status")]
1519
1520
1521    def updateSuppliesTab(self):
1522        beginWaitCursor()
1523        flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
1524
1525        try:
1526            self.SuppliesTable.clear()
1527            self.SuppliesTable.setRowCount(0)
1528            self.SuppliesTable.setColumnCount(0)
1529
1530            if self.cur_device is not None and \
1531                self.cur_device.supported and \
1532                self.cur_device.status_type != STATUS_TYPE_NONE and \
1533                self.cur_device.device_state != DEVICE_STATE_NOT_FOUND:
1534                self.cur_device.sorted_supplies = []
1535                a = 1
1536                while True:
1537                    try:
1538                        agent_type = int(self.cur_device.dq['agent%d-type' % a])
1539                        agent_kind = int(self.cur_device.dq['agent%d-kind' % a])
1540                        agent_sku = self.cur_device.dq['agent%d-sku' % a]
1541                    except KeyError:
1542                        break
1543                    else:
1544                        self.cur_device.sorted_supplies.append((a, agent_kind, agent_type, agent_sku))
1545
1546                    a += 1
1547
1548                self.cur_device.sorted_supplies.sort(key=utils.cmp_to_key(utils.levelsCmp))
1549
1550                self.SuppliesTable.setRowCount(len(self.cur_device.sorted_supplies))
1551                self.SuppliesTable.setColumnCount(len(self.supplies_headers))
1552                self.SuppliesTable.setHorizontalHeaderLabels(self.supplies_headers)
1553                self.SuppliesTable.verticalHeader().hide()
1554                self.SuppliesTable.horizontalHeader().show()
1555                self.SuppliesTable.setIconSize(QSize(100, 18))
1556
1557                for row, x in enumerate(self.cur_device.sorted_supplies):
1558                    a, agent_kind, agent_type, agent_sku = x
1559                    try:
1560                        agent_level = int(self.cur_device.dq['agent%d-level' % a])
1561                        agent_desc = self.cur_device.dq['agent%d-desc' % a]
1562                        agent_health_desc = self.cur_device.dq['agent%d-health-desc' % a]
1563                    except KeyError:
1564                        break
1565                    # Bar graph level
1566                    level_pixmap = None
1567                    if agent_kind in (AGENT_KIND_SUPPLY,
1568                                      #AGENT_KIND_HEAD,
1569                                      AGENT_KIND_HEAD_AND_SUPPLY,
1570                                      AGENT_KIND_TONER_CARTRIDGE,
1571                                      AGENT_KIND_MAINT_KIT,
1572                                      AGENT_KIND_ADF_KIT,
1573                                      AGENT_KIND_INT_BATTERY,
1574                                      AGENT_KIND_DRUM_KIT,
1575                                      ):
1576
1577                        level_pixmap = self.createStatusLevelGraphic(agent_level, agent_type)
1578
1579                    # Color icon
1580                    pixmap = None
1581                    if agent_kind in (AGENT_KIND_SUPPLY,
1582                                      AGENT_KIND_HEAD,
1583                                      AGENT_KIND_HEAD_AND_SUPPLY,
1584                                      AGENT_KIND_TONER_CARTRIDGE,
1585                                      #AGENT_KIND_MAINT_KIT,
1586                                      #AGENT_KIND_ADF_KIT,
1587                                      AGENT_KIND_INT_BATTERY,
1588                                      #AGENT_KIND_DRUM_KIT,
1589                                      ):
1590
1591                        pixmap = self.getStatusIcon(agent_kind, agent_type)
1592
1593                    if pixmap is not None:
1594                        i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
1595                        i.setFlags(flags)
1596                        self.SuppliesTable.setItem(row, 0, i)
1597
1598                    for col, t in [(1, agent_desc), (2, agent_sku), (4, agent_health_desc)]:
1599                        i = QTableWidgetItem(str(t))
1600                        i.setFlags(flags)
1601                        self.SuppliesTable.setItem(row, col, i)
1602
1603                    if level_pixmap is not None:
1604                        i = QTableWidgetItem(QIcon(level_pixmap), self.__tr(""))
1605                        i.setFlags(flags)
1606                        self.SuppliesTable.setItem(row, 3, i)
1607
1608                self.SuppliesTable.resizeColumnsToContents()
1609                self.SuppliesTable.setColumnWidth(0, 24)
1610                self.SuppliesTable.setColumnWidth(3, 120)
1611
1612            else: # No supplies info
1613                log.warning("Supplies information not available for this device.")
1614                flags = Qt.ItemIsEnabled
1615                self.SuppliesTable.setRowCount(1)
1616                self.SuppliesTable.setColumnCount(2)
1617                self.SuppliesTable.setHorizontalHeaderLabels(["", ""])
1618                self.SuppliesTable.verticalHeader().hide()
1619                self.SuppliesTable.horizontalHeader().hide()
1620
1621                i = QTableWidgetItem(self.__tr("Supplies information not available for this device."))
1622                i.setFlags(flags)
1623                self.SuppliesTable.setItem(0, 1, i)
1624
1625                pixmap = getStatusListIcon(ERROR_STATE_ERROR)[0]
1626                i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
1627                i.setFlags(flags)
1628                self.SuppliesTable.setItem(0, 0, i)
1629
1630                self.SuppliesTable.resizeColumnsToContents()
1631                self.SuppliesTable.setColumnWidth(0, 24)
1632
1633        finally:
1634            endWaitCursor()
1635
1636
1637    def getStatusIcon(self, agent_kind, agent_type):
1638        if agent_kind in (AGENT_KIND_SUPPLY,
1639                          AGENT_KIND_HEAD,
1640                          AGENT_KIND_HEAD_AND_SUPPLY,
1641                          AGENT_KIND_TONER_CARTRIDGE):
1642
1643            map = self.TYPE_TO_PIX_MAP[agent_type]
1644
1645            if isinstance(map, list):
1646                map_len = len(map)
1647                pix = QPixmap(16, 16)
1648                pix.fill(QColor(0, 0, 0, 0))
1649                p = QPainter()
1650
1651                p.begin(pix)
1652                p.setRenderHint(QPainter.Antialiasing)
1653
1654                if map_len == 1:
1655                    p.setPen(QColor(map[0]))
1656                    p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
1657                    p.drawPie(2, 2, 10, 10, 0, 5760)
1658
1659                elif map_len == 2:
1660                    p.setPen(QColor(map[0]))
1661                    p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
1662                    p.drawPie(2, 4, 8, 8, 0, 5760)
1663
1664                    p.setPen(QColor(map[1]))
1665                    p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
1666                    p.drawPie(6, 4, 8, 8, 0, 5760)
1667
1668                elif map_len == 3:
1669                    p.setPen(QColor(map[2]))
1670                    p.setBrush(QBrush(QColor(map[2]), Qt.SolidPattern))
1671                    p.drawPie(6, 6, 8, 8, 0, 5760)
1672
1673                    p.setPen(QColor(map[1]))
1674                    p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
1675                    p.drawPie(2, 6, 8, 8, 0, 5760)
1676
1677                    p.setPen(QColor(map[0]))
1678                    p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
1679                    p.drawPie(4, 2, 8, 8, 0, 5760)
1680
1681                p.end()
1682                return pix
1683
1684            else:
1685                return map
1686
1687        elif agent_kind == AGENT_KIND_INT_BATTERY:
1688                return self.pix_battery
1689
1690
1691    def createStatusLevelGraphic(self, percent, agent_type, w=100, h=18):
1692        if percent:
1693            fw = w/100*percent
1694        else:
1695            fw = 0
1696
1697        px = QPixmap(w, h)
1698        px.fill(QColor(0, 0, 0, 0))
1699        pp = QPainter()
1700        pp.begin(px)
1701        pp.setRenderHint(QPainter.Antialiasing)
1702        pp.setPen(Qt.black)
1703
1704        map = self.TYPE_TO_PIX_MAP[agent_type]
1705        map_len = len(map)
1706
1707        if map_len == 1 or map_len > 3:
1708            pp.fillRect(0, 0, fw, h, QBrush(QColor(map[0])))
1709
1710        elif map_len == 2:
1711            h2 = h / 2
1712            pp.fillRect(0, 0, fw, h2, QBrush(QColor(map[0])))
1713            pp.fillRect(0, h2, fw, h, QBrush(QColor(map[1])))
1714
1715        elif map_len == 3:
1716            h3 = h / 3
1717            h23 = 2 * h3
1718            pp.fillRect(0, 0, fw, h3, QBrush(QColor(map[0])))
1719            pp.fillRect(0, h3, fw, h23, QBrush(QColor(map[1])))
1720            pp.fillRect(0, h23, fw, h, QBrush(QColor(map[2])))
1721
1722        # draw black frame
1723        pp.drawRect(0, 0, w, h)
1724
1725        if percent > 75 and agent_type in \
1726          (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
1727            pp.setPen(Qt.white)
1728
1729        # 75% ticks
1730        w1 = 3 * w / 4
1731        h6 = h / 6
1732        pp.drawLine(w1, 0, w1, h6)
1733        pp.drawLine(w1, h, w1, h-h6)
1734
1735        if percent > 50 and agent_type in \
1736          (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
1737            pp.setPen(Qt.white)
1738
1739        # 50% ticks
1740        w2 = w / 2
1741        h4 = h / 4
1742        pp.drawLine(w2, 0, w2, h4)
1743        pp.drawLine(w2, h, w2, h-h4)
1744
1745        if percent > 25 and agent_type in \
1746          (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
1747            pp.setPen(Qt.white)
1748
1749        # 25% ticks
1750        w4 = w / 4
1751        pp.drawLine(w4, 0, w4, h6)
1752        pp.drawLine(w4, h, w4, h-h6)
1753
1754        pp.end()
1755
1756        return px
1757
1758
1759
1760    # ***********************************************************************************
1761    #
1762    # PRINTER SETTINGS TAB
1763    #
1764    # ***********************************************************************************
1765
1766    def initPrintSettingsTab(self):
1767        pass
1768
1769
1770    def updatePrintSettingsTab(self):
1771        beginWaitCursor()
1772        try:
1773            if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1774                self.PrintSettingsPrinterNameLabel.setText(self.__tr("Printer Name:"))
1775            else:
1776                self.PrintSettingsPrinterNameLabel.setText(self.__tr("Fax Name:"))
1777
1778            self.PrintSettingsToolbox.updateUi(self.cur_device, self.cur_printer)
1779        finally:
1780            endWaitCursor()
1781
1782
1783    # ***********************************************************************************
1784    #
1785    # PRINTER CONTROL TAB
1786    #
1787    # ***********************************************************************************
1788
1789    def initPrintControlTab(self):
1790        self.JOB_STATES = { cups.IPP_JOB_PENDING : self.__tr("Pending"),
1791                            cups.IPP_JOB_HELD : self.__tr("On hold"),
1792                            cups.IPP_JOB_PROCESSING : self.__tr("Printing"),
1793                            cups.IPP_JOB_STOPPED : self.__tr("Stopped"),
1794                            cups.IPP_JOB_CANCELLED : self.__tr("Canceled"),
1795                            cups.IPP_JOB_ABORTED : self.__tr("Aborted"),
1796                            cups.IPP_JOB_COMPLETED : self.__tr("Completed"),
1797                           }
1798
1799        self.CancelJobButton.setIcon(QIcon(load_pixmap('cancel', '16x16')))
1800        self.RefreshButton.setIcon(QIcon(load_pixmap('refresh', '16x16')))
1801
1802        self.JOB_STATE_ICONS = { cups.IPP_JOB_PENDING: QIcon(load_pixmap("busy", "16x16")),
1803                                 cups.IPP_JOB_HELD : QIcon(load_pixmap("busy", "16x16")),
1804                                 cups.IPP_JOB_PROCESSING : QIcon(load_pixmap("print", "16x16")),
1805                                 cups.IPP_JOB_STOPPED : QIcon(load_pixmap("warning", "16x16")),
1806                                 cups.IPP_JOB_CANCELLED : QIcon(load_pixmap("warning", "16x16")),
1807                                 cups.IPP_JOB_ABORTED : QIcon(load_pixmap("error", "16x16")),
1808                                 cups.IPP_JOB_COMPLETED : QIcon(load_pixmap("ok", "16x16")),
1809                                }
1810
1811        self.StartStopButton.clicked.connect(self.StartStopButton_clicked)
1812        self.AcceptRejectButton.clicked.connect(self.AcceptRejectButton_clicked)
1813        self.SetDefaultButton.clicked.connect(self.SetDefaultButton_clicked)
1814        self.CancelJobButton.clicked.connect(self.CancelJobButton_clicked)
1815        self.RefreshButton.clicked.connect(self.RefreshButton_clicked)
1816
1817        self.job_headers = [self.__tr("Status"), self.__tr("Title/Description"), self.__tr("Job ID")]
1818
1819        # TODO: Check queues at startup and send events if stopped or rejecting
1820
1821
1822    def initUpgradeTab(self):
1823        self.InstallLatestButton.clicked.connect(self.InstallLatestButton_clicked)
1824        self.InstallLatestButton_lock = False
1825
1826
1827    def InstallLatestButton_clicked(self):
1828        if self.InstallLatestButton_lock is True:
1829            return
1830        if self.Is_autoInstaller_distro:
1831            self.InstallLatestButton.setEnabled(False)
1832            terminal_cmd = utils.get_terminal()
1833            if terminal_cmd is not None and utils.which("hp-upgrade"):
1834                cmd = terminal_cmd + " 'hp-upgrade -w'"
1835                os_utils.execute(cmd)
1836            else:
1837                log.error("Failed to run hp-upgrade command from terminal =%s "%terminal_cmd)
1838            self.InstallLatestButton.setEnabled(True)
1839        else:
1840            self.InstallLatestButton_lock = True
1841            utils.openURL("http://hplipopensource.com/hplip-web/install/manual/index.html")
1842            QTimer.singleShot(1000, self.InstallLatestButton_unlock)
1843
1844
1845    def InstallLatestButton_unlock(self):
1846        self.InstallLatestButton_lock = False
1847
1848
1849    def CancelJobButton_clicked(self):
1850        item = self.JobTable.currentItem()
1851
1852        if item is not None:
1853            job_id, ok = value_int(item.data(Qt.UserRole))
1854            if ok and job_id:
1855               self.cur_device.cancelJob(job_id)
1856               QTimer.singleShot(1000, self.updatePrintControlTab)
1857
1858
1859    def RefreshButton_clicked(self):
1860        self.updatePrintControlTab()
1861
1862    def  updateHPLIPupgrade(self):
1863        self.initUpgradeTab()
1864
1865
1866
1867
1868    def updatePrintControlTab(self):
1869        if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1870            self.PrintControlPrinterNameLabel.setText(self.__tr("Printer Name:"))
1871            self.groupBox.setTitle(QApplication.translate("MainWindow", "Printer Queue Control", None))
1872        else:
1873            self.PrintControlPrinterNameLabel.setText(self.__tr("Fax Name:"))
1874            self.groupBox.setTitle(QApplication.translate("MainWindow", "Fax Queue Control", None))
1875
1876        self.JobTable.clear()
1877        self.JobTable.setRowCount(0)
1878        self.JobTable.setColumnCount(0)
1879        self.updatePrintController()
1880        flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
1881        jobs = cups.getJobs()
1882        num_jobs = 0
1883        for j in jobs:
1884            if j.dest == self.cur_printer:
1885                num_jobs += 1
1886
1887        if num_jobs:
1888            self.CancelJobButton.setEnabled(True)
1889            self.JobTable.setRowCount(num_jobs)
1890            self.JobTable.setColumnCount(len(self.job_headers))
1891            self.JobTable.setHorizontalHeaderLabels(self.job_headers)
1892
1893            for row, j in enumerate(jobs):
1894                if j.dest == self.cur_printer:
1895                    i = QTableWidgetItem(self.JOB_STATE_ICONS[j.state], self.JOB_STATES[j.state])
1896                    i.setData(Qt.UserRole, j.id)
1897                    i.setFlags(flags)
1898                    self.JobTable.setItem(row, 0, i)
1899
1900                    i = QTableWidgetItem(j.title)
1901                    i.setFlags(flags)
1902                    self.JobTable.setItem(row, 1, i)
1903
1904                    i = QTableWidgetItem(to_unicode(j.id))
1905                    i.setFlags(flags)
1906                    self.JobTable.setItem(row, 2, i)
1907
1908
1909            self.JobTable.setCurrentCell(0, 0)
1910            self.JobTable.resizeColumnsToContents()
1911
1912        else:
1913            self.CancelJobButton.setEnabled(False)
1914
1915
1916    def getPrinterState(self):
1917        self.printer_state = cups.IPP_PRINTER_STATE_IDLE
1918        self.printer_accepting = True
1919        cups_printers = cups.getPrinters()
1920
1921        for p in cups_printers:
1922            if p.name == self.cur_printer:
1923                self.printer_state = p.state
1924                self.printer_accepting = p.accepting
1925                break
1926
1927
1928    def updatePrintController(self):
1929        # default printer
1930        self.SetDefaultButton.setText(self.__tr("Set as Default"))
1931
1932        default_printer = cups.getDefaultPrinter()
1933
1934        if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1935            device_string = "Printer"
1936        else:
1937            device_string = "Fax"
1938
1939        if default_printer == self.cur_printer:
1940            self.SetDefaultLabel.setText(self.__tr("Default %s"%device_string))
1941            self.SetDefaultIcon.setPixmap(load_pixmap("ok", "16x16"))
1942            self.SetDefaultButton.setEnabled(False)
1943
1944        else:
1945            self.SetDefaultLabel.setText(self.__tr("Not Default %s"%device_string))
1946            self.SetDefaultIcon.setPixmap(load_pixmap("info", "16x16"))
1947            self.SetDefaultButton.setEnabled(True)
1948
1949        self.getPrinterState()
1950
1951        # start/stop
1952        if self.printer_state == cups.IPP_PRINTER_STATE_IDLE:
1953            self.StartStopLabel.setText(self.__tr("Started/Idle"))
1954            self.StartStopIcon.setPixmap(load_pixmap("idle", "16x16"))
1955            self.StartStopButton.setText(self.__tr("Stop %s"%device_string))
1956
1957
1958        elif self.printer_state == cups.IPP_PRINTER_STATE_PROCESSING:
1959            self.StartStopLabel.setText(self.__tr("Started/Processing"))
1960            self.StartStopIcon.setPixmap(load_pixmap("busy", "16x16"))
1961            self.StartStopButton.setText(self.__tr("Stop %s"%device_string))
1962
1963        else:
1964            self.StartStopLabel.setText(self.__tr("Stopped"))
1965            self.StartStopIcon.setPixmap(load_pixmap("warning", "16x16"))
1966            self.StartStopButton.setText(self.__tr("Start %s"%device_string))
1967
1968        # reject/accept
1969        if self.printer_accepting:
1970            self.AcceptRejectLabel.setText(self.__tr("Accepting Jobs"))
1971            self.AcceptRejectIcon.setPixmap(load_pixmap("idle", "16x16"))
1972            self.AcceptRejectButton.setText(self.__tr("Reject Jobs"))
1973
1974        else:
1975            self.AcceptRejectLabel.setText(self.__tr("Rejecting Jobs"))
1976            self.AcceptRejectIcon.setPixmap(load_pixmap("warning", "16x16"))
1977            self.AcceptRejectButton.setText(self.__tr("Accept Jobs"))
1978
1979
1980
1981    def StartStopButton_clicked(self):
1982        beginWaitCursor()
1983        try:
1984            if self.printer_state in (cups.IPP_PRINTER_STATE_IDLE, cups.IPP_PRINTER_STATE_PROCESSING):
1985                result, result_str = cups.cups_operation(cups.stop, GUI_MODE, 'qt4', self, self.cur_printer)
1986                if result == cups.IPP_OK:
1987                    if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1988                        e = EVENT_PRINTER_QUEUE_STOPPED
1989                    else:
1990                        e = EVENT_FAX_QUEUE_STOPPED
1991
1992            else:
1993                result, result_str = cups.cups_operation(cups.start, GUI_MODE, 'qt4', self, self.cur_printer)
1994                if result == cups.IPP_OK:
1995                    if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
1996                        e = EVENT_PRINTER_QUEUE_STARTED
1997                    else:
1998                        e = EVENT_FAX_QUEUE_STARTED
1999
2000            if result == cups.IPP_OK:
2001                self.updatePrintController()
2002                self.cur_device.sendEvent(e, self.cur_printer)
2003            else:
2004                FailureUI(self, self.__tr("<b>Start/Stop printer queue operation fails. </b><p>Error : %s"%result_str))
2005                cups.releaseCupsInstance()
2006
2007        finally:
2008            endWaitCursor()
2009
2010
2011
2012    def AcceptRejectButton_clicked(self):
2013        beginWaitCursor()
2014        try:
2015            if self.printer_accepting:
2016                result, result_str = cups.cups_operation(cups.reject, GUI_MODE, 'qt4', self, self.cur_printer)
2017                if result == cups.IPP_OK:
2018                    if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2019                        e = EVENT_PRINTER_QUEUE_REJECTING_JOBS
2020                    else:
2021                        e = EVENT_FAX_QUEUE_REJECTING_JOBS
2022
2023            else:
2024                result, result_str = cups.cups_operation(cups.accept, GUI_MODE, 'qt4', self, self.cur_printer)
2025                if result == cups.IPP_OK:
2026                    if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2027                        e = EVENT_PRINTER_QUEUE_ACCEPTING_JOBS
2028                    else:
2029                        e = EVENT_FAX_QUEUE_ACCEPTING_JOBS
2030
2031            if result == cups.IPP_OK:
2032                self.updatePrintController()
2033                self.cur_device.sendEvent(e, self.cur_printer)
2034            else:
2035                FailureUI(self, self.__tr("<b>Accept/Reject printer queue operation fails.</b><p>Error : %s"%result_str))
2036                cups.releaseCupsInstance()
2037
2038        finally:
2039            endWaitCursor()
2040
2041
2042
2043    def SetDefaultButton_clicked(self):
2044        beginWaitCursor()
2045        try:
2046            result, result_str = cups.cups_operation(cups.setDefaultPrinter, GUI_MODE, 'qt4', self, self.cur_printer.encode('utf8'))
2047            if result != cups.IPP_OK:
2048                FailureUI(self, self.__tr("<b>Set printer queue as default operation fails. </b><p>Error : %s"%result_str))
2049                cups.releaseCupsInstance()
2050            else:
2051                self.updatePrintController()
2052                if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
2053                    e = EVENT_PRINTER_QUEUE_SET_AS_DEFAULT
2054                else:
2055                    e = EVENT_FAX_QUEUE_SET_AS_DEFAULT
2056
2057                self.cur_device.sendEvent(e, self.cur_printer)
2058
2059        finally:
2060            endWaitCursor()
2061
2062
2063
2064    def cancelCheckedJobs(self):
2065        beginWaitCursor()
2066        try:
2067            item = self.JobTable.firstChild()
2068            while item is not None:
2069                if item.isOn():
2070                    self.cur_device.cancelJob(item.job_id)
2071
2072                item = item.nextSibling()
2073
2074        finally:
2075            endWaitCursor()
2076
2077
2078        self.updatePrintControlTab()
2079
2080
2081
2082
2083    # ***********************************************************************************
2084    #
2085    # EXIT/CHILD CLEANUP
2086    #
2087    # ***********************************************************************************
2088
2089    def closeEvent(self, event):
2090        self.cleanup()
2091        event.accept()
2092
2093
2094    def cleanup(self):
2095        self.cleanupChildren()
2096
2097
2098    def cleanupChildren(self):
2099        log.debug("Cleaning up child processes.")
2100        try:
2101            os.waitpid(-1, os.WNOHANG)
2102        except OSError:
2103            pass
2104
2105
2106    def quit(self):
2107        self.cleanupChildren()
2108        cups.releaseCupsInstance()
2109        self.close()
2110
2111
2112    # ***********************************************************************************
2113    #
2114    # DEVICE SETTINGS PLUGIN
2115    #
2116    # ***********************************************************************************
2117
2118
2119    # ***********************************************************************************
2120    #
2121    # SETTINGS DIALOG
2122    #
2123    # ***********************************************************************************
2124
2125    def PreferencesAction_activated(self, tab_to_show=0):
2126        dlg = SettingsDialog(self)
2127        dlg.TabWidget.setCurrentIndex(tab_to_show)
2128
2129        if dlg.exec_() == QDialog.Accepted:
2130            self.user_settings.load()
2131
2132            if self.cur_device is not None:
2133                self.cur_device.sendEvent(EVENT_USER_CONFIGURATION_CHANGED, self.cur_printer)
2134
2135
2136    # ***********************************************************************************
2137    #
2138    # SETUP/REMOVE
2139    #
2140    # ***********************************************************************************
2141
2142    def SetupDeviceAction_activated(self):
2143        if utils.which('hp-setup'):
2144            cmd = 'hp-setup --gui'
2145        else:
2146            cmd = 'python ./setup.py --gui'
2147
2148        log.debug(cmd)
2149        utils.run(cmd)
2150        self.rescanDevices()
2151        self.updatePrinterCombos()
2152
2153
2154    def RemoveDeviceAction_activated(self):
2155        if utils.which('hp-setup'):
2156            cmd = 'hp-setup --gui --remove'
2157        else:
2158            cmd = 'python ./setup.py --gui --remove'
2159
2160        if self.cur_device_uri is not None:
2161            cmd += ' --device=%s' % self.cur_device_uri
2162
2163        log.debug(cmd)
2164        utils.run(cmd)
2165        self.rescanDevices()
2166        self.updatePrinterCombos()
2167
2168
2169    def DiagnoseQueueAction_activated(self):
2170        if utils.which('hp-diagnose_queues'):
2171            cmd= 'hp-diagnose_queues --gui'
2172        else:
2173            cmd= 'python ./diagnose_queues.py --gui'
2174        log.debug(cmd)
2175#        ok, output = utils.run(cmd)
2176        os_utils.execute(cmd)
2177
2178
2179    def DiagnoseHPLIP_activated(self):
2180        if utils.which('hp-doctor'):
2181            cmd = 'hp-doctor -i -w'
2182        else:
2183            cmd = 'python ./doctor.py -i -w'
2184
2185        terminal_cmd = utils.get_terminal()
2186        if terminal_cmd:
2187            cmd = terminal_cmd + " '%s'"%cmd
2188            os_utils.execute(cmd)
2189
2190
2191
2192    # ***********************************************************************************
2193    #
2194    # MISC
2195    #
2196    # ***********************************************************************************
2197
2198    def runExternalCommand(self, cmd, macro_char='%'):
2199        beginWaitCursor()
2200
2201        try:
2202            if len(cmd) == 0:
2203                FailureUI(self,self.__tr("<p><b>Unable to run command. No command specified.</b><p>Use <pre>Configure...</pre> to specify a command to run."))
2204                log.error("No command specified. Use settings to configure commands.")
2205            else:
2206                log.debug("Run: %s %s (%s) %s" % ("*"*20, cmd, self.cur_device_uri, "*"*20))
2207                log.debug(cmd)
2208
2209                try:
2210                    cmd = ''.join([self.cur_device.device_vars.get(x, x) \
2211           for x in cmd.split(macro_char)])
2212                except AttributeError:
2213                    pass
2214
2215                log.debug(cmd)
2216
2217                path = cmd.split()[0]
2218                args = cmd.split()
2219
2220                log.debug(path)
2221                log.debug(args)
2222
2223                self.cleanupChildren()
2224                os.spawnvp(os.P_NOWAIT, path, args)
2225                qApp.processEvents()
2226
2227        finally:
2228            endWaitCursor()
2229
2230
2231    def helpContents(self):
2232        utils.openURL(self.docs)
2233
2234
2235    def helpAbout(self):
2236        dlg = AboutDialog(self, prop.version, self.toolbox_version + " (Qt4)")
2237        dlg.exec_()
2238
2239
2240    def __tr(self,s,c = None):
2241        return qApp.translate("DevMgr5",s,c)
2242
2243
2244# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2245
2246class PasswordDialog(QDialog):
2247    def __init__(self, prompt, parent=None, name=None, modal=0, fl=0):
2248        QDialog.__init__(self, parent)
2249        self.prompt = prompt
2250
2251        Layout= QGridLayout(self)
2252        Layout.setMargin(11)
2253        Layout.setSpacing(6)
2254
2255        self.PromptTextLabel = QLabel(self)
2256        Layout.addWidget(self.PromptTextLabel,0,0,1,3)
2257
2258        self.UsernameTextLabel = QLabel(self)
2259        Layout.addWidget(self.UsernameTextLabel,1,0)
2260
2261        self.UsernameLineEdit = QLineEdit(self)
2262        self.UsernameLineEdit.setEchoMode(QLineEdit.Normal)
2263        Layout.addWidget(self.UsernameLineEdit,1,1,1,2)
2264
2265        self.PasswordTextLabel = QLabel(self)
2266        Layout.addWidget(self.PasswordTextLabel,2,0)
2267
2268        self.PasswordLineEdit = QLineEdit(self)
2269        self.PasswordLineEdit.setEchoMode(QLineEdit.Password)
2270        Layout.addWidget(self.PasswordLineEdit,2,1,1,2)
2271
2272        self.OkPushButton = QPushButton(self)
2273        Layout.addWidget(self.OkPushButton,3,2)
2274
2275        self.languageChange()
2276
2277        self.resize(QSize(420,163).expandedTo(self.minimumSizeHint()))
2278
2279        self.OkPushButton.clicked.connect(self.accept)
2280        self.PasswordLineEdit.returnPressed.connect(self.accept)
2281
2282
2283    def getUsername(self):
2284        return to_unicode(self.UsernameLineEdit.text())
2285
2286
2287    def getPassword(self):
2288        return to_unicode(self.PasswordLineEdit.text())
2289
2290
2291    def languageChange(self):
2292        self.setWindowTitle(self.__tr("HP Device Manager - Enter Username/Password"))
2293        self.PromptTextLabel.setText(self.__tr(self.prompt))
2294        self.UsernameTextLabel.setText(self.__tr("Username:"))
2295        self.PasswordTextLabel.setText(self.__tr("Password:"))
2296        self.OkPushButton.setText(self.__tr("OK"))
2297
2298
2299    def __tr(self,s,c = None):
2300        return qApp.translate("DevMgr5",s,c)
2301
2302# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2303
2304def showPasswordUI(prompt):
2305    try:
2306        dlg = PasswordDialog(prompt, None)
2307
2308        if dlg.exec_() == QDialog.Accepted:
2309            return (dlg.getUsername(), dlg.getPassword())
2310
2311    finally:
2312        pass
2313
2314    return ("", "")
2315
2316
2317def openEWS(host, zc):
2318    if zc:
2319        status, ip = hpmudext.get_zc_ip_address(zc)
2320        if status != hpmudext.HPMUD_R_OK:
2321            ip = "hplipopensource.com"
2322    else:
2323        ip = host
2324    return "http://%s" % ip
2325