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