home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / system-config-printer / jobviewer.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-10-12  |  44.9 KB  |  1,546 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. import authconn
  5. import cups
  6. import dbus
  7. import dbus.glib as dbus
  8. import dbus.service as dbus
  9. import pynotify
  10. import gettext
  11. import gobject
  12. import gtk
  13. import gtk.gdk as gtk
  14. import gtk.glade as gtk
  15. from glade import GtkGUI
  16. import monitor
  17. import os
  18. import pango
  19. import pwd
  20. import smburi
  21. import subprocess
  22. import sys
  23. import time
  24. import urllib
  25. from debug import *
  26. import config
  27. import statereason
  28. import errordialogs
  29. import pprint
  30.  
  31. try:
  32.     import gnomekeyring
  33.     USE_KEYRING = True
  34. except ImportError:
  35.     USE_KEYRING = False
  36.  
  37. from gettext import gettext as _
  38. DOMAIN = 'system-config-printer'
  39. gettext.textdomain(DOMAIN)
  40. gtk.glade.textdomain(DOMAIN)
  41. from statereason import StateReason
  42. statereason.set_gettext_function(_)
  43. errordialogs.set_gettext_function(_)
  44. pkgdata = config.pkgdatadir
  45. GLADE = 'applet.glade'
  46. ICON = 'printer'
  47. SEARCHING_ICON = 'document-print-preview'
  48. pynotify.init('System Config Printer Notification')
  49.  
  50. class PrinterURIIndex:
  51.     
  52.     def __init__(self, names = None):
  53.         self.printer = { }
  54.         self.names = names
  55.  
  56.     
  57.     def update_from_attrs(self, printer, attrs):
  58.         uris = []
  59.         if attrs.has_key('printer-uri-supported'):
  60.             uri_supported = attrs['printer-uri-supported']
  61.             if type(uri_supported) != list:
  62.                 uri_supported = [
  63.                     uri_supported]
  64.             
  65.             uris.extend(uri_supported)
  66.         
  67.         if attrs.has_key('notify-printer-uri'):
  68.             uris.append(attrs['notify-printer-uri'])
  69.         
  70.         if attrs.has_key('printer-more-info'):
  71.             uris.append(attrs['printer-more-info'])
  72.         
  73.         for uri in uris:
  74.             self.printer[uri] = printer
  75.         
  76.  
  77.     
  78.     def remove_printer(self, printer):
  79.         uris = self.printer.keys()
  80.         for uri in uris:
  81.             if self.printer[uri] == printer:
  82.                 del self.printer[uri]
  83.                 continue
  84.         
  85.  
  86.     
  87.     def lookup(self, uri, connection = None):
  88.         
  89.         try:
  90.             return self.printer[uri]
  91.         except KeyError:
  92.             if connection == None:
  93.                 connection = cups.Connection()
  94.             
  95.             r = [
  96.                 'printer-name',
  97.                 'printer-uri-supported',
  98.                 'printer-more-info']
  99.             
  100.             try:
  101.                 attrs = connection.getPrinterAttributes(uri = uri, requested_attributes = r)
  102.             except cups.IPPError:
  103.                 raise KeyError
  104.  
  105.             name = attrs['printer-name']
  106.             self.update_from_attrs(name, attrs)
  107.             self.printer[uri] = name
  108.             
  109.             try:
  110.                 return self.printer[uri]
  111.             except KeyError:
  112.                 pass
  113.             except:
  114.                 None<EXCEPTION MATCH>KeyError
  115.             
  116.  
  117.             None<EXCEPTION MATCH>KeyError
  118.  
  119.         raise KeyError
  120.  
  121.  
  122.  
  123. class JobViewer(GtkGUI, monitor.Watcher):
  124.     required_job_attributes = set([
  125.         'job-k-octets',
  126.         'job-name',
  127.         'job-originating-user-name',
  128.         'job-printer-uri',
  129.         'job-state',
  130.         'time-at-creation'])
  131.     
  132.     def __init__(self, bus = None, loop = None, service_running = False, trayicon = False, suppress_icon_hide = False, my_jobs = True, specific_dests = None, exit_handler = None, parent = None):
  133.         self.loop = loop
  134.         self.service_running = service_running
  135.         self.trayicon = trayicon
  136.         self.suppress_icon_hide = suppress_icon_hide
  137.         self.my_jobs = my_jobs
  138.         self.specific_dests = specific_dests
  139.         self.exit_handler = exit_handler
  140.         self.jobs = { }
  141.         self.jobiters = { }
  142.         self.active_jobs = set()
  143.         self.stopped_job_prompts = set()
  144.         self.printer_state_reasons = { }
  145.         self.num_jobs_when_hidden = 0
  146.         self.connecting_to_device = { }
  147.         self.state_reason_notifications = { }
  148.         self.auth_info_dialogs = { }
  149.         self.job_creation_times_timer = None
  150.         self.special_status_icon = False
  151.         self.new_printer_notifications = { }
  152.         self.completed_job_notifications = { }
  153.         self.authenticated_jobs = set()
  154.         self.getWidgets({
  155.             'JobsWindow': [
  156.                 'JobsWindow',
  157.                 'job_menubar_item',
  158.                 'treeview',
  159.                 'statusbar'],
  160.             'statusicon_popupmenu': [
  161.                 'statusicon_popupmenu'] })
  162.         job_action_group = gtk.ActionGroup('JobActionGroup')
  163.         job_action_group.add_actions([
  164.             ('cancel-job', gtk.STOCK_CANCEL, None, None, None, self.on_job_cancel_activate),
  165.             ('hold-job', gtk.STOCK_MEDIA_PAUSE, _('_Hold'), None, None, self.on_job_hold_activate),
  166.             ('release-job', gtk.STOCK_MEDIA_PLAY, _('_Release'), None, None, self.on_job_release_activate),
  167.             ('reprint-job', gtk.STOCK_REDO, _('Re_print'), None, None, self.on_job_reprint_activate),
  168.             ('authenticate-job', None, _('_Authenticate'), None, None, self.on_job_authenticate_activate)])
  169.         self.job_ui_manager = gtk.UIManager()
  170.         self.job_ui_manager.insert_action_group(job_action_group, -1)
  171.         self.job_ui_manager.add_ui_from_string('\n<ui>\n <accelerator action="cancel-job"/>\n <accelerator action="hold-job"/>\n <accelerator action="release-job"/>\n <accelerator action="reprint-job"/>\n <accelerator action="authenticate-job"/>\n</ui>\n')
  172.         self.job_ui_manager.ensure_update()
  173.         self.JobsWindow.add_accel_group(self.job_ui_manager.get_accel_group())
  174.         self.job_context_menu = gtk.Menu()
  175.         for action_name in [
  176.             'cancel-job',
  177.             'hold-job',
  178.             'release-job',
  179.             'reprint-job',
  180.             None,
  181.             'authenticate-job']:
  182.             if not action_name:
  183.                 item = gtk.SeparatorMenuItem()
  184.             else:
  185.                 action = job_action_group.get_action(action_name)
  186.                 action.set_sensitive(False)
  187.                 item = action.create_menu_item()
  188.             item.show()
  189.             self.job_context_menu.append(item)
  190.         
  191.         self.job_menubar_item.set_submenu(self.job_context_menu)
  192.         text = 0
  193.         for name in [
  194.             _('Job'),
  195.             _('User'),
  196.             _('Document'),
  197.             _('Printer'),
  198.             _('Size'),
  199.             _('Time submitted'),
  200.             _('Status')]:
  201.             if text == 1 and trayicon:
  202.                 text += 1
  203.                 continue
  204.             
  205.             cell = gtk.CellRendererText()
  206.             if text == 2 or text == 3:
  207.                 cell.set_property('ellipsize', pango.ELLIPSIZE_END)
  208.                 cell.set_property('width-chars', 20)
  209.             
  210.             column = gtk.TreeViewColumn(name, cell, text = text)
  211.             column.set_resizable(True)
  212.             self.treeview.append_column(column)
  213.             text += 1
  214.         
  215.         self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
  216.         self.store = gtk.TreeStore(int, str, str, str, str, str, str)
  217.         self.store.set_sort_column_id(0, gtk.SORT_DESCENDING)
  218.         self.treeview.set_model(self.store)
  219.         self.treeview.set_rules_hint(True)
  220.         self.treeview.connect('button_release_event', self.on_treeview_button_release_event)
  221.         self.treeview.connect('popup-menu', self.on_treeview_popup_menu)
  222.         self.treeview.connect('cursor-changed', self.on_treeview_cursor_changed)
  223.         self.JobsWindow.set_icon_name(ICON)
  224.         self.JobsWindow.hide()
  225.         if specific_dests:
  226.             the_dests = reduce((lambda x, y: x + ', ' + y), specific_dests)
  227.         
  228.         if my_jobs:
  229.             if specific_dests:
  230.                 title = _('my jobs on %s') % the_dests
  231.             else:
  232.                 title = _('my jobs')
  233.         elif specific_dests:
  234.             title = '%s' % the_dests
  235.         else:
  236.             title = _('all jobs')
  237.         self.JobsWindow.set_title(_('Document Print Status (%s)') % title)
  238.         if parent:
  239.             self.JobsWindow.set_transient_for(parent)
  240.         
  241.         self.statusbar_set = False
  242.         if self.trayicon:
  243.             self.statusicon = gtk.StatusIcon()
  244.             theme = gtk.icon_theme_get_default()
  245.             pixbuf = theme.load_icon(ICON, 22, 0)
  246.             self.statusicon.set_from_pixbuf(pixbuf)
  247.             self.icon_jobs = self.statusicon.get_pixbuf()
  248.             self.icon_jobs_processing = theme.load_icon('printer-printing', 22, 0)
  249.             self.icon_no_jobs = self.icon_jobs.copy()
  250.             self.icon_no_jobs.fill(0)
  251.             self.icon_jobs.composite(self.icon_no_jobs, 0, 0, self.icon_no_jobs.get_width(), self.icon_no_jobs.get_height(), 0, 0, 1, 1, gtk.gdk.INTERP_BILINEAR, 127)
  252.             self.set_statusicon_from_pixbuf(self.icon_no_jobs)
  253.             self.statusicon.connect('activate', self.toggle_window_display)
  254.             self.statusicon.connect('popup-menu', self.on_icon_popupmenu)
  255.             self.statusicon.set_visible(False)
  256.         
  257.         if bus == None:
  258.             bus = dbus.SystemBus()
  259.         
  260.         self.set_process_pending(True)
  261.         self.host = cups.getServer()
  262.         self.port = cups.getPort()
  263.         self.encryption = cups.getEncryption()
  264.         self.monitor = monitor.Monitor(self, bus = bus, my_jobs = my_jobs, specific_dests = specific_dests, host = self.host, port = self.port, encryption = self.encryption)
  265.         if not self.trayicon:
  266.             self.JobsWindow.show()
  267.         
  268.  
  269.     
  270.     def cleanup(self):
  271.         self.monitor.cleanup()
  272.         for l in [
  273.             self.new_printer_notifications.values(),
  274.             self.state_reason_notifications.values()]:
  275.             for notification in l:
  276.                 if notification.get_data('closed') != True:
  277.                     notification.close()
  278.                     notification.set_data('closed', True)
  279.                     continue
  280.             
  281.         
  282.         if self.exit_handler:
  283.             self.exit_handler(self)
  284.         
  285.  
  286.     
  287.     def set_process_pending(self, whether):
  288.         self.process_pending_events = whether
  289.  
  290.     
  291.     def set_special_statusicon(self, iconname):
  292.         self.special_status_icon = True
  293.         self.statusicon.set_from_icon_name(iconname)
  294.         self.set_statusicon_visibility()
  295.  
  296.     
  297.     def unset_special_statusicon(self):
  298.         self.special_status_icon = False
  299.         self.statusicon.set_from_pixbuf(self.saved_statusicon_pixbuf)
  300.         self.set_statusicon_visibility()
  301.  
  302.     
  303.     def notify_new_printer(self, printer, notification):
  304.         self.new_printer_notifications[printer] = notification
  305.         notification.set_data('printer-name', printer)
  306.         notification.connect('closed', self.on_new_printer_notification_closed)
  307.         self.set_statusicon_visibility()
  308.         notification.attach_to_status_icon(self.statusicon)
  309.         notification.show()
  310.  
  311.     
  312.     def on_new_printer_notification_closed(self, notification, reason = None):
  313.         printer = notification.get_data('printer-name')
  314.         del self.new_printer_notifications[printer]
  315.         self.set_statusicon_visibility()
  316.  
  317.     
  318.     def set_statusicon_from_pixbuf(self, pb):
  319.         self.saved_statusicon_pixbuf = pb
  320.         if not self.special_status_icon:
  321.             self.statusicon.set_from_pixbuf(pb)
  322.         
  323.  
  324.     
  325.     def on_delete_event(self, *args):
  326.         if self.trayicon or not (self.loop):
  327.             self.JobsWindow.hide()
  328.             if not self.loop:
  329.                 self.cleanup()
  330.             
  331.         else:
  332.             self.loop.quit()
  333.         return True
  334.  
  335.     
  336.     def show_IPP_Error(self, exception, message):
  337.         return errordialogs.show_IPP_Error(exception, message, self.JobsWindow)
  338.  
  339.     
  340.     def toggle_window_display(self, icon, force_show = False):
  341.         visible = self.JobsWindow.get_property('visible')
  342.         if force_show:
  343.             visible = False
  344.         
  345.         if visible:
  346.             self.JobsWindow.hide()
  347.         else:
  348.             self.JobsWindow.show()
  349.  
  350.     
  351.     def on_show_completed_jobs_activate(self, menuitem):
  352.         if menuitem.get_active():
  353.             which_jobs = 'all'
  354.         else:
  355.             which_jobs = 'not-completed'
  356.         self.monitor.refresh(which_jobs = which_jobs, refresh_all = False)
  357.  
  358.     
  359.     def update_job_creation_times(self):
  360.         now = time.time()
  361.         need_update = False
  362.         for job, data in self.jobs.iteritems():
  363.             if self.jobs.has_key(job):
  364.                 iter = self.jobiters[job]
  365.             
  366.             t = _('Unknown')
  367.             if data.has_key('time-at-creation'):
  368.                 created = data['time-at-creation']
  369.                 ago = now - created
  370.                 need_update = True
  371.                 if ago < 120:
  372.                     t = _('a minute ago')
  373.                 elif ago < 3600:
  374.                     mins = int(ago / 60)
  375.                     t = _('%d minutes ago') % mins
  376.                 elif ago < 86400:
  377.                     hours = int(ago / 3600)
  378.                     if hours == 1:
  379.                         t = _('an hour ago')
  380.                     else:
  381.                         t = _('%d hours ago') % hours
  382.                 elif ago < 604800:
  383.                     days = int(ago / 86400)
  384.                     if days == 1:
  385.                         t = _('yesterday')
  386.                     else:
  387.                         t = _('%d days ago') % days
  388.                 elif ago < 3628800:
  389.                     weeks = int(ago / 604800)
  390.                     if weeks == 1:
  391.                         t = _('last week')
  392.                     else:
  393.                         t = _('%d weeks ago') % weeks
  394.                 else:
  395.                     need_update = False
  396.                     t = time.strftime('%B %Y', time.localtime(created))
  397.             
  398.             self.store.set_value(iter, 5, t)
  399.         
  400.         if need_update and not (self.job_creation_times_timer):
  401.             t = gobject.timeout_add(60000, self.update_job_creation_times)
  402.             self.job_creation_times_timer = t
  403.         
  404.         if not need_update:
  405.             if self.job_creation_times_timer:
  406.                 gobject.source_remove(self.job_creation_times_timer)
  407.                 self.job_creation_times_timer = None
  408.             
  409.         
  410.         return need_update
  411.  
  412.     
  413.     def print_error_dialog_response(self, dialog, response, jobid):
  414.         dialog.hide()
  415.         dialog.destroy()
  416.         self.stopped_job_prompts.remove(jobid)
  417.         if response == gtk.RESPONSE_NO:
  418.             if not self.__dict__.has_key('troubleshooter'):
  419.                 import troubleshoot
  420.                 troubleshooter = troubleshoot.run(self.on_troubleshoot_quit)
  421.                 self.troubleshooter = troubleshooter
  422.             
  423.         
  424.  
  425.     
  426.     def on_troubleshoot_quit(self, troubleshooter):
  427.         del self.troubleshooter
  428.  
  429.     
  430.     def add_job(self, job, data, connection = None):
  431.         store = self.store
  432.         iter = self.store.append(None)
  433.         store.set_value(iter, 0, job)
  434.         store.set_value(iter, 1, data.get('job-originating-user-name', _('Unknown')))
  435.         store.set_value(iter, 2, data.get('job-name', _('Unknown')))
  436.         debugprint('Job %d added' % job)
  437.         self.jobiters[job] = iter
  438.         if not self.job_creation_times_timer:
  439.             t = gobject.timeout_add(1000, self.update_job_creation_times)
  440.             self.job_creation_times_timer = t
  441.         
  442.         self.update_job(job, data, connection = connection)
  443.  
  444.     
  445.     def update_job(self, job, data, connection = None):
  446.         r = self.required_job_attributes - set(data.keys())
  447.         if r:
  448.             attrs = None
  449.             
  450.             try:
  451.                 if connection == None:
  452.                     connection = cups.Connection(host = self.host, port = self.port, encryption = self.encryption)
  453.                 
  454.                 debugprint('requesting %s' % r)
  455.                 r = list(r)
  456.                 attrs = connection.getJobAttributes(job, requested_attributes = r)
  457.             except RuntimeError:
  458.                 pass
  459.             except AttributeError:
  460.                 pass
  461.  
  462.             if attrs:
  463.                 data.update(attrs)
  464.             
  465.         
  466.         store = self.store
  467.         iter = self.jobiters[job]
  468.         self.jobs[job] = data
  469.         printer = data['job-printer-name']
  470.         store.set_value(iter, 3, printer)
  471.         size = _('Unknown')
  472.         if data.has_key('job-k-octets'):
  473.             size = str(data['job-k-octets']) + 'k'
  474.         
  475.         store.set_value(iter, 4, size)
  476.         job_requires_auth = False
  477.         c = None
  478.         
  479.         try:
  480.             jstate = data.get('job-state', cups.IPP_JOB_PROCESSING)
  481.             s = int(jstate)
  482.             if s in [
  483.                 cups.IPP_JOB_HELD,
  484.                 cups.IPP_JOB_STOPPED]:
  485.                 jattrs = [
  486.                     'job-state',
  487.                     'job-hold-until']
  488.                 pattrs = [
  489.                     'auth-info-required',
  490.                     'device-uri']
  491.                 uri = data.get('job-printer-uri')
  492.                 c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  493.                 attrs = c.getPrinterAttributes(uri = uri, requested_attributes = pattrs)
  494.                 
  495.                 try:
  496.                     auth_info_required = attrs['auth-info-required']
  497.                 except KeyError:
  498.                     debugprint('No auth-info-required attribute; guessing instead')
  499.                     auth_info_required = [
  500.                         'username',
  501.                         'password']
  502.  
  503.                 if not isinstance(auth_info_required, list):
  504.                     auth_info_required = [
  505.                         auth_info_required]
  506.                     attrs['auth-info-required'] = auth_info_required
  507.                 
  508.                 data.update(attrs)
  509.                 attrs = c.getJobAttributes(job, requested_attributes = jattrs)
  510.                 data.update(attrs)
  511.                 jstate = data.get('job-state', cups.IPP_JOB_PROCESSING)
  512.                 s = int(jstate)
  513.         except ValueError:
  514.             pass
  515.         except RuntimeError:
  516.             pass
  517.         except cups.IPPError:
  518.             (e, m) = None
  519.  
  520.         if s == cups.IPP_JOB_HELD:
  521.             pass
  522.         job_requires_auth = data.get('job-hold-until', 'none') == 'auth-info-required'
  523.         state = None
  524.         if job_requires_auth:
  525.             state = _('Held for authentication')
  526.         elif s == cups.IPP_JOB_HELD:
  527.             state = _('Held')
  528.             until = data.get('job-hold-until')
  529.             if until != None:
  530.                 
  531.                 try:
  532.                     colon1 = until.find(':')
  533.                     if colon1 != -1:
  534.                         now = time.gmtime()
  535.                         hh = int(until[:colon1])
  536.                         colon2 = until[colon1 + 1:].find(':')
  537.                         if colon2 != -1:
  538.                             colon2 += colon1 + 1
  539.                             mm = int(until[colon1 + 1:colon2])
  540.                             ss = int(until[colon2 + 1:])
  541.                         else:
  542.                             mm = int(until[colon1 + 1:])
  543.                             ss = 0
  544.                         day = now.tm_mday
  545.                         if hh < now.tm_hour or hh == now.tm_hour:
  546.                             if (mm < now.tm_min or mm == now.tm_min) and ss < now.tm_sec:
  547.                                 day += 1
  548.                             
  549.                         hold = (now.tm_year, now.tm_mon, day, hh, mm, ss, 0, 0, -1)
  550.                         local = time.localtime(time.mktime(hold) - time.timezone)
  551.                         state = _('Held until %s') % time.strftime('%X', local)
  552.                 except ValueError:
  553.                     pass
  554.                 except:
  555.                     None<EXCEPTION MATCH>ValueError
  556.                 
  557.  
  558.             None<EXCEPTION MATCH>ValueError
  559.             if until == 'day-time':
  560.                 state = _('Held until day-time')
  561.             elif until == 'evening':
  562.                 state = _('Held until evening')
  563.             elif until == 'night':
  564.                 state = _('Held until night-time')
  565.             elif until == 'second-shift':
  566.                 state = _('Held until second shift')
  567.             elif until == 'third-shift':
  568.                 state = _('Held until third shift')
  569.             elif until == 'weekend':
  570.                 state = _('Held until weekend')
  571.             
  572.         else:
  573.             
  574.             try:
  575.                 state = {
  576.                     cups.IPP_JOB_PENDING: _('Pending'),
  577.                     cups.IPP_JOB_PROCESSING: _('Processing'),
  578.                     cups.IPP_JOB_STOPPED: _('Stopped'),
  579.                     cups.IPP_JOB_CANCELED: _('Canceled'),
  580.                     cups.IPP_JOB_ABORTED: _('Aborted'),
  581.                     cups.IPP_JOB_COMPLETED: _('Completed') }[s]
  582.             except IndexError:
  583.                 pass
  584.  
  585.         if state == None:
  586.             state = _('Unknown')
  587.         
  588.         store.set_value(iter, 6, state)
  589.         if self.trayicon:
  590.             if job_requires_auth and not self.auth_info_dialogs.has_key(job):
  591.                 
  592.                 try:
  593.                     cups.require('1.9.37')
  594.                 except:
  595.                     debugprint('Authentication required but authenticateJob() not available')
  596.                     return None
  597.  
  598.                 try_keyring = USE_KEYRING
  599.                 keyring_attrs = None
  600.                 auth_info = None
  601.                 if try_keyring and 'password' in auth_info_required:
  602.                     auth_info_required = data.get('auth-info-required', [])
  603.                     device_uri = data.get('device-uri')
  604.                     (scheme, rest) = urllib.splittype(device_uri)
  605.                     keyring_attrs = dict()
  606.                     if scheme == 'smb':
  607.                         uri = smburi.SMBURI(uri = device_uri)
  608.                         (group, server, share, user, password) = uri.separate()
  609.                         keyring_attrs['domain'] = str(group)
  610.                     else:
  611.                         (serverport, rest) = urllib.splithost(rest)
  612.                         (server, port) = urllib.splitnport(serverport)
  613.                     username = pwd.getpwuid(os.getuid())[0]
  614.                     keyring_attrs.update({
  615.                         'server': str(server.lower()),
  616.                         'protocol': str(scheme),
  617.                         'user': str(username) })
  618.                 
  619.                 if job in self.authenticated_jobs:
  620.                     try_keyring = False
  621.                 
  622.                 if try_keyring and 'password' in auth_info_required:
  623.                     type = gnomekeyring.ITEM_NETWORK_PASSWORD
  624.                     
  625.                     try:
  626.                         items = gnomekeyring.find_items_sync(type, keyring_attrs)
  627.                         auth_info = map((lambda x: ''), auth_info_required)
  628.                         ind = auth_info_required.index('username')
  629.                         auth_info[ind] = items[0].attributes.get('user', '')
  630.                         ind = auth_info_required.index('password')
  631.                         auth_info[ind] = items[0].secret
  632.                     except gnomekeyring.NoMatchError:
  633.                         debugprint('gnomekeyring: no match for %s' % keyring_attrs)
  634.                     except gnomekeyring.DeniedError:
  635.                         debugprint('gnomekeyring: denied for %s' % keyring_attrs)
  636.                     except:
  637.                         None<EXCEPTION MATCH>gnomekeyring.NoMatchError
  638.                     
  639.  
  640.                 None<EXCEPTION MATCH>gnomekeyring.NoMatchError
  641.                 if try_keyring and c == None:
  642.                     
  643.                     try:
  644.                         c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  645.                     except RuntimeError:
  646.                         try_keyring = False
  647.                     except:
  648.                         None<EXCEPTION MATCH>RuntimeError
  649.                     
  650.  
  651.                 None<EXCEPTION MATCH>RuntimeError
  652.                 if try_keyring and auth_info != None:
  653.                     
  654.                     try:
  655.                         c._begin_operation(_('authenticating job'))
  656.                         c.authenticateJob(job, auth_info)
  657.                         c._end_operation()
  658.                         self.monitor.update()
  659.                         debugprint('Automatically authenticated job %d' % job)
  660.                         self.authenticated_jobs.add(job)
  661.                         return None
  662.                     except cups.IPPError:
  663.                         (e, m) = None
  664.                         c._end_operation()
  665.                         nonfatalException()
  666.                         return None
  667.                         c._end_operation()
  668.                         nonfatalException()
  669.                     
  670.  
  671.                 None<EXCEPTION MATCH>cups.IPPError
  672.                 self.display_auth_info_dialog(job)
  673.             
  674.         
  675.  
  676.     
  677.     def on_auth_notification_closed(self, notification, reason = None):
  678.         job = notification.get_data('job-id')
  679.         debugprint('auth notification closed for job %s' % job)
  680.         self.auth_notifications[job].set_data('closed', True)
  681.         del self.auth_notifications[job]
  682.  
  683.     
  684.     def on_auth_notification_authenticate(self, notification, action):
  685.         job = notification.get_data('job-id')
  686.         keyring_attrs = notification.get_data('keyring-attrs')
  687.         debugprint('auth notification authenticate for job %s' % job)
  688.         self.display_auth_info_dialog(job, keyring_attrs)
  689.  
  690.     
  691.     def display_auth_info_dialog(self, job, keyring_attrs = None):
  692.         data = self.jobs[job]
  693.         auth_info_required = data['auth-info-required']
  694.         dialog = authconn.AuthDialog(auth_info_required = auth_info_required, allow_remember = USE_KEYRING)
  695.         dialog.set_data('keyring-attrs', keyring_attrs)
  696.         dialog.set_data('auth-info-required', auth_info_required)
  697.         dialog.set_position(gtk.WIN_POS_CENTER)
  698.         auth_info = map((lambda x: ''), auth_info_required)
  699.         username = pwd.getpwuid(os.getuid())[0]
  700.         if 'username' in auth_info_required:
  701.             
  702.             try:
  703.                 ind = auth_info_required.index('username')
  704.                 auth_info[ind] = username
  705.                 dialog.set_auth_info(auth_info)
  706.             nonfatalException()
  707.  
  708.         
  709.         index = 0
  710.         for field in auth_info_required:
  711.             if auth_info[index] == '':
  712.                 dialog.field_grab_focus(field)
  713.                 break
  714.             
  715.             index += 1
  716.         
  717.         dialog.set_prompt(_("Authentication required for printing document `%s' (job %d)") % (data.get('job-name', _('Unknown')), job))
  718.         self.auth_info_dialogs[job] = dialog
  719.         dialog.connect('response', self.auth_info_dialog_response)
  720.         dialog.connect('delete-event', self.auth_info_dialog_delete)
  721.         dialog.set_data('job-id', job)
  722.         dialog.show_all()
  723.         dialog.set_keep_above(True)
  724.         dialog.show_now()
  725.  
  726.     
  727.     def auth_info_dialog_delete(self, dialog, event):
  728.         self.auth_info_dialog_response(dialog, gtk.RESPONSE_CANCEL)
  729.  
  730.     
  731.     def auth_info_dialog_response(self, dialog, response):
  732.         jobid = dialog.get_data('job-id')
  733.         del self.auth_info_dialogs[jobid]
  734.         if response != gtk.RESPONSE_OK:
  735.             dialog.destroy()
  736.             return None
  737.         auth_info = dialog.get_auth_info()
  738.         
  739.         try:
  740.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  741.         except RuntimeError:
  742.             response != gtk.RESPONSE_OK
  743.             response != gtk.RESPONSE_OK
  744.             debugprint('Error connecting to CUPS for authentication')
  745.             return None
  746.  
  747.         remember = False
  748.         c._begin_operation(_('authenticating job'))
  749.         
  750.         try:
  751.             c.authenticateJob(jobid, auth_info)
  752.             remember = dialog.get_remember_password()
  753.             self.authenticated_jobs.add(jobid)
  754.             self.monitor.update()
  755.         except cups.IPPError:
  756.             response != gtk.RESPONSE_OK
  757.             (e, m) = response != gtk.RESPONSE_OK
  758.             self.show_IPP_Error(e, m)
  759.         except:
  760.             response != gtk.RESPONSE_OK
  761.  
  762.         c._end_operation()
  763.         dialog.destroy()
  764.  
  765.     
  766.     def set_statusicon_visibility(self):
  767.         if not self.trayicon:
  768.             return None
  769.         if self.suppress_icon_hide:
  770.             self.suppress_icon_hide = False
  771.             return None
  772.         open_notifications = len(self.new_printer_notifications.keys())
  773.         open_notifications += len(self.completed_job_notifications.keys())
  774.         for reason, notification in self.state_reason_notifications.iteritems():
  775.             if notification.get_data('closed') != True:
  776.                 open_notifications += 1
  777.                 continue
  778.             self.suppress_icon_hide
  779.         
  780.         num_jobs = len(self.active_jobs)
  781.         debugprint('open notifications: %d' % open_notifications)
  782.         debugprint('num_jobs: %d' % num_jobs)
  783.         debugprint('num_jobs_when_hidden: %d' % self.num_jobs_when_hidden)
  784.         if not self.special_status_icon and open_notifications > 0:
  785.             pass
  786.         self.statusicon.set_visible(num_jobs > self.num_jobs_when_hidden)
  787.         while self.process_pending_events and gtk.events_pending():
  788.             gtk.main_iteration()
  789.             continue
  790.             self.trayicon
  791.  
  792.     
  793.     def on_treeview_popup_menu(self, treeview):
  794.         event = gtk.gdk.Event(gtk.gdk.NOTHING)
  795.         self.show_treeview_popup_menu(treeview, event, 0)
  796.  
  797.     
  798.     def on_treeview_button_release_event(self, treeview, event):
  799.         if event.button == 3:
  800.             self.show_treeview_popup_menu(treeview, event, event.button)
  801.         
  802.  
  803.     
  804.     def on_treeview_cursor_changed(self, treeview):
  805.         (path, column) = treeview.get_cursor()
  806.         cancel = self.job_ui_manager.get_action('/cancel-job')
  807.         hold = self.job_ui_manager.get_action('/hold-job')
  808.         release = self.job_ui_manager.get_action('/release-job')
  809.         reprint = self.job_ui_manager.get_action('/reprint-job')
  810.         authenticate = self.job_ui_manager.get_action('/authenticate-job')
  811.         if path == None:
  812.             for widget in [
  813.                 cancel,
  814.                 hold,
  815.                 release,
  816.                 reprint,
  817.                 authenticate]:
  818.                 widget.set_sensitive(False)
  819.             
  820.             return None
  821.         iter = self.store.get_iter(path)
  822.         self.jobid = self.store.get_value(iter, 0)
  823.         job = self.jobs[self.jobid]
  824.         authenticate.set_sensitive(False)
  825.         for widget in [
  826.             cancel,
  827.             hold,
  828.             release,
  829.             reprint]:
  830.             widget.set_sensitive(True)
  831.         
  832.         if job.has_key('job-state'):
  833.             s = job['job-state']
  834.             if s >= cups.IPP_JOB_CANCELED:
  835.                 cancel.set_sensitive(False)
  836.             
  837.             if s != cups.IPP_JOB_PENDING:
  838.                 hold.set_sensitive(False)
  839.             
  840.             if s != cups.IPP_JOB_HELD:
  841.                 release.set_sensitive(False)
  842.             
  843.             if not job.get('job-preserved', False):
  844.                 reprint.set_sensitive(False)
  845.             
  846.         
  847.         if job.get('job-state', cups.IPP_JOB_CANCELED) == cups.IPP_JOB_HELD:
  848.             if job.get('job-hold-until', 'none') == 'auth-info-required':
  849.                 authenticate.set_sensitive(True)
  850.             
  851.         
  852.  
  853.     
  854.     def show_treeview_popup_menu(self, treeview, event, event_button):
  855.         self.job_context_menu.popup(None, None, None, event_button, event.get_time())
  856.  
  857.     
  858.     def on_icon_popupmenu(self, icon, button, time):
  859.         self.statusicon_popupmenu.popup(None, None, None, button, time)
  860.  
  861.     
  862.     def on_icon_hide_activate(self, menuitem):
  863.         self.num_jobs_when_hidden = len(self.jobs.keys())
  864.         self.set_statusicon_visibility()
  865.  
  866.     
  867.     def on_icon_configure_printers_activate(self, menuitem):
  868.         if self.loop:
  869.             env = { }
  870.             for name, value in os.environ.iteritems():
  871.                 if name == 'SYSTEM_CONFIG_PRINTER_GLADE':
  872.                     continue
  873.                 
  874.                 env[name] = value
  875.             
  876.             p = subprocess.Popen([
  877.                 'system-config-printer'], close_fds = True, env = env)
  878.             gobject.timeout_add(10000, self.poll_subprocess, p)
  879.         
  880.  
  881.     
  882.     def poll_subprocess(self, process):
  883.         returncode = process.poll()
  884.         return returncode == None
  885.  
  886.     
  887.     def on_icon_quit_activate(self, menuitem):
  888.         if self.loop:
  889.             self.loop.quit()
  890.         
  891.  
  892.     
  893.     def on_job_cancel_activate(self, menuitem):
  894.         dialog = gtk.Dialog(_('Cancel Job'), self.JobsWindow, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, (gtk.STOCK_NO, gtk.RESPONSE_NO, gtk.STOCK_YES, gtk.RESPONSE_YES))
  895.         dialog.set_default_response(gtk.RESPONSE_NO)
  896.         dialog.set_border_width(6)
  897.         dialog.set_resizable(False)
  898.         hbox = gtk.HBox(False, 12)
  899.         image = gtk.Image()
  900.         image.set_from_stock(gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG)
  901.         image.set_alignment(0, 0)
  902.         hbox.pack_start(image, False, False, 0)
  903.         label = gtk.Label(_('Do you really want to cancel this job?'))
  904.         label.set_line_wrap(True)
  905.         label.set_alignment(0, 0)
  906.         hbox.pack_start(label, False, False, 0)
  907.         dialog.vbox.pack_start(hbox, False, False, 0)
  908.         dialog.set_data('job-id', self.jobid)
  909.         dialog.connect('response', self.on_job_cancel_prompt_response)
  910.         dialog.connect('delete-event', self.on_job_cancel_prompt_delete)
  911.         dialog.show_all()
  912.  
  913.     
  914.     def on_job_cancel_prompt_delete(self, dialog, event):
  915.         self.on_job_cancel_prompt_response(dialog, gtk.RESPONSE_NO)
  916.  
  917.     
  918.     def on_job_cancel_prompt_response(self, dialog, response):
  919.         jobid = dialog.get_data('job-id')
  920.         dialog.destroy()
  921.         if response != gtk.RESPONSE_YES:
  922.             return None
  923.         
  924.         try:
  925.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  926.         except RuntimeError:
  927.             response != gtk.RESPONSE_YES
  928.             response != gtk.RESPONSE_YES
  929.             return None
  930.  
  931.         c._begin_operation(_('canceling job'))
  932.         
  933.         try:
  934.             c.cancelJob(jobid)
  935.         except cups.IPPError:
  936.             response != gtk.RESPONSE_YES
  937.             (e, m) = response != gtk.RESPONSE_YES
  938.             if e != cups.IPP_NOT_POSSIBLE and e != cups.IPP_NOT_FOUND:
  939.                 self.show_IPP_Error(e, m)
  940.             
  941.             self.monitor.update()
  942.             c._end_operation()
  943.             return None
  944.  
  945.         c._end_operation()
  946.         del c
  947.         self.monitor.update()
  948.  
  949.     
  950.     def on_job_hold_activate(self, menuitem):
  951.         
  952.         try:
  953.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  954.         except RuntimeError:
  955.             return None
  956.  
  957.         c._begin_operation(_('holding job'))
  958.         
  959.         try:
  960.             c.setJobHoldUntil(self.jobid, 'indefinite')
  961.         except cups.IPPError:
  962.             (e, m) = None
  963.             if e != cups.IPP_NOT_POSSIBLE and e != cups.IPP_NOT_FOUND:
  964.                 self.show_IPP_Error(e, m)
  965.             
  966.             self.monitor.update()
  967.             c._end_operation()
  968.             return None
  969.  
  970.         c._end_operation()
  971.         del c
  972.         self.monitor.update()
  973.  
  974.     
  975.     def on_job_release_activate(self, menuitem):
  976.         
  977.         try:
  978.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  979.         except RuntimeError:
  980.             return None
  981.  
  982.         c._begin_operation(_('releasing job'))
  983.         
  984.         try:
  985.             c.setJobHoldUntil(self.jobid, 'no-hold')
  986.         except cups.IPPError:
  987.             (e, m) = None
  988.             if e != cups.IPP_NOT_POSSIBLE and e != cups.IPP_NOT_FOUND:
  989.                 self.show_IPP_Error(e, m)
  990.             
  991.             self.monitor.update()
  992.             c._end_operation()
  993.             return None
  994.  
  995.         c._end_operation()
  996.         del c
  997.         self.monitor.update()
  998.  
  999.     
  1000.     def on_job_reprint_activate(self, menuitem):
  1001.         
  1002.         try:
  1003.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  1004.             c.restartJob(self.jobid)
  1005.             del c
  1006.         except cups.IPPError:
  1007.             (e, m) = None
  1008.             self.show_IPP_Error(e, m)
  1009.             self.monitor.update()
  1010.             return None
  1011.             except RuntimeError:
  1012.                 return None
  1013.             else:
  1014.                 self.monitor.update()
  1015.                 return None
  1016.  
  1017.  
  1018.     
  1019.     def on_job_authenticate_activate(self, menuitem):
  1020.         self.display_auth_info_dialog(self.jobid)
  1021.  
  1022.     
  1023.     def on_refresh_activate(self, menuitem):
  1024.         self.monitor.refresh()
  1025.  
  1026.     
  1027.     def job_is_active(self, jobdata):
  1028.         state = jobdata.get('job-state', cups.IPP_JOB_CANCELED)
  1029.         if state >= cups.IPP_JOB_CANCELED:
  1030.             return False
  1031.         return True
  1032.  
  1033.     
  1034.     def add_state_reason_emblem(self, pixbuf):
  1035.         if self.worst_reason != None:
  1036.             printer = self.worst_reason.get_printer()
  1037.             found = False
  1038.             for reason in self.printer_state_reasons[printer]:
  1039.                 if reason == self.worst_reason:
  1040.                     found = True
  1041.                     break
  1042.                     continue
  1043.             
  1044.             if not found:
  1045.                 self.worst_reason = None
  1046.             
  1047.         
  1048.         if self.worst_reason != None:
  1049.             level = self.worst_reason.get_level()
  1050.             if level > StateReason.REPORT:
  1051.                 icon = StateReason.LEVEL_ICON[level]
  1052.                 pixbuf = pixbuf.copy()
  1053.                 theme = gtk.icon_theme_get_default()
  1054.                 emblem = theme.load_icon(icon, 22, 0)
  1055.                 emblem.composite(pixbuf, pixbuf.get_width() / 2, pixbuf.get_height() / 2, emblem.get_width() / 2, emblem.get_height() / 2, pixbuf.get_width() / 2, pixbuf.get_height() / 2, 0.5, 0.5, gtk.gdk.INTERP_BILINEAR, 255)
  1056.             
  1057.         
  1058.         return pixbuf
  1059.  
  1060.     
  1061.     def get_icon_pixbuf(self, have_jobs = None):
  1062.         if not self.trayicon:
  1063.             return None
  1064.         if have_jobs == None:
  1065.             have_jobs = len(self.jobs.keys()) > 0
  1066.         
  1067.         if have_jobs:
  1068.             pixbuf = self.icon_jobs
  1069.             for jobid, jobdata in self.jobs.iteritems():
  1070.                 jstate = jobdata.get('job-state', cups.IPP_JOB_PENDING)
  1071.                 if jstate == cups.IPP_JOB_PROCESSING:
  1072.                     pixbuf = self.icon_jobs_processing
  1073.                     break
  1074.                     continue
  1075.             
  1076.         else:
  1077.             pixbuf = self.icon_no_jobs
  1078.         
  1079.         try:
  1080.             pixbuf = self.add_state_reason_emblem(pixbuf)
  1081.         except:
  1082.             nonfatalException()
  1083.  
  1084.         return pixbuf
  1085.  
  1086.     
  1087.     def set_statusicon_tooltip(self, tooltip = None):
  1088.         if not self.trayicon:
  1089.             return None
  1090.         if tooltip == None:
  1091.             num_jobs = len(self.jobs)
  1092.             if num_jobs == 0:
  1093.                 tooltip = _('No documents queued')
  1094.             elif num_jobs == 1:
  1095.                 tooltip = _('1 document queued')
  1096.             else:
  1097.                 tooltip = _('%d documents queued') % num_jobs
  1098.         
  1099.         self.statusicon.set_tooltip(tooltip)
  1100.  
  1101.     
  1102.     def update_status(self, have_jobs = None):
  1103.         upset_printers = set()
  1104.         for printer, reasons in self.printer_state_reasons.iteritems():
  1105.             if len(reasons) > 0:
  1106.                 upset_printers.add(printer)
  1107.                 continue
  1108.         
  1109.         debugprint('Upset printers: %s' % upset_printers)
  1110.         my_upset_printers = set()
  1111.         if len(upset_printers):
  1112.             my_upset_printers = set()
  1113.             for jobid in self.active_jobs:
  1114.                 printer = self.jobs[jobid]['job-printer-name']
  1115.                 if printer in upset_printers:
  1116.                     my_upset_printers.add(printer)
  1117.                     continue
  1118.             
  1119.             debugprint('My upset printers: %s' % my_upset_printers)
  1120.         
  1121.         my_reasons = []
  1122.         for printer in my_upset_printers:
  1123.             my_reasons.extend(self.printer_state_reasons[printer])
  1124.         
  1125.         self.worst_reason = None
  1126.         if len(my_reasons) > 0:
  1127.             worst_reason = my_reasons[0]
  1128.             for reason in my_reasons:
  1129.                 if reason > worst_reason:
  1130.                     worst_reason = reason
  1131.                     continue
  1132.             
  1133.             self.worst_reason = worst_reason
  1134.             debugprint('Worst reason: %s' % worst_reason)
  1135.         
  1136.         if self.worst_reason != None:
  1137.             (title, tooltip) = self.worst_reason.get_description()
  1138.             if self.statusbar_set:
  1139.                 self.statusbar.pop(0)
  1140.             
  1141.             self.statusbar.push(0, tooltip)
  1142.             self.statusbar_set = True
  1143.         else:
  1144.             tooltip = None
  1145.             if self.statusbar_set:
  1146.                 self.statusbar.pop(0)
  1147.                 self.statusbar_set = False
  1148.             
  1149.         if self.trayicon:
  1150.             pixbuf = self.get_icon_pixbuf(have_jobs = have_jobs)
  1151.             self.set_statusicon_from_pixbuf(pixbuf)
  1152.             self.set_statusicon_visibility()
  1153.             self.set_statusicon_tooltip(tooltip = tooltip)
  1154.         
  1155.  
  1156.     
  1157.     def notify_printer_state_reason_if_important(self, reason):
  1158.         level = reason.get_level()
  1159.         if level < StateReason.WARNING:
  1160.             return None
  1161.         self.notify_printer_state_reason(reason)
  1162.  
  1163.     
  1164.     def notify_printer_state_reason(self, reason):
  1165.         tuple = reason.get_tuple()
  1166.         if self.state_reason_notifications.has_key(tuple):
  1167.             debugprint('Already sent notification for %s' % repr(reason))
  1168.             return None
  1169.         level = reason.get_level()
  1170.         if level == StateReason.ERROR or reason.get_reason() == 'connecting-to-device':
  1171.             urgency = pynotify.URGENCY_NORMAL
  1172.         else:
  1173.             urgency = pynotify.URGENCY_LOW
  1174.         (title, text) = reason.get_description()
  1175.         notification = pynotify.Notification(title, text, 'printer')
  1176.         reason.user_notified = True
  1177.         notification.set_urgency(urgency)
  1178.         if 'actions' in pynotify.get_server_caps():
  1179.             notification.set_timeout(pynotify.EXPIRES_NEVER)
  1180.         
  1181.         notification.connect('closed', self.on_state_reason_notification_closed)
  1182.         self.state_reason_notifications[reason.get_tuple()] = notification
  1183.         self.set_statusicon_visibility()
  1184.         notification.attach_to_status_icon(self.statusicon)
  1185.         notification.show()
  1186.  
  1187.     
  1188.     def on_state_reason_notification_closed(self, notification, reason = None):
  1189.         debugprint('Notification %s closed' % repr(notification))
  1190.         notification.set_data('closed', True)
  1191.         self.set_statusicon_visibility()
  1192.  
  1193.     
  1194.     def notify_completed_job(self, jobid):
  1195.         job = self.jobs.get(jobid, { })
  1196.         document = job.get('job-name', _('Unknown'))
  1197.         printer = job.get('job-printer-name', _('Unknown'))
  1198.         notification = pynotify.Notification(_('Job %d completed') % jobid, _("Document `%s' has finished printing on `%s'.") % (document, printer), 'printer')
  1199.         notification.set_urgency(pynotify.URGENCY_LOW)
  1200.         notification.connect('closed', self.on_completed_job_notification_closed)
  1201.         notification.set_data('jobid', jobid)
  1202.         self.completed_job_notifications[jobid] = notification
  1203.         self.set_statusicon_visibility()
  1204.         notification.attach_to_status_icon(self.statusicon)
  1205.         notification.show()
  1206.  
  1207.     
  1208.     def on_completed_job_notification_closed(self, notification, reason = None):
  1209.         jobid = notification.get_data('jobid')
  1210.         del self.completed_job_notifications[jobid]
  1211.         self.set_statusicon_visibility()
  1212.  
  1213.     
  1214.     def current_printers_and_jobs(self, mon, printers, jobs):
  1215.         monitor.Watcher.current_printers_and_jobs(self, mon, printers, jobs)
  1216.         self.set_process_pending(False)
  1217.         self.store.clear()
  1218.         self.jobs = { }
  1219.         self.jobiters = { }
  1220.         self.printer_uri_index = PrinterURIIndex(names = printers)
  1221.         connection = None
  1222.         for jobid, jobdata in jobs.iteritems():
  1223.             uri = jobdata.get('job-printer-uri', '')
  1224.             
  1225.             try:
  1226.                 printer = self.printer_uri_index.lookup(uri, connection = connection)
  1227.             except KeyError:
  1228.                 printer = uri
  1229.  
  1230.             jobdata['job-printer-name'] = printer
  1231.             self.add_job(jobid, jobdata, connection = connection)
  1232.         
  1233.         self.jobs = jobs
  1234.         self.active_jobs = set()
  1235.         for jobid, jobdata in jobs.iteritems():
  1236.             if self.job_is_active(jobdata):
  1237.                 self.active_jobs.add(jobid)
  1238.                 continue
  1239.         
  1240.         self.set_process_pending(True)
  1241.         self.update_status()
  1242.  
  1243.     
  1244.     def job_added(self, mon, jobid, eventname, event, jobdata):
  1245.         monitor.Watcher.job_added(self, mon, jobid, eventname, event, jobdata)
  1246.         uri = jobdata.get('job-printer-uri', '')
  1247.         
  1248.         try:
  1249.             printer = self.printer_uri_index.lookup(uri)
  1250.         except KeyError:
  1251.             printer = uri
  1252.  
  1253.         jobdata['job-printer-name'] = printer
  1254.         if not self.jobiters.has_key(jobid):
  1255.             self.add_job(jobid, jobdata)
  1256.         
  1257.         if self.job_is_active(jobdata):
  1258.             self.active_jobs.add(jobid)
  1259.         elif jobid in self.active_jobs:
  1260.             self.active_jobs.remove(jobid)
  1261.         
  1262.         self.update_status(have_jobs = True)
  1263.         if self.trayicon:
  1264.             if not self.job_is_active(jobdata):
  1265.                 return None
  1266.             for reason in self.printer_state_reasons.get(printer, []):
  1267.                 if not reason.user_notified:
  1268.                     self.notify_printer_state_reason_if_important(reason)
  1269.                     continue
  1270.                 self.job_is_active(jobdata)
  1271.             
  1272.         
  1273.  
  1274.     
  1275.     def job_event(self, mon, jobid, eventname, event, jobdata):
  1276.         monitor.Watcher.job_event(self, mon, jobid, eventname, event, jobdata)
  1277.         uri = jobdata.get('job-printer-uri', '')
  1278.         
  1279.         try:
  1280.             printer = self.printer_uri_index.lookup(uri)
  1281.         except KeyError:
  1282.             printer = uri
  1283.  
  1284.         jobdata['job-printer-name'] = printer
  1285.         any_active = len(self.active_jobs) > 0
  1286.         if self.job_is_active(jobdata):
  1287.             self.active_jobs.add(jobid)
  1288.         elif jobid in self.active_jobs:
  1289.             self.active_jobs.remove(jobid)
  1290.         
  1291.         if (len(self.active_jobs) > 0) != any_active:
  1292.             self.update_status()
  1293.         
  1294.         self.update_job(jobid, jobdata)
  1295.         jobdata = self.jobs[jobid]
  1296.         if self.trayicon:
  1297.             if (eventname == 'job-completed' or eventname == 'job-state-changed') and event['job-state'] == cups.IPP_JOB_COMPLETED:
  1298.                 reasons = event['job-state-reasons']
  1299.                 if type(reasons) != list:
  1300.                     reasons = [
  1301.                         reasons]
  1302.                 
  1303.                 canceled = False
  1304.                 for reason in reasons:
  1305.                     if reason.startswith('job-canceled'):
  1306.                         canceled = True
  1307.                         break
  1308.                         continue
  1309.                 
  1310.                 if not canceled:
  1311.                     self.notify_completed_job(jobid)
  1312.                 
  1313.             
  1314.         if self.trayicon and eventname == 'job-stopped' and jobid not in self.stopped_job_prompts:
  1315.             may_be_problem = True
  1316.             jstate = jobdata['job-state']
  1317.             if (jstate in [
  1318.                 cups.IPP_JOB_PENDING,
  1319.                 cups.IPP_JOB_PROCESSING] or jstate == cups.IPP_JOB_HELD) and jobdata['job-hold-until'] == 'auth-info-required':
  1320.                 may_be_problem = False
  1321.             else:
  1322.                 notify_text = event['notify-text']
  1323.                 document = jobdata['job-name']
  1324.                 if notify_text.find('backend errors') != -1:
  1325.                     message = _("There was a problem sending document `%s' (job %d) to the printer.") % (document, jobid)
  1326.                 elif notify_text.find('filter errors') != -1:
  1327.                     message = _("There was a problem processing document `%s' (job %d).") % (document, jobid)
  1328.                 elif notify_text.find('being paused') != -1:
  1329.                     may_be_problem = False
  1330.                 else:
  1331.                     message = _("There was a problem printing document `%s' (job %d): `%s'.") % (document, jobid, notify_text)
  1332.             if may_be_problem:
  1333.                 debugprint('Problem detected')
  1334.                 self.toggle_window_display(self.statusicon, force_show = True)
  1335.                 dialog = gtk.Dialog(_('Print Error'), self.JobsWindow, 0, (_('_Diagnose'), gtk.RESPONSE_NO, gtk.STOCK_OK, gtk.RESPONSE_OK))
  1336.                 dialog.set_default_response(gtk.RESPONSE_OK)
  1337.                 dialog.set_border_width(6)
  1338.                 dialog.set_resizable(False)
  1339.                 dialog.set_icon_name(ICON)
  1340.                 hbox = gtk.HBox(False, 12)
  1341.                 hbox.set_border_width(6)
  1342.                 image = gtk.Image()
  1343.                 image.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
  1344.                 hbox.pack_start(image, False, False, 0)
  1345.                 vbox = gtk.VBox(False, 12)
  1346.                 markup = '<span weight="bold" size="larger">' + _('Print Error') + '</span>\n\n' + message
  1347.                 
  1348.                 try:
  1349.                     if event['printer-state'] == cups.IPP_PRINTER_STOPPED:
  1350.                         name = event['printer-name']
  1351.                         markup += ' '
  1352.                         markup += _("The printer called `%s' has been disabled.") % name
  1353.                 except KeyError:
  1354.                     pass
  1355.  
  1356.                 label = gtk.Label(markup)
  1357.                 label.set_use_markup(True)
  1358.                 label.set_line_wrap(True)
  1359.                 label.set_alignment(0, 0)
  1360.                 vbox.pack_start(label, False, False, 0)
  1361.                 hbox.pack_start(vbox, False, False, 0)
  1362.                 dialog.vbox.pack_start(hbox)
  1363.                 dialog.connect('response', self.print_error_dialog_response, jobid)
  1364.                 self.stopped_job_prompts.add(jobid)
  1365.                 dialog.show_all()
  1366.             
  1367.         
  1368.  
  1369.     
  1370.     def job_removed(self, mon, jobid, eventname, event):
  1371.         monitor.Watcher.job_removed(self, mon, jobid, eventname, event)
  1372.         if self.trayicon:
  1373.             if (eventname == 'job-completed' or eventname == 'job-state-changed') and event['job-state'] == cups.IPP_JOB_COMPLETED:
  1374.                 reasons = event['job-state-reasons']
  1375.                 debugprint(reasons)
  1376.                 if type(reasons) != list:
  1377.                     reasons = [
  1378.                         reasons]
  1379.                 
  1380.                 canceled = False
  1381.                 for reason in reasons:
  1382.                     if reason.startswith('job-canceled'):
  1383.                         canceled = True
  1384.                         break
  1385.                         continue
  1386.                 
  1387.                 if not canceled:
  1388.                     self.notify_completed_job(jobid)
  1389.                 
  1390.             
  1391.         if self.jobiters.has_key(jobid):
  1392.             self.store.remove(self.jobiters[jobid])
  1393.             del self.jobiters[jobid]
  1394.             del self.jobs[jobid]
  1395.         
  1396.         if jobid in self.active_jobs:
  1397.             self.active_jobs.remove(jobid)
  1398.         
  1399.         self.update_status()
  1400.  
  1401.     
  1402.     def state_reason_added(self, mon, reason):
  1403.         monitor.Watcher.state_reason_added(self, mon, reason)
  1404.         (title, text) = reason.get_description()
  1405.         printer = reason.get_printer()
  1406.         
  1407.         try:
  1408.             l = self.printer_state_reasons[printer]
  1409.         except KeyError:
  1410.             l = []
  1411.             self.printer_state_reasons[printer] = l
  1412.  
  1413.         reason.user_notified = False
  1414.         l.append(reason)
  1415.         self.update_status()
  1416.         if not self.trayicon:
  1417.             return None
  1418.         for job, data in self.jobs.iteritems():
  1419.             if not self.job_is_active(data):
  1420.                 continue
  1421.             
  1422.             if data['job-printer-name'] == printer:
  1423.                 self.notify_printer_state_reason_if_important(reason)
  1424.                 break
  1425.                 continue
  1426.         
  1427.  
  1428.     
  1429.     def state_reason_removed(self, mon, reason):
  1430.         monitor.Watcher.state_reason_removed(self, mon, reason)
  1431.         printer = reason.get_printer()
  1432.         
  1433.         try:
  1434.             reasons = self.printer_state_reasons[printer]
  1435.         except KeyError:
  1436.             debugprint('Printer not found')
  1437.             return None
  1438.  
  1439.         
  1440.         try:
  1441.             i = reasons.index(reason)
  1442.         except IndexError:
  1443.             debugprint('Reason not found')
  1444.             return None
  1445.  
  1446.         del reasons[i]
  1447.         self.update_status()
  1448.         if not self.trayicon:
  1449.             return None
  1450.         tuple = reason.get_tuple()
  1451.         
  1452.         try:
  1453.             notification = self.state_reason_notifications[tuple]
  1454.             if notification.get_data('closed') != True:
  1455.                 notification.close()
  1456.             
  1457.             del self.state_reason_notifications[tuple]
  1458.             self.set_statusicon_visibility()
  1459.         except KeyError:
  1460.             self.trayicon
  1461.             self.trayicon
  1462.         except:
  1463.             self.trayicon
  1464.  
  1465.  
  1466.     
  1467.     def still_connecting(self, mon, reason):
  1468.         monitor.Watcher.still_connecting(self, mon, reason)
  1469.         if not self.trayicon:
  1470.             return None
  1471.         self.notify_printer_state_reason(reason)
  1472.  
  1473.     
  1474.     def now_connected(self, mon, printer):
  1475.         monitor.Watcher.now_connected(self, mon, printer)
  1476.         if not self.trayicon:
  1477.             return None
  1478.         
  1479.         try:
  1480.             reasons = self.printer_state_reasons[printer]
  1481.             reason = None
  1482.             for r in reasons:
  1483.                 if r.get_reason() == 'connecting-to-device':
  1484.                     reason = r
  1485.                     break
  1486.                     continue
  1487.                 self.trayicon
  1488.         except KeyError:
  1489.             self.trayicon
  1490.             self.trayicon
  1491.             debugprint("Couldn't find state reason (no reasons)!")
  1492.         except:
  1493.             self.trayicon
  1494.  
  1495.         if reason != None:
  1496.             tuple = reason.get_tuple()
  1497.         else:
  1498.             debugprint("Couldn't find state reason in list!")
  1499.             for level, p, r in self.state_reason_notifications.keys():
  1500.                 if p == printer and r == 'connecting-to-device':
  1501.                     debugprint('Found from notifications list')
  1502.                     tuple = (level, p, r)
  1503.                     break
  1504.                     continue
  1505.             
  1506.         
  1507.         try:
  1508.             notification = self.state_reason_notifications[tuple]
  1509.         except KeyError:
  1510.             debugprint('Unexpected now_connected signal')
  1511.             return None
  1512.  
  1513.         if notification.get_data('closed') != True:
  1514.             notification.close()
  1515.             notification.set_data('closed', True)
  1516.         
  1517.  
  1518.     
  1519.     def printer_event(self, mon, printer, eventname, event):
  1520.         monitor.Watcher.printer_event(self, mon, printer, eventname, event)
  1521.         self.printer_uri_index.update_from_attrs(printer, event)
  1522.  
  1523.     
  1524.     def printer_removed(self, mon, printer):
  1525.         monitor.Watcher.printer_removed(self, mon, printer)
  1526.         self.printer_uri_index.remove_printer(printer)
  1527.  
  1528.     
  1529.     def set_printer_status_icon(self, column, cell, model, iter, *user_data):
  1530.         level = model.get_value(iter, 0)
  1531.         icon = StateReason.LEVEL_ICON[level]
  1532.         theme = gtk.icon_theme_get_default()
  1533.         
  1534.         try:
  1535.             pixbuf = theme.load_icon(icon, 22, 0)
  1536.             cell.set_property('pixbuf', pixbuf)
  1537.         except gobject.GError:
  1538.             exc = None
  1539.  
  1540.  
  1541.     
  1542.     def set_printer_status_name(self, column, cell, model, iter, *user_data):
  1543.         cell.set_property('text', model.get_value(iter, 1))
  1544.  
  1545.  
  1546.