Back to index

system-config-printer  1.3.9+20120706
newprinter.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 
00003 ## system-config-printer
00004 
00005 ## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
00006 ## Authors:
00007 ##  Tim Waugh <twaugh@redhat.com>
00008 ##  Florian Festi <ffesti@redhat.com>
00009 
00010 ## This program is free software; you can redistribute it and/or modify
00011 ## it under the terms of the GNU General Public License as published by
00012 ## the Free Software Foundation; either version 2 of the License, or
00013 ## (at your option) any later version.
00014 
00015 ## This program is distributed in the hope that it will be useful,
00016 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 ## GNU General Public License for more details.
00019 
00020 ## You should have received a copy of the GNU General Public License
00021 ## along with this program; if not, write to the Free Software
00022 ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023 
00024 # config is generated from config.py.in by configure
00025 import config
00026 
00027 import authconn
00028 import cupshelpers
00029 
00030 import errno
00031 import sys, os, tempfile, time, traceback, re, httplib
00032 import locale
00033 import string
00034 import subprocess
00035 import thread
00036 from timedops import *
00037 import dbus
00038 import gtk
00039 
00040 import cups
00041 
00042 try:
00043     import pysmb
00044     PYSMB_AVAILABLE=True
00045 except:
00046     PYSMB_AVAILABLE=False
00047 
00048 import cupshelpers, options
00049 import gobject
00050 from gui import GtkGUI
00051 from optionwidgets import OptionWidget
00052 from debug import *
00053 import probe_printer
00054 
00055 try:
00056     # label_set_autowrap()
00057     import slip.gtk
00058 except:
00059     import gtk_label_autowrap
00060 
00061 import urllib
00062 from smburi import SMBURI
00063 from errordialogs import *
00064 from PhysicalDevice import PhysicalDevice
00065 import gtkspinner
00066 import firewall
00067 import asyncconn
00068 import ppdsloader
00069 import dnssdresolve
00070 
00071 from gettext import gettext as _
00072 
00073 TEXT_adjust_firewall = _("The firewall may need adjusting in order to "
00074                          "detect network printers.  Adjust the "
00075                          "firewall now?")
00076 
00077 def validDeviceURI (uri):
00078     """Returns True is the provided URI is valid."""
00079     (scheme, rest) = urllib.splittype (uri)
00080     if scheme == None or scheme == '':
00081         return False
00082     return True
00083 
00084 # Both the printer properties window and the new printer window
00085 # need to be able to drive 'class members' selections.
00086 def moveClassMembers(treeview_from, treeview_to):
00087     selection = treeview_from.get_selection()
00088     model_from, rows = selection.get_selected_rows()
00089     rows = [gtk.TreeRowReference(model_from, row) for row in rows]
00090 
00091     model_to = treeview_to.get_model()
00092 
00093     for row in rows:
00094         path = row.get_path()
00095         iter = model_from.get_iter(path)
00096         row_data = model_from.get(iter, 0)
00097         model_to.append(row_data)
00098         model_from.remove(iter)
00099 
00100 def getCurrentClassMembers(treeview):
00101     model = treeview.get_model()
00102     iter = model.get_iter_root()
00103     result = []
00104     while iter:
00105         result.append(model.get(iter, 0)[0])
00106         iter = model.iter_next(iter)
00107     result.sort()
00108     return result
00109 
00110 def checkNPName(printers, name):
00111     if not name: return False
00112     name = unicode (name.lower())
00113     for printer in printers.values():
00114         if not printer.discovered and printer.name.lower()==name:
00115             return False
00116     return True
00117 
00118 def ready (win, cursor=None):
00119     try:
00120         gdkwin = win.window
00121         if gdkwin:
00122             gdkwin.set_cursor (cursor)
00123             while gtk.events_pending ():
00124                 gtk.main_iteration ()
00125     except:
00126         nonfatalException ()
00127 
00128 def busy (win):
00129     ready (win, gtk.gdk.Cursor(gtk.gdk.WATCH))
00130 
00131 def on_delete_just_hide (widget, event):
00132     widget.hide ()
00133     return True # stop other handlers
00134 
00135 def _singleton (x):
00136     """If we don't know whether getPPDs() or getPPDs2() was used, this
00137     function can unwrap an item from a list in either case."""
00138     if isinstance (x, list):
00139         return x[0]
00140     return x
00141 
00142 class NewPrinterGUI(GtkGUI):
00143 
00144     __gsignals__ = {
00145         'destroy':          (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, []),
00146         'printer-added' :   (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
00147                              [gobject.TYPE_STRING]),
00148         'printer-modified': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
00149                              [gobject.TYPE_STRING, # printer name
00150                               gobject.TYPE_BOOLEAN]), # PPD modified?
00151         'dialog-canceled':  (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [])
00152         }
00153 
00154     new_printer_device_tabs = {
00155         "parallel" : 0, # empty tab
00156         "usb" : 0,
00157         "bluetooth" : 0,
00158         "hal" : 0,
00159         "beh" : 0,
00160         "hp" : 0,
00161         "hpfax" : 0,
00162         "dnssd" : 0,
00163         "socket": 2,
00164         "lpd" : 3,
00165         "scsi" : 4,
00166         "serial" : 5,
00167         "smb" : 6,
00168         "network": 7,
00169         }
00170 
00171     DOWNLOADABLE_ONLYPPD=True
00172 
00173     def __init__(self):
00174         gobject.GObject.__init__ (self)
00175         self.language = locale.getlocale (locale.LC_MESSAGES)
00176 
00177         self.options = {} # keyword -> Option object
00178         self.changed = set()
00179         self.conflicts = set()
00180         self.device = None
00181         self.ppd = None
00182         self.remotecupsqueue = False
00183         self.exactdrivermatch = False
00184         self.installable_options = False
00185         self.ppdsloader = None
00186         self.jockey_installed_files = []
00187         self.printers = {} # set in init()
00188 
00189         # Synchronisation objects.
00190         self.drivers_lock = thread.allocate_lock()
00191 
00192         self.getWidgets({"NewPrinterWindow":
00193                              ["NewPrinterWindow",
00194                               "ntbkNewPrinter",
00195                               "btnNPBack",
00196                               "btnNPForward",
00197                               "btnNPApply",
00198                               "imgProcessWorking",
00199                               "entNPName",
00200                               "entNPDescription",
00201                               "entNPLocation",
00202                               "tvNPDevices",
00203                               "ntbkNPType",
00204                               "lblNPDeviceDescription",
00205                               "expNPDeviceURIs",
00206                               "tvNPDeviceURIs",
00207                               "cmbNPTSerialBaud",
00208                               "cmbNPTSerialParity",
00209                               "cmbNPTSerialBits",
00210                               "cmbNPTSerialFlow",
00211                               "btnNPTLpdProbe",
00212                               "entNPTLpdHost",
00213                               "entNPTLpdQueue",
00214                               "entNPTJetDirectHostname",
00215                               "entNPTJetDirectPort",
00216                               "entSMBURI",
00217                               "btnSMBBrowse",
00218                               "tblSMBAuth",
00219                               "rbtnSMBAuthPrompt",
00220                               "rbtnSMBAuthSet",
00221                               "entSMBUsername",
00222                               "entSMBPassword",
00223                               "btnSMBVerify",
00224                               "entNPTNetworkHostname",
00225                               "btnNetworkFind",
00226                               "lblNetworkFindSearching",
00227                               "lblNetworkFindNotFound",
00228                               "entNPTDevice",
00229                               "tvNCMembers",
00230                               "tvNCNotMembers",
00231                               "btnNCAddMember",
00232                               "btnNCDelMember",
00233                               "ntbkPPDSource",
00234                               "rbtnNPPPD",
00235                               "tvNPMakes",
00236                               "rbtnNPFoomatic",
00237                               "filechooserPPD",
00238                               "rbtnNPDownloadableDriverSearch",
00239                               "entNPDownloadableDriverSearch",
00240                               "btnNPDownloadableDriverSearch",
00241                               "cmbNPDownloadableDriverFoundPrinters",
00242                               "tvNPModels",
00243                               "tvNPDrivers",
00244                               "rbtnChangePPDasIs",
00245                               "rbtnChangePPDKeepSettings",
00246                               "scrNPInstallableOptions",
00247                               "vbNPInstallOptions",
00248                               "tvNPDownloadableDrivers",
00249                               "ntbkNPDownloadableDriverProperties",
00250                               "lblNPDownloadableDriverSupplier",
00251                               "cbNPDownloadableDriverSupplierVendor",
00252                               "lblNPDownloadableDriverLicense",
00253                               "cbNPDownloadableDriverLicensePatents",
00254                               "cbNPDownloadableDriverLicenseFree",
00255                               "lblNPDownloadableDriverDescription",
00256                               "lblNPDownloadableDriverSupportContacts",
00257                               "hsDownloadableDriverPerfText",
00258                               "hsDownloadableDriverPerfLineArt",
00259                               "hsDownloadableDriverPerfGraphics",
00260                               "hsDownloadableDriverPerfPhoto",
00261                               "lblDownloadableDriverPerfTextUnknown",
00262                               "lblDownloadableDriverPerfLineArtUnknown",
00263                               "lblDownloadableDriverPerfGraphicsUnknown",
00264                               "lblDownloadableDriverPerfPhotoUnknown",
00265                               "frmNPDownloadableDriverLicenseTerms",
00266                               "tvNPDownloadableDriverLicense",
00267                               "rbtnNPDownloadLicenseYes",
00268                               "rbtnNPDownloadLicenseNo"],
00269                          "WaitWindow":
00270                              ["WaitWindow",
00271                               "lblWait"],
00272                          "SMBBrowseDialog":
00273                              ["SMBBrowseDialog",
00274                               "tvSMBBrowser",
00275                               "btnSMBBrowseOk"]},
00276 
00277                         domain=config.PACKAGE)
00278 
00279         # Fill in liststores for combo-box widgets
00280         for (widget,
00281              opts) in [(self.cmbNPTSerialBaud,
00282                         [[_("Default")],
00283                          [_("1200")],
00284                          [_("2400")],
00285                          [_("4800")],
00286                          [_("9600")],
00287                          [_("19200")],
00288                          [_("38400")],
00289                          [_("57600")],
00290                          [_("115200")]]),
00291 
00292                        (self.cmbNPTSerialParity,
00293                         [[_("Default")],
00294                          [_("None")],
00295                          [_("Odd")],
00296                          [_("Even")]]),
00297 
00298                        (self.cmbNPTSerialBits,
00299                         [[_("Default")],
00300                          [_("8")],
00301                          [_("7")]]),
00302 
00303                        (self.cmbNPTSerialFlow,
00304                         [[_("Default")],
00305                          [_("None")],
00306                          [_("XON/XOFF (Software)")],
00307                          [_("RTS/CTS (Hardware)")],
00308                          [_("DTR/DSR (Hardware)")]]),
00309 
00310                        ]:
00311             model = gtk.ListStore (gobject.TYPE_STRING)
00312             for row in opts:
00313                 model.append (row=row)
00314 
00315             cell = gtk.CellRendererText ()
00316             widget.pack_start (cell, True)
00317             widget.add_attribute (cell, 'text', 0)
00318             widget.set_model (model)
00319 
00320         # Set up some lists
00321         m = gtk.SELECTION_MULTIPLE
00322         s = gtk.SELECTION_SINGLE
00323         b = gtk.SELECTION_BROWSE
00324         for name, model, treeview, selection_mode in (
00325             (_("Members of this class"), gtk.ListStore(str),
00326              self.tvNCMembers, m),
00327             (_("Others"), gtk.ListStore(str), self.tvNCNotMembers, m),
00328             (_("Devices"), gtk.ListStore(str), self.tvNPDevices, s),
00329             (_("Connections"), gtk.ListStore(str), self.tvNPDeviceURIs, s),
00330             (_("Makes"), gtk.ListStore(str, str), self.tvNPMakes,s),
00331             (_("Models"), gtk.ListStore(str, str), self.tvNPModels,s),
00332             (_("Drivers"), gtk.ListStore(str), self.tvNPDrivers,s),
00333             (_("Downloadable Drivers"), gtk.ListStore(str),
00334              self.tvNPDownloadableDrivers, b),
00335             ):
00336 
00337             cell = gtk.CellRendererText()
00338             column = gtk.TreeViewColumn(name, cell, text=0)
00339             treeview.set_model(model)
00340             treeview.append_column(column)
00341             treeview.get_selection().set_mode(selection_mode)
00342 
00343         # Since some dialogs are reused we can't let the delete-event's
00344         # default handler destroy them
00345         self.SMBBrowseDialog.connect ("delete-event", on_delete_just_hide)
00346         self.WaitWindow_handler = self.WaitWindow.connect ("delete-event",
00347                                                            on_delete_just_hide)
00348 
00349         try:
00350             slip.gtk.label_set_autowrap(self.NewPrinterWindow)
00351         except: # no slip.gtk module
00352             gtk_label_autowrap.set_autowrap(self.NewPrinterWindow)
00353 
00354         self.ntbkNewPrinter.set_show_tabs(False)
00355         self.ntbkPPDSource.set_show_tabs(False)
00356         self.ntbkNPType.set_show_tabs(False)
00357         self.ntbkNPDownloadableDriverProperties.set_show_tabs(False)
00358 
00359         self.spinner = gtkspinner.Spinner (self.imgProcessWorking)
00360         self.spinner_count = 0
00361 
00362         # Set up OpenPrinting widgets.
00363         self.openprinting = cupshelpers.openprinting.OpenPrinting ()
00364         self.openprinting_query_handle = None
00365         combobox = self.cmbNPDownloadableDriverFoundPrinters
00366         cell = gtk.CellRendererText()
00367         combobox.pack_start (cell, True)
00368         combobox.add_attribute(cell, 'text', 0)
00369         if self.DOWNLOADABLE_ONLYPPD:
00370             for widget in [self.cbNPDownloadableDriverLicenseFree,
00371                            self.cbNPDownloadableDriverLicensePatents]:
00372                 widget.hide ()
00373 
00374         def protect_toggle (toggle_widget):
00375             active = toggle_widget.get_data ('protect_active')
00376             if active != None:
00377                 toggle_widget.set_active (active)
00378 
00379         for widget in [self.cbNPDownloadableDriverSupplierVendor,
00380                        self.cbNPDownloadableDriverLicenseFree,
00381                        self.cbNPDownloadableDriverLicensePatents]:
00382             widget.connect ('clicked', protect_toggle)
00383 
00384         for widget in [self.hsDownloadableDriverPerfText,
00385                        self.hsDownloadableDriverPerfLineArt,
00386                        self.hsDownloadableDriverPerfGraphics,
00387                        self.hsDownloadableDriverPerfPhoto]:
00388             widget.connect ('change-value',
00389                             lambda x, y, z: True)
00390 
00391         # Device list
00392         slct = self.tvNPDevices.get_selection ()
00393         slct.set_select_function (self.device_select_function)
00394         self.tvNPDevices.set_row_separator_func (self.device_row_separator_fn)
00395         self.tvNPDevices.connect ("row-activated", self.device_row_activated)
00396 
00397         # Devices expander
00398         self.expNPDeviceURIs.connect ("notify::expanded",
00399                                       self.on_expNPDeviceURIs_expanded)
00400         self.expNPDeviceURIs.set_expanded(1)
00401 
00402         # SMB browser
00403         self.smb_store = gtk.TreeStore (gobject.TYPE_PYOBJECT)
00404         self.btnSMBBrowse.set_sensitive (PYSMB_AVAILABLE)
00405         if not PYSMB_AVAILABLE:
00406             self.btnSMBBrowse.set_tooltip_text (_("Browsing not available "
00407                                                   "(pysmbc not installed)"))
00408 
00409         self.tvSMBBrowser.set_model (self.smb_store)
00410 
00411         # SMB list columns
00412         col = gtk.TreeViewColumn (_("Share"))
00413         cell = gtk.CellRendererText ()
00414         col.pack_start (cell, False)
00415         col.set_cell_data_func (cell, self.smbbrowser_cell_share)
00416         self.tvSMBBrowser.append_column (col)
00417 
00418         col = gtk.TreeViewColumn (_("Comment"))
00419         cell = gtk.CellRendererText ()
00420         col.pack_start (cell, False)
00421         col.set_cell_data_func (cell, self.smbbrowser_cell_comment)
00422         self.tvSMBBrowser.append_column (col)
00423 
00424         slct = self.tvSMBBrowser.get_selection ()
00425         slct.set_select_function (self.smb_select_function)
00426 
00427         self.SMBBrowseDialog.set_transient_for(self.NewPrinterWindow)
00428 
00429         self.tvNPDrivers.set_has_tooltip(True)
00430         self.tvNPDrivers.connect("query-tooltip", self.on_NPDrivers_query_tooltip)
00431 
00432         ppd_filter = gtk.FileFilter()
00433         ppd_filter.set_name(_("PostScript Printer Description files (*.ppd, *.PPD, *.ppd.gz, *.PPD.gz, *.PPD.GZ)"))
00434         ppd_filter.add_pattern("*.ppd")
00435         ppd_filter.add_pattern("*.PPD")
00436         ppd_filter.add_pattern("*.ppd.gz")
00437         ppd_filter.add_pattern("*.PPD.gz")
00438         ppd_filter.add_pattern("*.PPD.GZ")
00439         self.filechooserPPD.add_filter(ppd_filter)
00440 
00441         ppd_filter = gtk.FileFilter()
00442         ppd_filter.set_name(_("All files (*)"))
00443         ppd_filter.add_pattern("*")
00444         self.filechooserPPD.add_filter(ppd_filter)
00445         debugprint ("+%s" % self)
00446 
00447     def __del__ (self):
00448         debugprint ("-%s" % self)
00449 
00450     def do_destroy (self):
00451         debugprint ("DESTROY: %s" % self)
00452         if self.SMBBrowseDialog:
00453             self.SMBBrowseDialog.destroy ()
00454             self.SMBBrowseDialog = None
00455 
00456         if self.NewPrinterWindow:
00457             self.NewPrinterWindow.destroy ()
00458             self.NewPrinterWindow = None
00459 
00460         if self.WaitWindow:
00461             self.WaitWindow.destroy ()
00462             self.WaitWindow = None
00463 
00464     def inc_spinner_task (self):
00465         if self.spinner_count == 0:
00466             self.imgProcessWorking.show ()
00467             self.spinner.start ()
00468 
00469         self.spinner_count += 1
00470 
00471     def dec_spinner_task (self):
00472         self.spinner_count -= 1
00473         if self.spinner_count == 0:
00474             self.imgProcessWorking.hide ()
00475             self.spinner.stop ()
00476 
00477     def show_IPP_Error (self, exception, message):
00478         return show_IPP_Error (exception, message, parent=self.NewPrinterWindow)
00479 
00480     def option_changed(self, option):
00481         if option.is_changed():
00482             self.changed.add(option)
00483         else:
00484             self.changed.discard(option)
00485 
00486         if option.conflicts:
00487             self.conflicts.add(option)
00488         else:
00489             self.conflicts.discard(option)
00490         self.setDataButtonState()
00491 
00492         return
00493 
00494     def setDataButtonState(self):
00495         self.btnNPForward.set_sensitive(not bool(self.conflicts))
00496 
00497     def makeNameUnique(self, name):
00498         """Make a suggested queue name valid and unique."""
00499         name = name.replace (" ", "-")
00500         name = name.replace ("/", "-")
00501         name = name.replace ("#", "-")
00502         if not checkNPName (self.printers, name):
00503             suffix=2
00504             while not checkNPName (self.printers, name + "-" + str (suffix)):
00505                 suffix += 1
00506                 if suffix == 100:
00507                     break
00508             name += "-" + str (suffix)
00509         return name
00510 
00511     def destroy (self):
00512         self.emit ('destroy')
00513 
00514     def init(self, dialog_mode, device_uri=None, name=None, ppd=None,
00515              devid="", host=None, encryption=None, parent=None, xid=0):
00516         if xid != 0:
00517             display = gtk.gdk.display_get_default ()
00518             parent = gtk.gdk.window_foreign_new_for_display (display, xid)
00519             debugprint ("Parent is %s" % parent)
00520             self.parent = parent
00521         else:
00522             self.parent = parent
00523             if not self.parent:
00524                 self.NewPrinterWindow.set_focus_on_map (False)
00525             
00526         self.dialog_mode = dialog_mode
00527         self.orig_ppd = ppd
00528         self.devid = devid
00529         self._host = host
00530         self._encryption = encryption
00531         self._name = name
00532         if not host:
00533             self._host = cups.getServer ()
00534         if not encryption:
00535             self._encryption = cups.getEncryption ()
00536 
00537         self.options = {} # keyword -> Option object
00538         self.changed = set()
00539         self.conflicts = set()
00540         self.fetchDevices_conn = None
00541         self.ppds = None
00542         self.ppdsmatch_result = None
00543         self.printer_finder = None
00544 
00545         # Get a current list of printers so that we can know whether
00546         # the chosen name is unique.
00547         try:
00548             self.cups = authconn.Connection (parent=self.NewPrinterWindow,
00549                                              host=self._host,
00550                                              encryption=self._encryption)
00551         except cups.HTTPError, (s,):
00552             show_HTTP_Error (s, self.parent)
00553             return False
00554         except RuntimeError:
00555             show_HTTP_Error (-1, self.parent)
00556             return False
00557         except Exception, e:
00558             nonfatalException (e)
00559             return False
00560 
00561         try:
00562             self.printers = cupshelpers.getPrinters (self.cups)
00563         except cups.IPPError, (e, m):
00564             show_IPP_Error (e, m, parent=self.parent)
00565             return False
00566 
00567         # Initialise widgets.
00568         self.lblNetworkFindSearching.hide ()
00569         self.entNPTNetworkHostname.set_sensitive (True)
00570         self.entNPTNetworkHostname.set_text ('')
00571         self.btnNetworkFind.set_sensitive (True)
00572         self.lblNetworkFindNotFound.hide ()
00573 
00574         # Clear out any previous list of makes.
00575         model = self.tvNPMakes.get_model()
00576         model.clear()
00577 
00578         if device_uri == None and dialog_mode in ['printer_with_uri',
00579                                                   'device',
00580                                                   'ppd']:
00581             raise RuntimeError
00582 
00583         combobox = self.cmbNPDownloadableDriverFoundPrinters
00584         combobox.set_model (gtk.ListStore (str, str))
00585         self.entNPDownloadableDriverSearch.set_text ('')
00586         button = self.btnNPDownloadableDriverSearch
00587         label = button.get_children ()[0].get_children ()[0].get_children ()[1]
00588         self.btnNPDownloadableDriverSearch_label = label
00589         label.set_text (_("Search"))
00590 
00591         if self.dialog_mode in ("printer", "printer_with_uri", "class"):
00592             if self.dialog_mode == "class":
00593                 name_proto = "class"
00594             else:
00595                 name_proto = "printer"
00596             self.entNPName.set_text (self.makeNameUnique(name_proto))
00597             self.entNPName.grab_focus()
00598             for widget in [self.entNPLocation,
00599                            self.entNPDescription,
00600                            self.entSMBURI, self.entSMBUsername,
00601                            self.entSMBPassword]:
00602                 widget.set_text('')
00603 
00604         if self.dialog_mode in ['printer_with_uri', 'ppd']:
00605             device_dict = { }
00606             self.device = cupshelpers.Device (device_uri, **device_dict)
00607 
00608         self.entNPTJetDirectPort.set_text('9100')
00609         self.rbtnSMBAuthPrompt.set_active(True)
00610 
00611         if xid != 0 and self.parent:
00612             self.NewPrinterWindow.show_now()
00613             self.NewPrinterWindow.window.set_transient_for (self.parent)
00614 
00615         if self.dialog_mode == "printer":
00616             self.NewPrinterWindow.set_title(_("New Printer"))
00617             # Start on devices page (1, not 0)
00618             self.ntbkNewPrinter.set_current_page(1)
00619             self.fillDeviceTab()
00620             self.rbtnNPFoomatic.set_active (True)
00621             self.on_rbtnNPFoomatic_toggled(self.rbtnNPFoomatic)
00622 
00623         elif self.dialog_mode == "class":
00624             self.NewPrinterWindow.set_title(_("New Class"))
00625             self.fillNewClassMembers()
00626             # Start on name page
00627             self.ntbkNewPrinter.set_current_page(0)
00628         elif self.dialog_mode == "device":
00629             self.NewPrinterWindow.set_title(_("Change Device URI"))
00630             self.ntbkNewPrinter.set_current_page(1)
00631             self.fillDeviceTab(device_uri)
00632         elif self.dialog_mode == "ppd" or \
00633             self.dialog_mode == "printer_with_uri":
00634             if self.dialog_mode == "ppd":
00635                 self.NewPrinterWindow.set_title(_("Change Driver"))
00636             else:
00637                 self.NewPrinterWindow.set_title(_("New Printer"))
00638 
00639             # We'll need to know the Device ID for this device.
00640             if self.dialog_mode == "ppd" and not self.devid:
00641                 scheme = str(device_uri.split (":", 1)[0])
00642                 schemes = [scheme]
00643                 if scheme in ["socket", "lpd", "ipp"]:
00644                     schemes.extend (["snmp", "dnssd"])
00645                 self.fetchDevices_conn = asyncconn.Connection ()
00646                 self.fetchDevices_conn._begin_operation (_("fetching device "
00647                                                            "list"))
00648                 self.inc_spinner_task ()
00649                 cupshelpers.getDevices (self.fetchDevices_conn,
00650                                         include_schemes=schemes,
00651                                         reply_handler=self.change_ppd_got_devs,
00652                                         error_handler=self.change_ppd_got_devs)
00653 
00654             self.ntbkNewPrinter.set_current_page(2)
00655             self.rbtnNPFoomatic.set_active (True)
00656             self.on_rbtnNPFoomatic_toggled(self.rbtnNPFoomatic)
00657             self.rbtnChangePPDKeepSettings.set_active(True)
00658 
00659             if self.dialog_mode == "printer_with_uri":
00660                 p = ppdsloader.PPDsLoader (device_id=devid,
00661                                            device_uri=device_uri,
00662                                            parent=self.NewPrinterWindow,
00663                                            host=self._host,
00664                                            encryption=self._encryption)
00665                 self.ppdsloader = p
00666                 p.connect ('finished',self.change_ppd_have_devs_and_ppds)
00667                 p.run ()
00668 
00669             self.auto_make = ""
00670             self.auto_model = ""
00671             self.auto_driver = None
00672 
00673         if xid == 0 and self.parent:
00674             self.NewPrinterWindow.set_transient_for (parent)
00675 
00676         self.NewPrinterWindow.show()
00677         self.setNPButtons()
00678         return True
00679 
00680     def on_ppdsloader_finished_initial (self, ppdsloader):
00681         """
00682         This method is called when the PPDs loader has finished
00683         loading PPDs in preparation for the first screen the user
00684         sees.  We are either changing the PPD of an existing queue
00685         (dialog_mode=="ppd") or else we are setting up a queue for a
00686         given device URI (dialog_mode=="printer_with_uri").
00687         """
00688 
00689         self._getPPDs_reply (ppdsloader)
00690         if not self.ppds:
00691             return
00692 
00693     def change_ppd_got_devs (self, conn, result):
00694         self.fetchDevices_conn._end_operation ()
00695         self.fetchDevices_conn.destroy ()
00696         self.fetchDevices_conn = None
00697         self.dec_spinner_task ()
00698         if isinstance (result, Exception):
00699             current_devices = {}
00700         else:
00701             current_devices = result
00702 
00703         devid = None
00704         mm = None
00705         if self.devid != "":
00706             devid = self.devid
00707         else:
00708             device = current_devices.get (self.device.uri)
00709             if device:
00710                 devid = device.id
00711                 mm = device.make_and_model
00712                 self.device = device
00713 
00714         # We'll also need the list of PPDs.
00715         self.ppdsloader = ppdsloader.PPDsLoader (device_id=devid,
00716                                                  device_uri=self.device.uri,
00717                                                  device_make_and_model=mm,
00718                                                  parent=self.NewPrinterWindow,
00719                                                  host=self._host,
00720                                                  encryption=self._encryption,
00721                                                  language=self.language[0])
00722         self.ppdsloader.connect ('finished',
00723                                  self.change_ppd_have_devs_and_ppds)
00724         self.ppdsloader.run ()
00725 
00726     def change_ppd_have_devs_and_ppds (self, ppdsloader):
00727         self._getPPDs_reply (ppdsloader)
00728         if not self.ppds:
00729             self.setNPButtons()
00730             return
00731 
00732         self.exactdrivermatch = False
00733         self.id_matched_ppdnames = []
00734         if self.ppdsmatch_result != None:
00735             try:
00736                 (fit, ppdnamelist) = self.ppdsmatch_result
00737                 self.id_matched_ppdnames = ppdnamelist
00738                 ppdname = ppdnamelist[0]
00739                 status = fit[ppdname]
00740                 ppddict = self.ppds.getInfoFromPPDName (ppdname)
00741                 make_model = _singleton (ppddict['ppd-make-and-model'])
00742                 (self.auto_make, self.auto_model) = \
00743                     cupshelpers.ppds.ppdMakeModelSplit (make_model)
00744                 self.auto_driver = ppdname
00745                 if (status.startswith ("exact") and
00746                     self.dialog_mode == "printer_with_uri"):
00747                     self.exactdrivermatch = True
00748                     self.fillMakeList()
00749                     self.ntbkNewPrinter.set_current_page(6)
00750                     self.nextNPTab(step = 0)
00751             except:
00752                 nonfatalException ()
00753         elif self.orig_ppd:
00754             attr = self.orig_ppd.findAttr("NickName")
00755             if not attr:
00756                 attr = self.orig_ppd.findAttr("ModelName")
00757 
00758             if attr and attr.value:
00759                 value = attr.value
00760                 if value.endswith (" (recommended)"):
00761                     value = value[:-14]
00762 
00763                 mfgmdl = cupshelpers.ppds.ppdMakeModelSplit (value)
00764                 (self.auto_make, self.auto_model) = mfgmdl
00765 
00766                 # Search for ppdname with that make-and-model
00767                 ppds = self.ppds.getInfoFromModel (self.auto_make,
00768                                                    self.auto_model)
00769                 for ppd, info in ppds.iteritems ():
00770                     if _singleton (info.
00771                                    get ("ppd-make-and-model")) == value:
00772                         self.auto_driver = ppd
00773                         break
00774         else:
00775             # Special CUPS names for a raw queue.
00776             self.auto_make = 'Generic'
00777             self.auto_model = 'Raw Queue'
00778 
00779         self.fillMakeList()
00780 
00781     def on_ppdsloader_finished_next (self, ppdsloader):
00782         """
00783         This method is called when the PPDs loader has finished
00784         loading PPDs in preparation for the next screen the user will
00785         see, having clicked 'Forward'.  We are creating a new queue,
00786         and dialog_mode is either "printer" or "printer_with_uri".
00787         """
00788 
00789         self._getPPDs_reply (ppdsloader)
00790         if not self.ppds:
00791             return
00792 
00793         debugprint ("Loaded PPDs this time; try nextNPTab again...")
00794         self.nextNPTab()
00795 
00796     # get PPDs
00797 
00798     def _getPPDs_reply (self, ppdsloader):
00799         exc = ppdsloader.get_error ()
00800         if exc:
00801             try:
00802                 raise exc
00803             except cups.IPPError, (e, m):
00804                 show_IPP_Error (e, m, parent=self.NewPrinterWindow)
00805                 return
00806 
00807         ppds = ppdsloader.get_ppds ()
00808         if ppds:
00809             self.ppds = ppds
00810             self.ppdsmatch_result = ppdsloader.get_ppdsmatch_result ()
00811             self.jockey_installed_files = ppdsloader.get_installed_files ()
00812         else:
00813             self.ppds = None
00814             self.ppdsmatch_result = None
00815 
00816         ppdsloader.destroy ()
00817         self.ppdsloader = None
00818 
00819     # Class members
00820 
00821     def fillNewClassMembers(self):
00822         model = self.tvNCMembers.get_model()
00823         model.clear()
00824         model = self.tvNCNotMembers.get_model()
00825         model.clear()
00826         try:
00827             self.printers = cupshelpers.getPrinters (self.cups)
00828         except cups.IPPError:
00829             self.printers = {}
00830 
00831         for printer in self.printers.keys():
00832             model.append((printer,))
00833 
00834     def on_btnNCAddMember_clicked(self, button):
00835         moveClassMembers(self.tvNCNotMembers, self.tvNCMembers)
00836         self.btnNPApply.set_sensitive(
00837             bool(getCurrentClassMembers(self.tvNCMembers)))
00838         button.set_sensitive(False)
00839 
00840     def on_btnNCDelMember_clicked(self, button):
00841         moveClassMembers(self.tvNCMembers, self.tvNCNotMembers)
00842         self.btnNPApply.set_sensitive(
00843             bool(getCurrentClassMembers(self.tvNCMembers)))
00844         button.set_sensitive(False)
00845 
00846     def on_tvNCMembers_cursor_changed(self, widget):
00847         selection = widget.get_selection()
00848         model_from, rows = selection.get_selected_rows()
00849         self.btnNCDelMember.set_sensitive(rows != [])
00850 
00851     def on_tvNCNotMembers_cursor_changed(self, widget):
00852         selection = widget.get_selection()
00853         model_from, rows = selection.get_selected_rows()
00854         self.btnNCAddMember.set_sensitive(rows != [])
00855 
00856     # Navigation buttons
00857 
00858     def on_NPCancel(self, widget, event=None):
00859         if self.fetchDevices_conn:
00860             self.fetchDevices_conn.destroy ()
00861             self.fetchDevices_conn = None
00862             self.dec_spinner_task ()
00863 
00864         if self.ppdsloader:
00865             self.ppdsloader.destroy ()
00866             self.ppds_loader = None
00867 
00868         if self.printer_finder:
00869             self.printer_finder.cancel ()
00870             self.printer_finder = None
00871             self.dec_spinner_task ()
00872 
00873         self.NewPrinterWindow.hide()
00874         if self.openprinting_query_handle != None:
00875             self.openprinting.cancelOperation (self.openprinting_query_handle)
00876             self.openprinting_query_handle = None
00877 
00878         self.device = None
00879         self.printers = {}
00880         self.emit ('dialog-canceled')
00881         return True
00882 
00883     def on_btnNPBack_clicked(self, widget):
00884         self.nextNPTab(-1)
00885 
00886     def on_btnNPForward_clicked(self, widget):
00887         self.nextNPTab()
00888 
00889     def nextNPTab(self, step=1):
00890         page_nr = self.ntbkNewPrinter.get_current_page()
00891 
00892         if self.dialog_mode == "class":
00893             order = [0, 4, 5]
00894         elif self.dialog_mode == "printer" or \
00895                 self.dialog_mode == "printer_with_uri":
00896             busy (self.NewPrinterWindow)
00897             if page_nr == 1: # Device (first page)
00898                 self.auto_make, self.auto_model = "", ""
00899                 self.auto_driver = None
00900                 self.device.uri = self.getDeviceURI()
00901 
00902                 # Cancel the printer finder now as the user has
00903                 # already selected their device.
00904                 if self.printer_finder:
00905                     self.printer_finder.cancel ()
00906                     self.printer_finder = None
00907                     self.dec_spinner_task ()
00908 
00909                 if (not self.device.id and
00910                     self.device.type in ["socket", "lpd", "ipp"]):
00911                     # This is a network printer whose model we don't yet know.
00912                     # Try to discover it.
00913                     self.getNetworkPrinterMakeModel ()
00914 
00915                 uri = self.device.uri
00916                 if uri and uri.startswith ("smb://"):
00917                     uri = SMBURI (uri=uri[6:]).sanitize_uri ()
00918 
00919                 # Try to access the PPD, in this case our detected IPP
00920                 # printer is a queue on a remote CUPS server which is
00921                 # not automatically set up on our local CUPS server
00922                 # (for example DNS-SD broadcasted queue from Mac OS X)
00923                 self.remotecupsqueue = None
00924                 res = re.search ("ipp://(\S+?)(:\d+|)/printers/(\S+)", uri)
00925                 if res:
00926                     resg = res.groups()
00927                     if len (resg[1]) > 0:
00928                         port = int (resg[1][1:])
00929                     else:
00930                         port = 631
00931                     try:
00932                         conn = httplib.HTTPConnection(resg[0], port)
00933                         conn.request("GET", "/printers/%s.ppd" % resg[2])
00934                         resp = conn.getresponse()
00935                         if resp.status == 200: self.remotecupsqueue = resg[2]
00936                     except:
00937                         pass
00938 
00939                     # We also want to fetch the printer-info and
00940                     # printer-location attributes, to pre-fill those
00941                     # fields for this new queue.
00942                     try:
00943                         encryption = cups.HTTP_ENCRYPT_IF_REQUESTED
00944                         c = cups.Connection (host=resg[0],
00945                                              port=port,
00946                                              encryption=encryption)
00947 
00948                         r = ['printer-info', 'printer-location']
00949                         attrs = c.getPrinterAttributes (uri=uri,
00950                                                         requested_attributes=r)
00951                         info = attrs.get ('printer-info', '')
00952                         location = attrs.get ('printer-location', '')
00953                         if len (info) > 0:
00954                             self.entNPDescription.set_text (info)
00955                         if len (location) > 0:
00956                             self.device.location = location
00957                     except RuntimeError:
00958                         pass
00959                     except:
00960                         nonfatalException ()
00961                 elif ((uri.startswith ("dnssd:") or uri.startswith("mdns:")) and
00962                       uri.find ("/cups") != -1 and
00963                       self.device.info):
00964                     # Remote CUPS queue discovered by "dnssd" CUPS backend
00965                     self.remotecupsqueue = self.device.info
00966 
00967                 if not self.remotecupsqueue:
00968                     if self.ppds == None:
00969                         devid = self.device.id # ID of selected device
00970                         if not devid:
00971                             devid = self.devid # ID supplied at init()
00972                         if not devid:
00973                             devid = None
00974 
00975                         debugprint ("nextNPTab: need PPDs loaded")
00976                         p = ppdsloader.PPDsLoader (device_id=devid,
00977                                                    device_uri=uri,
00978                                                    parent=self.NewPrinterWindow,
00979                                                    host=self._host,
00980                                                    encryption=self._encryption)
00981                         self.ppdsloader = p
00982                         p.connect ('finished',self.on_ppdsloader_finished_next)
00983                         p.run ()
00984                         ready (self.NewPrinterWindow)
00985                         return
00986 
00987                 if (hasattr (self.device, 'hp_scannable') and
00988                     self.device.hp_scannable and
00989                     not os.access ("/etc/sane.d/dll.d/hpaio", os.R_OK)):
00990                     try:
00991                         pk = installpackage.PackageKit ()
00992                         xid = self.NewPrinterWindow.window.xid
00993                         pk.InstallPackageName (xid, 0, "libsane-hpaio")
00994                     except:
00995                         pass
00996 
00997                 ppdname = None
00998                 self.id_matched_ppdnames = []
00999                 try:
01000                     if self.remotecupsqueue:
01001                         # We have a remote CUPS queue, let the client queue
01002                         # stay raw so that the driver on the server gets used
01003                         ppdname = 'raw'
01004                         self.ppd = ppdname
01005                         name = self.remotecupsqueue
01006                         name = self.makeNameUnique (name)
01007                         self.entNPName.set_text (name)
01008                     elif (self.device.id or
01009                           (self.device.make_and_model and
01010                            self.device.make_and_model != "Unknown")):
01011                         if self.device.id:
01012                             id_dict = self.device.id_dict
01013                         else:
01014                             id_dict = {}
01015                             (id_dict["MFG"],
01016                              id_dict["MDL"]) = cupshelpers.ppds.\
01017                                  ppdMakeModelSplit (self.device.make_and_model)
01018                             id_dict["DES"] = ""
01019                             id_dict["CMD"] = []
01020 
01021                         fit = self.ppds.\
01022                             getPPDNamesFromDeviceID (id_dict["MFG"],
01023                                                      id_dict["MDL"],
01024                                                      id_dict["DES"],
01025                                                      id_dict["CMD"],
01026                                                      self.device.uri,
01027                                                      self.device.make_and_model)
01028                         ppdnamelist = self.ppds.\
01029                             orderPPDNamesByPreference (fit.keys (),
01030                                                        self.jockey_installed_files,
01031                                                        devid=id_dict, fit=fit)
01032                         self.id_matched_ppdnames = ppdnamelist
01033                         ppdname = ppdnamelist[0]
01034                         status = fit[ppdname]
01035                     else:
01036                         (status, ppdname) = self.ppds.\
01037                             getPPDNameFromDeviceID ("Generic",
01038                                                     "Printer",
01039                                                     "Generic Printer",
01040                                                     [],
01041                                                     self.device.uri)
01042                         status = "generic"
01043 
01044                     if ppdname and not self.remotecupsqueue:
01045                         ppddict = self.ppds.getInfoFromPPDName (ppdname)
01046                         make_model = _singleton (ppddict['ppd-make-and-model'])
01047                         (make, model) = \
01048                             cupshelpers.ppds.ppdMakeModelSplit (make_model)
01049                         self.auto_make = make
01050                         self.auto_model = model
01051                         self.auto_driver = ppdname
01052                         if (status == "exact" and \
01053                             self.dialog_mode != "ppd"):
01054                             self.exactdrivermatch = True
01055                         else:
01056                             self.exactdrivermatch = False
01057                 except:
01058                     nonfatalException ()
01059 
01060                 if not self.remotecupsqueue:
01061                     self.fillMakeList()
01062             elif page_nr == 3: # Model has been selected
01063                 if not self.device.id:
01064                     # Choose an appropriate name when no Device ID
01065                     # is available, based on the model the user has
01066                     # selected.
01067                     try:
01068                         model, iter = self.tvNPModels.get_selection ().\
01069                                       get_selected ()
01070                         name = model.get(iter, 0)[0]
01071                         name = self.makeNameUnique (name)
01072                         self.entNPName.set_text (name)
01073                     except:
01074                         nonfatalException ()
01075 
01076             ready (self.NewPrinterWindow)
01077             if self.dialog_mode == "printer":
01078                 if self.remotecupsqueue:
01079                     order = [1, 0]
01080                 elif self.exactdrivermatch:
01081                     order = [1, 6, 0]
01082                 elif self.rbtnNPFoomatic.get_active():
01083                     order = [1, 2, 3, 6, 0]
01084                 elif self.rbtnNPPPD.get_active():
01085                     order = [1, 2, 6, 0]
01086                 else:
01087                     # Downloadable driver
01088                     order = [1, 2, 7, 6, 0]
01089             else:
01090                 if self.remotecupsqueue:
01091                     order = [0]
01092                 elif self.exactdrivermatch:
01093                     order = [6, 0]
01094                 elif self.rbtnNPFoomatic.get_active():
01095                     order = [2, 3, 6, 0]
01096                 elif self.rbtnNPPPD.get_active():
01097                     order = [2, 6, 0]
01098                 else:
01099                     # Downloadable driver
01100                     order = [2, 7, 6, 0]
01101         elif self.dialog_mode == "device":
01102             order = [1]
01103         elif self.dialog_mode == "ppd":
01104             if self.rbtnNPFoomatic.get_active():
01105                 order = [2, 3, 5, 6]
01106             elif self.rbtnNPPPD.get_active():
01107                 order = [2, 5, 6]
01108             else:
01109                 # Downloadable driver
01110                 order = [2, 7, 5, 6]
01111 
01112         next_page_nr = order[order.index(page_nr)+step]
01113 
01114         # fill Installable Options tab
01115         fetch_ppd = False
01116         try:
01117             if order.index (5) > -1:
01118                 # There is a copy settings page in this set
01119                 fetch_ppd = next_page_nr == 5 and step >= 0
01120         except ValueError:
01121             fetch_ppd = next_page_nr == 6 and step >= 0
01122 
01123         debugprint ("Will fetch ppd? %d" % fetch_ppd)
01124         if fetch_ppd:
01125             self.ppd = self.getNPPPD()
01126             self.installable_options = False
01127             if self.ppd == None:
01128                 return
01129 
01130             # Prepare Installable Options screen.
01131             if isinstance(self.ppd, cups.PPD):
01132                 self.fillNPInstallableOptions()
01133             else:
01134                 # Put a label there explaining why the page is empty.
01135                 ppd = self.ppd
01136                 self.ppd = None
01137                 self.fillNPInstallableOptions()
01138                 self.ppd = ppd
01139 
01140             if not self.installable_options:
01141                 if next_page_nr == 6:
01142                     # step over if empty
01143                     next_page_nr = order[order.index(next_page_nr)+1]
01144 
01145         # Step over empty Installable Options tab when moving backwards.
01146         if next_page_nr == 6 and not self.installable_options and step<0:
01147             next_page_nr = order[order.index(next_page_nr)-1]
01148 
01149         if step >= 0 and next_page_nr == 7: # About to show downloadable drivers
01150             if self.drivers_lock.locked ():
01151                 # Still searching for drivers.
01152                 self.lblWait.set_markup ('<span weight="bold" size="larger">' +
01153                                          _('Searching') + '</span>\n\n' +
01154                                          _('Searching for drivers'))
01155                 self.WaitWindow.set_transient_for (self.NewPrinterWindow)
01156                 self.WaitWindow.show ()
01157                 busy (self.WaitWindow)
01158                 busy (self.NewPrinterWindow)
01159 
01160                 # Keep the UI refreshed while we wait for the drivers
01161                 # query to complete.
01162                 while self.drivers_lock.locked ():
01163                     while gtk.events_pending ():
01164                         gtk.main_iteration ()
01165                     time.sleep (0.1)
01166 
01167                 ready (self.NewPrinterWindow)
01168                 self.WaitWindow.hide ()
01169 
01170             self.fillDownloadableDrivers()
01171 
01172         if step >= 0 and next_page_nr == 0: # About to choose a name.
01173             # Suggest an appropriate name.
01174             name = None
01175             descr = None
01176 
01177             try:
01178                 if (self.device.id and
01179                     not self.device.type in ("socket", "lpd", "ipp",
01180                                              "http", "https", "bluetooth")):
01181                     name = "%s %s" % (self.device.id_dict["MFG"], 
01182                                       self.device.id_dict["MDL"])
01183             except:
01184                 nonfatalException ()
01185 
01186             try:
01187                 if name == None and isinstance (self.ppd, cups.PPD):
01188                     mname = self.ppd.findAttr ("modelName").value
01189                     make, model = cupshelpers.ppds.ppdMakeModelSplit (mname)
01190                     if make and model:
01191                         name = "%s %s" % (make, model)
01192                     elif make or model:
01193                         name = "%s%s" % (make, model)
01194             except:
01195                 nonfatalException ()
01196 
01197             if name:
01198                 descr = name
01199             else:
01200                 name = 'printer'
01201 
01202             name = self.makeNameUnique (name)
01203             self.entNPName.set_text (name)
01204 
01205             if self.entNPDescription.get_text () == '' and descr:
01206                 self.entNPDescription.set_text (descr)
01207 
01208         self.ntbkNewPrinter.set_current_page(next_page_nr)
01209 
01210         self.setNPButtons()
01211 
01212     def setNPButtons(self):
01213         nr = self.ntbkNewPrinter.get_current_page()
01214 
01215         if self.dialog_mode == "device":
01216             self.btnNPBack.hide()
01217             self.btnNPForward.hide()
01218             self.btnNPApply.show()
01219             try:
01220                 uri = self.getDeviceURI ()
01221                 valid = validDeviceURI (uri)
01222             except AttributeError:
01223                 # No device selected yet.
01224                 valid = False
01225             self.btnNPApply.set_sensitive (valid)
01226             return
01227 
01228         if self.dialog_mode == "ppd":
01229             if nr == 5: # Apply
01230                 if not self.installable_options:
01231                     # There are no installable options, so this is the
01232                     # last page.
01233                     debugprint ("No installable options")
01234                     self.btnNPForward.hide ()
01235                     self.btnNPApply.show ()
01236                 else:
01237                     self.btnNPForward.show ()
01238                     self.btnNPApply.hide ()
01239                 return
01240             elif nr == 6:
01241                 self.btnNPForward.hide()
01242                 self.btnNPApply.show()
01243                 return
01244             else:
01245                 self.btnNPForward.show()
01246                 self.btnNPApply.hide()
01247             if nr == 2:
01248                 self.btnNPBack.hide()
01249                 self.btnNPForward.show()
01250                 downloadable_selected = False
01251                 if self.rbtnNPDownloadableDriverSearch.get_active ():
01252                     combobox = self.cmbNPDownloadableDriverFoundPrinters
01253                     iter = combobox.get_active_iter ()
01254                     if iter and combobox.get_model ().get_value (iter, 1):
01255                         downloadable_selected = True
01256 
01257                 self.btnNPForward.set_sensitive(bool(
01258                         (self.rbtnNPFoomatic.get_active() and
01259                          self.tvNPMakes.get_cursor()[0] != None) or
01260                         self.filechooserPPD.get_filename() or
01261                         downloadable_selected))
01262                 return
01263             else:
01264                 self.btnNPBack.show()
01265 
01266         # class/printer
01267 
01268         if nr == 1: # Device
01269             valid = False
01270             try:
01271                 uri = self.getDeviceURI ()
01272                 valid = validDeviceURI (uri)
01273             except:
01274                 pass
01275             self.btnNPForward.set_sensitive(valid)
01276             self.btnNPBack.hide ()
01277         else:
01278             self.btnNPBack.show()
01279 
01280         self.btnNPForward.show()
01281         self.btnNPApply.hide()
01282 
01283         if nr == 0: # Name
01284             self.btnNPBack.show()
01285             if self.dialog_mode == "printer" or \
01286                     self.dialog_mode == "printer_with_uri":
01287                 self.btnNPForward.hide()
01288                 self.btnNPApply.show()
01289                 self.btnNPApply.set_sensitive(
01290                     checkNPName(self.printers, self.entNPName.get_text()))
01291             if self.dialog_mode == "class":
01292                 # This is the first page for the New Class dialog, so
01293                 # hide the Back button.
01294                 self.btnNPBack.hide ()
01295             if self.dialog_mode == "printer_with_uri" and \
01296                     (self.remotecupsqueue or \
01297                          (self.exactdrivermatch and \
01298                               not self.installable_options)):
01299                 self.btnNPBack.hide ()
01300         if nr == 2: # Make/PPD file
01301             downloadable_selected = False
01302             if self.rbtnNPDownloadableDriverSearch.get_active ():
01303                 combobox = self.cmbNPDownloadableDriverFoundPrinters
01304                 iter = combobox.get_active_iter ()
01305                 if iter and combobox.get_model ().get_value (iter, 1):
01306                     downloadable_selected = True
01307 
01308             self.btnNPForward.set_sensitive(bool(
01309                 self.rbtnNPFoomatic.get_active() or
01310                 self.filechooserPPD.get_filename() or
01311                 downloadable_selected))
01312             # If we have an auto-detected printer for which there was no
01313             # driver found, we have already the URI and so this step is
01314             # not needed in the wizard. This makes manufacturer?PPD selection
01315             # the firts step
01316             if self.dialog_mode == "printer_with_uri":
01317                 self.btnNPBack.hide()
01318         if nr == 3: # Model/Driver
01319             model, iter = self.tvNPDrivers.get_selection().get_selected()
01320             self.btnNPForward.set_sensitive(bool(iter))
01321         if nr == 4: # Class Members
01322             self.btnNPForward.hide()
01323             self.btnNPApply.show()
01324             self.btnNPApply.set_sensitive(
01325                 bool(getCurrentClassMembers(self.tvNCMembers)))
01326         if nr == 6: # Installable options
01327             if self.dialog_mode == "printer_with_uri" and \
01328                     self.exactdrivermatch:
01329                 self.btnNPBack.hide ()
01330         if nr == 7: # Downloadable drivers
01331             if self.ntbkNPDownloadableDriverProperties.get_current_page() == 1:
01332                 accepted = self.rbtnNPDownloadLicenseYes.get_active ()
01333             else:
01334                 treeview = self.tvNPDownloadableDrivers
01335                 model, iter = treeview.get_selection ().get_selected ()
01336                 accepted = (iter != None)
01337 
01338             self.btnNPForward.set_sensitive(accepted)
01339 
01340     def on_entNPName_changed(self, widget):
01341         # restrict
01342         text = unicode (widget.get_text())
01343         new_text = text
01344         new_text = new_text.replace("/", "")
01345         new_text = new_text.replace("#", "")
01346         new_text = new_text.replace(" ", "")
01347         if text!=new_text:
01348             widget.set_text(new_text)
01349         if self.dialog_mode == "printer":
01350             self.btnNPApply.set_sensitive(
01351                 checkNPName(self.printers, new_text))
01352         else:
01353             self.btnNPForward.set_sensitive(
01354                 checkNPName(self.printers, new_text))
01355 
01356     def fetchDevices(self, network=False, current_uri=None):
01357         debugprint ("fetchDevices")
01358         self.inc_spinner_task ()
01359 
01360         # Search for Bluetooth printers together with the network printers
01361         # as the Bluetooth search takes rather long time
01362         network_schemes = ["dnssd", "snmp", "bluetooth"]
01363         error_handler = self.error_getting_devices
01364         if network == False:
01365             reply_handler = (lambda x, y:
01366                                  self.local_devices_reply (x, y,
01367                                                            current_uri))
01368             cupshelpers.getDevices (self.fetchDevices_conn,
01369                                     exclude_schemes=network_schemes,
01370                                     reply_handler=reply_handler,
01371                                     error_handler=error_handler)
01372         else:
01373             reply_handler = (lambda x, y:
01374                                  self.network_devices_reply (x, y,
01375                                                              current_uri))
01376             cupshelpers.getDevices (self.fetchDevices_conn,
01377                                     include_schemes=network_schemes,
01378                                     reply_handler=reply_handler,
01379                                     error_handler=error_handler)
01380 
01381     def error_getting_devices (self, conn, exc):
01382         # Just ignore the error.
01383         debugprint ("Error fetching devices: %s" % repr (exc))
01384         self.dec_spinner_task ()
01385         self.fetchDevices_conn._end_operation ()
01386         self.fetchDevices_conn.destroy ()
01387         self.fetchDevices_conn = None
01388 
01389     def local_devices_reply (self, conn, result, current_uri):
01390         self.dec_spinner_task ()
01391 
01392         # Now we've got the local devices, start a request for the
01393         # network devices.
01394         self.fetchDevices (network=True, current_uri=current_uri)
01395 
01396         # Add the local devices to the list.
01397         self.add_devices (result, current_uri)
01398 
01399     def network_devices_reply (self, conn, result, current_uri):
01400         self.dec_spinner_task ()
01401         self.fetchDevices_conn._end_operation ()
01402         self.fetchDevices_conn.destroy ()
01403         self.fetchDevices_conn = None
01404 
01405         # Add the network devices to the list.
01406         no_more = True
01407         need_resolving = {}
01408         for uri, device in result.iteritems ():
01409             if uri.startswith ("dnssd://"):
01410                 need_resolving[uri] = device
01411                 no_more = False
01412 
01413         for uri in need_resolving.keys ():
01414             del result[uri]
01415 
01416         self.add_devices (result, current_uri, no_more=no_more)
01417 
01418         if len (need_resolving) > 0:
01419             resolver = dnssdresolve.DNSSDHostNamesResolver (need_resolving)
01420             resolver.resolve (reply_handler=lambda devices:
01421                                   self.dnssd_resolve_reply (current_uri,
01422                                                             devices))
01423 
01424     def dnssd_resolve_reply (self, current_uri, devices):
01425         self.add_devices (devices, current_uri, no_more=True)
01426 
01427     def get_hpfax_device_id(self, faxuri):
01428         new_environ = os.environ.copy()
01429         new_environ['LC_ALL'] = "C"
01430         new_environ['DISPLAY'] = ""
01431         args = ["hp-info", "-x", "-i", "-d" + faxuri]
01432         debugprint (faxuri + ": " + repr(args))
01433         try:
01434             p = subprocess.Popen (args, env=new_environ, close_fds=True,
01435                                   stdin=file("/dev/null"),
01436                                   stdout=subprocess.PIPE,
01437                                   stderr=subprocess.PIPE)
01438             (stdout, stderr) = p.communicate ()
01439         except:
01440             # Problem executing command.
01441             return None
01442 
01443         faxtype = -1
01444         for line in stdout.split ("\n"):
01445             if line.find ("fax-type") == -1:
01446                 continue
01447             res = re.search ("(\d+)", line)
01448             if res:
01449                 resg = res.groups()
01450                 faxtype = resg[0]
01451             if faxtype >= 0:
01452                 break
01453         if faxtype <= 0:
01454             return None
01455         elif faxtype == 4:
01456             return 'MFG:HP;MDL:Fax 2;DES:HP Fax 2;'
01457         else:
01458             return 'MFG:HP;MDL:Fax;DES:HP Fax;'
01459 
01460     def get_hplip_scan_type_for_uri(self, uri):
01461         args = ["hp-query", "-k", "scan-type", "-d", uri]
01462         debugprint (uri + ": " + repr(args))
01463         try:
01464             p = subprocess.Popen (args, close_fds=True,
01465                                   stdin=file("/dev/null"),
01466                                   stdout=subprocess.PIPE,
01467                                   stderr=subprocess.PIPE)
01468             (stdout, stderr) = p.communicate ()
01469             if p.returncode != 0:
01470                 return None
01471         except:
01472             # Problem executing command.
01473             return None
01474 
01475         scan_type = stdout.strip ()
01476         fields = scan_type.split ("=", 1)
01477         if len (fields) < 2:
01478             return None
01479 
01480         value = fields[1]
01481         if value == '0':
01482             return None
01483 
01484         return value
01485 
01486     def get_hplip_uri_for_network_printer(self, host, mode):
01487         if mode == "print": mod = "-c"
01488         elif mode == "fax": mod = "-f"
01489         else: mod = "-c"
01490         args = ["hp-makeuri", mod, host]
01491         debugprint (host + ": " + repr(args))
01492         uri = None
01493         try:
01494             p = subprocess.Popen (args, close_fds=True,
01495                                   stdin=file("/dev/null"),
01496                                   stdout=subprocess.PIPE,
01497                                   stderr=subprocess.PIPE)
01498             (stdout, stderr) = p.communicate ()
01499             if p.returncode != 0:
01500                 return None
01501         except:
01502             # Problem executing command.
01503             return None
01504 
01505         uri = stdout.strip ()
01506         return uri
01507 
01508     def getNetworkPrinterMakeModel(self, host=None, device=None):
01509         """
01510         Try to determine the make and model for the currently selected
01511         network printer, and store this in the data structure for the
01512         printer.
01513         Returns (hostname or None, uri or None).
01514         """
01515         uri = None
01516         if device == None:
01517             device = self.device
01518         # Determine host name/IP
01519         if host == None:
01520             s = device.uri.find ("://")
01521             if s != -1:
01522                 s += 3
01523                 e = device.uri[s:].find (":")
01524                 if e == -1: e = device.uri[s:].find ("/")
01525                 if e == -1: e = device.uri[s:].find ("?")
01526                 if e == -1: e = len (device.uri)
01527                 host = device.uri[s:s+e]
01528         # Try to get make and model via SNMP
01529         if host:
01530             args = ["/usr/lib/cups/backend/snmp", host]
01531             debugprint (host + ": " + repr(args))
01532             stdout = None
01533             try:
01534                 p = subprocess.Popen (args, close_fds=True,
01535                                       stdin=file("/dev/null"),
01536                                       stdout=subprocess.PIPE,
01537                                       stderr=subprocess.PIPE)
01538                 (stdout, stderr) = p.communicate ()
01539                 if p.returncode != 0:
01540                     stdout = None
01541             except:
01542                 # Problem executing command.
01543                 pass
01544 
01545             if stdout != None:
01546                 line = stdout.strip ()
01547                 words = probe_printer.wordsep (line)
01548                 n = len (words)
01549                 if n < 4:
01550                     words.extend (['','','',''])
01551                     words = words[:4]
01552                     n = 4
01553                 elif n > 6:
01554                     words = words[:6]
01555                     n = 6
01556 
01557                 if n == 6:
01558                     (device_class, uri, make_and_model,
01559                      info, device_id, device_location) = words
01560                 elif n == 5:
01561                     (device_class, uri, make_and_model,
01562                      info, device_id) = words
01563                 elif n == 4:
01564                     (device_class, uri, make_and_model, info) = words
01565 
01566                 if n == 4:
01567                     # No Device ID given so we'll have to make one
01568                     # up.
01569                     debugprint ("No Device ID from snmp backend")
01570                     (mk, md) = cupshelpers.ppds.\
01571                         ppdMakeModelSplit (make_and_model)
01572                     device.id = "MFG:%s;MDL:%s;DES:%s %s;" % (mk, md,
01573                                                               mk, md)
01574                 else:
01575                     debugprint ("Got Device ID: %s" % device_id)
01576                     device.id = device_id
01577 
01578                 device.id_dict = cupshelpers.parseDeviceID (device.id)
01579                 device.make_and_model = make_and_model
01580                 device.info = info
01581                 if n == 6:
01582                     device.location = device_location
01583 
01584         return (host, uri)
01585 
01586     def fillDeviceTab(self, current_uri=None):
01587         self.device_selected = -1
01588         model = gtk.TreeStore (gobject.TYPE_STRING,   # device-info
01589                                gobject.TYPE_PYOBJECT, # PhysicalDevice obj
01590                                gobject.TYPE_BOOLEAN)  # Separator?
01591         other = cupshelpers.Device('', **{'device-info' :_("Enter URI")})
01592         physother = PhysicalDevice (other)
01593         self.devices = [physother]
01594         uri_iter = model.append (None, row=[physother.get_info (),
01595                                             physother, False])
01596         network_iter = model.append (None, row=[_("Network Printer"),
01597                                                 None,
01598                                                 False])
01599         network_dict = { 'device-class': 'network',
01600                          'device-info': _("Find Network Printer") }
01601         network = cupshelpers.Device ('network', **network_dict)
01602         find_nw_iter = model.append (network_iter,
01603                                      row=[network_dict['device-info'],
01604                                           PhysicalDevice (network), False])
01605         model.insert_after (network_iter, find_nw_iter, row=['', None, True])
01606         self.devices_uri_iter = uri_iter
01607         self.devices_find_nw_iter = find_nw_iter
01608         self.devices_network_iter = network_iter
01609         self.devices_network_fetched = False
01610         self.tvNPDevices.set_model (model)
01611         self.entNPTDevice.set_text ('')
01612         self.expNPDeviceURIs.hide ()
01613         column = self.tvNPDevices.get_column (0)
01614         self.tvNPDevices.set_cursor ((0,), column)
01615 
01616         allowed = True
01617         self.current_uri = current_uri
01618         try:
01619             if (self._host == 'localhost' or
01620                 self._host[0] == '/'):
01621                 self.firewall = firewall.Firewall ()
01622                 debugprint ("Examining firewall")
01623                 self.firewall.read (reply_handler=self.on_firewall_read,
01624                                     error_handler=lambda x:
01625                                         self.start_fetching_devices())
01626                 allowed = False
01627             else:
01628                 # This is a remote server.  Nothing we can do about
01629                 # the firewall there.
01630                 allowed = True
01631         except (dbus.DBusException, Exception):
01632             nonfatalException ()
01633 
01634         if allowed:
01635             debugprint ("Fetching devices (no firewall service")
01636             self.start_fetching_devices ()
01637 
01638     def on_firewall_read (self, data):
01639         f = self.firewall
01640         allowed = True
01641         try:
01642             ipp_allowed = f.check_ipp_client_allowed ()
01643             mdns_allowed = f.check_mdns_allowed ()
01644             allowed = (ipp_allowed and mdns_allowed)
01645 
01646             secondary_text = TEXT_adjust_firewall + "\n\n"
01647             if not ipp_allowed:
01648                 secondary_text += ("- " +
01649                                    _("Allow all incoming IPP Browse packets") +
01650                                    "\n")
01651                 f.add_rule (f.ALLOW_IPP_CLIENT)
01652             if not mdns_allowed:
01653                 secondary_text += ("- " +
01654                                    _("Allow all incoming mDNS traffic") + "\n")
01655                 f.add_rule (f.ALLOW_MDNS)
01656 
01657             if not allowed:
01658                 debugprint ("Asking for permission to adjust firewall:\n%s" %
01659                             secondary_text)
01660                 dialog = gtk.MessageDialog (self.NewPrinterWindow,
01661                                             gtk.DIALOG_MODAL |
01662                                             gtk.DIALOG_DESTROY_WITH_PARENT,
01663                                             gtk.MESSAGE_QUESTION,
01664                                             gtk.BUTTONS_NONE,
01665                                             _("Adjust Firewall"))
01666                 dialog.format_secondary_markup (secondary_text)
01667                 dialog.add_buttons (_("Do It Later"), gtk.RESPONSE_NO,
01668                                     _("Adjust Firewall"), gtk.RESPONSE_YES)
01669                 dialog.connect ('response', self.adjust_firewall_response)
01670                 dialog.show ()
01671         except (dbus.DBusException, Exception):
01672             nonfatalException ()
01673 
01674         if allowed:
01675             debugprint ("Firewall all OK; fetching devices")
01676             self.start_fetching_devices ()
01677 
01678     def adjust_firewall_response (self, dialog, response):
01679         dialog.destroy ()
01680         if response == gtk.RESPONSE_YES:
01681             self.firewall.add_rule (self.firewall.ALLOW_IPP_SERVER)
01682             self.firewall.write ()
01683 
01684         debugprint ("Fetching devices after firewall dialog response")
01685         self.start_fetching_devices ()
01686 
01687     def start_fetching_devices (self):
01688         self.fetchDevices_conn = asyncconn.Connection ()
01689         self.fetchDevices_conn._begin_operation (_("fetching device list"))
01690         self.fetchDevices (network=False, current_uri=self.current_uri)
01691         del self.current_uri
01692 
01693     def add_devices (self, devices, current_uri, no_more=False):
01694         if current_uri:
01695             if devices.has_key (current_uri):
01696                 current = devices.pop(current_uri)
01697             elif devices.has_key (current_uri.replace (":9100", "")):
01698                 current_uri = current_uri.replace (":9100", "")
01699                 current = devices.pop(current_uri)
01700             elif no_more:
01701                 current = cupshelpers.Device (current_uri)
01702                 current.info = "Current device"
01703             else:
01704                 current_uri = None
01705 
01706         devices = devices.values()
01707 
01708         for device in devices:
01709             if device.type == "socket":
01710                 # Remove default port to more easily find duplicate URIs
01711                 device.uri = device.uri.replace (":9100", "")
01712 
01713         # Map generic URIs to something canonical
01714         def replace_generic (device):
01715             if device.uri == "hp:/no_device_found":
01716                 device.uri = "hp"
01717             elif device.uri == "hpfax:/no_device_found":
01718                 device.uri = "hpfax"
01719             return device
01720 
01721         devices = map (replace_generic, devices)
01722 
01723         # Mark duplicate URIs for deletion
01724         for i in range (len (devices) - 1):
01725             for j in range (i + 1, len (devices)):
01726                 device1 = devices[i]
01727                 device2 = devices[j]
01728                 if device1.uri == "delete" or device2.uri == "delete":
01729                     continue
01730                 if device1.uri == device2.uri:
01731                     # Keep the one with the longer (better) device ID
01732                     if (not device1.id):
01733                         device1.uri = "delete"
01734                     elif (not device2.id):
01735                         device2.uri = "delete"
01736                     elif (len (device1.id) < len (device2.id)):
01737                         device1.uri = "delete"
01738                     else:
01739                         device2.uri = "delete"
01740         devices = filter(lambda x: x.uri not in ("hp", "hpfax",
01741                                                  "hal", "beh",
01742                                                  "scsi", "http", "delete"),
01743                          devices)
01744         newdevices = []
01745         for device in devices:
01746             physicaldevice = PhysicalDevice (device)
01747             try:
01748                 i = self.devices.index (physicaldevice)
01749                 self.devices[i].add_device (device)
01750             except ValueError:
01751                 self.devices.append (physicaldevice)
01752                 newdevices.append (physicaldevice)
01753 
01754         self.devices.sort()
01755         if current_uri:
01756             current_device = PhysicalDevice (current)
01757             try:
01758                 i = self.devices.index (current_device)
01759                 self.devices[i].add_device (current)
01760                 current_device = self.devices[i]
01761             except ValueError:
01762                 self.devices.append (current_device)
01763                 newdevices.append (current_device)
01764         else:
01765             current_device = None
01766 
01767         model = self.tvNPDevices.get_model ()
01768 
01769         network_iter = self.devices_network_iter
01770         find_nw_iter = self.devices_find_nw_iter
01771         for device in newdevices:
01772             devs = device.get_devices ()
01773             network = devs[0].device_class == 'network'
01774             info = device.get_info ()
01775             if device == current_device:
01776                 info += _(" (Current)")
01777             row=[info, device, False]
01778             if network:
01779                 if devs[0].uri != devs[0].type:
01780                     # An actual network printer device.  Put this at the top.
01781                     iter = model.insert_before (network_iter, find_nw_iter,
01782                                                 row=row)
01783 
01784                     # If this is the currently selected device we need
01785                     # to expand the "Network Printer" row so that it
01786                     # is visible.
01787                     if device == current_device:
01788                         network_path = model.get_path (network_iter)
01789                         self.tvNPDevices.expand_row (network_path, False)
01790                 else:
01791                     # Just a method of finding one.
01792                     iter = model.append (network_iter, row=row)
01793             else:
01794                 # Insert this local device in order.
01795                 network_path = model.get_path (network_iter)
01796                 iter = model.get_iter_first ()
01797                 while model.get_path (iter) != network_path:
01798                     physdev = model.get_value (iter, 1)
01799                     if physdev > device:
01800                         break
01801 
01802                     iter = model.iter_next (iter)
01803 
01804                 iter = model.insert_before (None, iter, row=row)
01805 
01806             if device == current_device:
01807                 device_select_path = model.get_path (iter)
01808                 self.tvNPDevices.scroll_to_cell (device_select_path,
01809                                                  row_align=0.5)
01810                 column = self.tvNPDevices.get_column (0)
01811                 self.tvNPDevices.set_cursor (device_select_path, column)
01812 
01813         connection_select_path = 0
01814         if current_uri:
01815             model = self.tvNPDeviceURIs.get_model ()
01816             iter = model.get_iter_first ()
01817             i = 0
01818             while iter:
01819                 dev = model.get_value (iter, 1)
01820                 if current_uri == dev.uri:
01821                     connection_select_path = i
01822                     break
01823 
01824                 iter = model.iter_next (iter)
01825                 i += 1
01826         elif not self.device_selected:
01827             # Select the device.
01828             column = self.tvNPDevices.get_column (0)
01829             self.tvNPDevices.set_cursor ((0,), column)
01830 
01831             # Select the connection.
01832             column = self.tvNPDeviceURIs.get_column (0)
01833             self.tvNPDeviceURIs.set_cursor (connection_select_path, column)
01834 
01835     ## SMB browsing
01836 
01837     def browse_smb_hosts(self):
01838         """Initialise the SMB tree store."""
01839         store = self.smb_store
01840         store.clear ()
01841         busy(self.SMBBrowseDialog)
01842         class X:
01843             pass
01844         dummy = X()
01845         dummy.smbc_type = pysmb.smbc.PRINTER_SHARE
01846         dummy.name = _('Scanning...')
01847         dummy.comment = ''
01848         store.append(None, [dummy])
01849         while gtk.events_pending ():
01850             gtk.main_iteration ()
01851 
01852         debug = 0
01853         if get_debugging ():
01854             debug = 10
01855         smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
01856         ctx = pysmb.smbc.Context (debug=debug,
01857                                   auth_fn=smbc_auth.callback)
01858         entries = None
01859         try:
01860             while smbc_auth.perform_authentication () > 0:
01861                 try:
01862                     entries = ctx.opendir ("smb://").getdents ()
01863                 except Exception, e:
01864                     smbc_auth.failed (e)
01865         except RuntimeError, (e, s):
01866             if e != errno.ENOENT:
01867                 debugprint ("Runtime error: %s" % repr ((e, s)))
01868         except:
01869             nonfatalException ()
01870 
01871         store.clear ()
01872         if entries:
01873             for entry in entries:
01874                 if entry.smbc_type in [pysmb.smbc.WORKGROUP,
01875                                        pysmb.smbc.SERVER]:
01876                     iter = store.append (None, [entry])
01877                     i = store.append (iter)
01878 
01879         specified_uri = SMBURI (uri=self.entSMBURI.get_text ())
01880         (group, host, share, user, password) = specified_uri.separate ()
01881         if len (host) > 0:
01882             # The user has specified a server before clicking Browse.
01883             # Append the server as a top-level entry.
01884             class FakeEntry:
01885                 pass
01886             toplevel = FakeEntry ()
01887             toplevel.smbc_type = pysmb.smbc.SERVER
01888             toplevel.name = host
01889             toplevel.comment = ''
01890             iter = store.append (None, [toplevel])
01891             i = store.append (iter)
01892 
01893             # Now expand it.
01894             path = store.get_path (iter)
01895             self.tvSMBBrowser.expand_row (path, 0)
01896 
01897         ready(self.SMBBrowseDialog)
01898 
01899         if store.get_iter_first () == None:
01900             self.SMBBrowseDialog.hide ()
01901             show_info_dialog (_("No Print Shares"),
01902                               _("There were no print shares found.  "
01903                                 "Please check that the Samba service is "
01904                                 "marked as trusted in your firewall "
01905                                 "configuration."),
01906                               parent=self.NewPrinterWindow)
01907 
01908     def smb_select_function (self, path):
01909         """Don't allow this path to be selected unless it is a leaf."""
01910         iter = self.smb_store.get_iter (path)
01911         return not self.smb_store.iter_has_child (iter)
01912 
01913     def smbbrowser_cell_share (self, column, cell, model, iter):
01914         entry = model.get_value (iter, 0)
01915         share = ''
01916         if entry != None:
01917             share = entry.name
01918         cell.set_property ('text', share)
01919 
01920     def smbbrowser_cell_comment (self, column, cell, model, iter):
01921         entry = model.get_value (iter, 0)
01922         comment = ''
01923         if entry != None:
01924             comment = entry.comment
01925         cell.set_property ('text', comment)
01926 
01927     def on_tvSMBBrowser_row_activated (self, view, path, column):
01928         """Handle double-clicks in the SMB tree view."""
01929         store = self.smb_store
01930         iter = store.get_iter (path)
01931         entry = store.get_value (iter, 0)
01932         if entry and entry.smbc_type == pysmb.smbc.PRINTER_SHARE:
01933             # This is a share, not a host.
01934             self.btnSMBBrowseOk.clicked ()
01935             return
01936 
01937         if view.row_expanded (path):
01938             view.collapse_row (path)
01939         else:
01940             self.on_tvSMBBrowser_row_expanded (view, iter, path)
01941 
01942     def on_tvSMBBrowser_row_expanded (self, view, iter, path):
01943         """Handler for expanding a row in the SMB tree view."""
01944         model = view.get_model ()
01945         entry = model.get_value (iter, 0)
01946         if entry == None:
01947             return
01948 
01949         if entry.smbc_type == pysmb.smbc.WORKGROUP:
01950             # Workgroup
01951             # Be careful though: if there is a server with the
01952             # same name as the workgroup we will get a list of its
01953             # shares, not the workgroup's servers.
01954             try:
01955                 if self.expanding_row:
01956                     return
01957             except:
01958                 self.expanding_row = 1
01959 
01960             busy (self.SMBBrowseDialog)
01961             uri = "smb://%s/" % entry.name
01962             debug = 0
01963             if get_debugging ():
01964                 debug = 10
01965             smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
01966             ctx = pysmb.smbc.Context (debug=debug,
01967                                       auth_fn=smbc_auth.callback)
01968             entries = []
01969             try:
01970                 while smbc_auth.perform_authentication () > 0:
01971                     try:
01972                         entries = ctx.opendir (uri).getdents ()
01973                     except Exception, e:
01974                         smbc_auth.failed (e)
01975             except RuntimeError, (e, s):
01976                 if e != errno.ENOENT:
01977                     debugprint ("Runtime error: %s" % repr ((e, s)))
01978             except:
01979                 nonfatalException()
01980 
01981             while model.iter_has_child (iter):
01982                 i = model.iter_nth_child (iter, 0)
01983                 model.remove (i)
01984 
01985             for entry in entries:
01986                 if entry.smbc_type in [pysmb.smbc.SERVER,
01987                                        pysmb.smbc.PRINTER_SHARE]:
01988                     i = model.append (iter, [entry])
01989                 if entry.smbc_type == pysmb.smbc.SERVER:
01990                     n = model.append (i)
01991 
01992             view.expand_row (path, 0)
01993             del self.expanding_row
01994             ready (self.SMBBrowseDialog)
01995 
01996         elif entry.smbc_type == pysmb.smbc.SERVER:
01997             # Server
01998             try:
01999                 if self.expanding_row:
02000                     return
02001             except:
02002                 self.expanding_row = 1
02003 
02004             busy (self.SMBBrowseDialog)
02005             uri = "smb://%s/" % entry.name
02006             debug = 0
02007             if get_debugging ():
02008                 debug = 10
02009             smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog)
02010             ctx = pysmb.smbc.Context (debug=debug,
02011                                       auth_fn=smbc_auth.callback)
02012             shares = []
02013             try:
02014                 while smbc_auth.perform_authentication () > 0:
02015                     try:
02016                         shares = ctx.opendir (uri).getdents ()
02017                     except Exception, e:
02018                         smbc_auth.failed (e)
02019             except RuntimeError, (e, s):
02020                 if e != errno.EACCES and e != errno.EPERM:
02021                     debugprint ("Runtime error: %s" % repr ((e, s)))
02022             except:
02023                 nonfatalException()
02024 
02025             while model.iter_has_child (iter):
02026                 i = model.iter_nth_child (iter, 0)
02027                 model.remove (i)
02028 
02029             for share in shares:
02030                 if share.smbc_type == pysmb.smbc.PRINTER_SHARE:
02031                     i = model.append (iter, [share])
02032                     debugprint (repr (share))
02033 
02034             view.expand_row (path, 0)
02035             del self.expanding_row
02036             ready (self.SMBBrowseDialog)
02037 
02038     def on_entSMBURI_changed (self, ent):
02039         allowed_chars = string.letters+string.digits+'_-./:@%'
02040         self.entry_changed(ent, allowed_chars)
02041         uri = ent.get_text ()
02042         (group, host, share, user, password) = SMBURI (uri=uri).separate ()
02043         if user:
02044             self.entSMBUsername.set_text (user)
02045         if password:
02046             self.entSMBPassword.set_text (password)
02047         if user or password:
02048             uri = SMBURI (group=group, host=host, share=share).get_uri ()
02049             ent.set_text(uri)
02050             self.rbtnSMBAuthSet.set_active(True)
02051         elif self.entSMBUsername.get_text () == '':
02052             self.rbtnSMBAuthPrompt.set_active(True)
02053 
02054         self.btnSMBVerify.set_sensitive(bool(uri))
02055         self.setNPButtons ()
02056 
02057     def on_tvSMBBrowser_cursor_changed(self, widget):
02058         store, iter = self.tvSMBBrowser.get_selection().get_selected()
02059         is_share = False
02060         if iter:
02061             entry = store.get_value (iter, 0)
02062             if entry:
02063                 is_share = entry.smbc_type == pysmb.smbc.PRINTER_SHARE
02064 
02065         self.btnSMBBrowseOk.set_sensitive(iter != None and is_share)
02066 
02067     def on_btnSMBBrowse_clicked(self, button):
02068         self.btnSMBBrowseOk.set_sensitive(False)
02069 
02070         try:
02071             # Note: we do the browsing from *this* machine, regardless
02072             # of which CUPS server we are connected to.
02073             f = firewall.Firewall ()
02074             allowed = f.check_samba_client_allowed ()
02075             secondary_text = TEXT_adjust_firewall + "\n\n"
02076             if not allowed:
02077                 dialog = gtk.MessageDialog (self.NewPrinterWindow,
02078                                             gtk.DIALOG_MODAL |
02079                                             gtk.DIALOG_DESTROY_WITH_PARENT,
02080                                             gtk.MESSAGE_QUESTION,
02081                                             gtk.BUTTONS_NONE,
02082                                             _("Adjust Firewall"))
02083                 secondary_text += ("- " +
02084                                    _("Allow all incoming SMB/CIFS "
02085                                      "browse packets"))
02086                 dialog.format_secondary_markup (secondary_text)
02087                 dialog.add_buttons (_("Do It Later"), gtk.RESPONSE_NO,
02088                                     _("Adjust Firewall"), gtk.RESPONSE_YES)
02089                 response = dialog.run ()
02090                 dialog.destroy ()
02091 
02092                 if response == gtk.RESPONSE_YES:
02093                     f.add_rule (f.ALLOW_SAMBA_CLIENT)
02094                     f.write ()
02095         except (dbus.DBusException, Exception):
02096             nonfatalException ()
02097 
02098         self.SMBBrowseDialog.show()
02099         self.browse_smb_hosts()
02100 
02101     def on_btnSMBBrowseOk_clicked(self, button):
02102         store, iter = self.tvSMBBrowser.get_selection().get_selected()
02103         is_share = False
02104         if iter:
02105             entry = store.get_value (iter, 0)
02106             if entry:
02107                 is_share = entry.smbc_type == pysmb.smbc.PRINTER_SHARE
02108 
02109         if not iter or not is_share:
02110             self.SMBBrowseDialog.hide()
02111             return
02112 
02113         parent_iter = store.iter_parent (iter)
02114         domain_iter = store.iter_parent (parent_iter)
02115         share = store.get_value (iter, 0)
02116         host = store.get_value (parent_iter, 0)
02117         if domain_iter:
02118             group = store.get_value (domain_iter, 0).name
02119         else:
02120             group = ''
02121         uri = SMBURI (group=group,
02122                       host=host.name,
02123                       share=share.name).get_uri ()
02124 
02125         self.entSMBUsername.set_text ('')
02126         self.entSMBPassword.set_text ('')
02127         self.entSMBURI.set_text (uri)
02128 
02129         self.SMBBrowseDialog.hide()
02130 
02131     def on_btnSMBBrowseCancel_clicked(self, widget, *args):
02132         self.SMBBrowseDialog.hide()
02133 
02134     def on_btnSMBBrowseRefresh_clicked(self, button):
02135         self.browse_smb_hosts()
02136 
02137     def on_rbtnSMBAuthSet_toggled(self, widget):
02138         self.tblSMBAuth.set_sensitive(widget.get_active())
02139 
02140     def on_btnSMBVerify_clicked(self, button):
02141         uri = self.entSMBURI.get_text ()
02142         (group, host, share, u, p) = SMBURI (uri=uri).separate ()
02143         user = ''
02144         passwd = ''
02145         reason = None
02146         auth_set = self.rbtnSMBAuthSet.get_active()
02147         if auth_set:
02148             user = self.entSMBUsername.get_text ()
02149             passwd = self.entSMBPassword.get_text ()
02150 
02151         accessible = False
02152         canceled = False
02153         busy (self.NewPrinterWindow)
02154         try:
02155             debug = 0
02156             if get_debugging ():
02157                 debug = 10
02158 
02159             if auth_set:
02160                 # No prompting.
02161                 def do_auth (svr, shr, wg, un, pw):
02162                     return (group, user, passwd)
02163                 ctx = pysmb.smbc.Context (debug=debug, auth_fn=do_auth)
02164                 try:
02165                     ctx.optionUseKerberos = True
02166                 except AttributeError:
02167                     # requires pysmbc >= 1.0.12
02168                     pass
02169 
02170                 f = ctx.open ("smb://%s/%s" % (host, share),
02171                               os.O_RDWR, 0777)
02172                 accessible = True
02173             else:
02174                 # May need to prompt.
02175                 smbc_auth = pysmb.AuthContext (self.NewPrinterWindow,
02176                                                workgroup=group,
02177                                                user=user,
02178                                                passwd=passwd)
02179                 ctx = pysmb.smbc.Context (debug=debug,
02180                                           auth_fn=smbc_auth.callback)
02181                 while smbc_auth.perform_authentication () > 0:
02182                     try:
02183                         f = ctx.open ("smb://%s/%s" % (host, share),
02184                                       os.O_RDWR, 0777)
02185                         accessible = True
02186                     except Exception, e:
02187                         smbc_auth.failed (e)
02188 
02189                 if not accessible:
02190                     canceled = True
02191         except RuntimeError, (e, s):
02192             debugprint ("Error accessing share: %s" % repr ((e, s)))
02193             reason = s
02194         except:
02195             nonfatalException()
02196         ready (self.NewPrinterWindow)
02197 
02198         if accessible:
02199             show_info_dialog (_("Print Share Verified"),
02200                               _("This print share is accessible."),
02201                               parent=self.NewPrinterWindow)
02202             return
02203 
02204         if not canceled:
02205             text = _("This print share is not accessible.")
02206             if reason:
02207                 text = reason
02208             show_error_dialog (_("Print Share Inaccessible"), text,
02209                                parent=self.NewPrinterWindow)
02210 
02211     def entry_changed(self, entry, allowed_chars):
02212         "Remove all chars from entry's text that are not in allowed_chars."
02213         try:
02214             allowed_chars = unicode (allowed_chars, locale.getpreferredencoding())
02215         except UnicodeDecodeError:
02216             allowed_chars = unicode (allowed_chars)
02217         origtext = unicode (entry.get_text())
02218         new_text = origtext
02219         for char in origtext:
02220             if char not in allowed_chars:
02221                 new_text = new_text.replace(char, "")
02222                 debugprint ("removed disallowed character %s" % char)
02223         if origtext!=new_text:
02224             entry.set_text(new_text)
02225 
02226     def on_entNPTDevice_changed(self, ent):
02227         allowed_chars = string.letters+string.digits+'_-./:%()@?=&'
02228         self.entry_changed(ent, allowed_chars)
02229         self.setNPButtons()
02230 
02231     def on_entNPTJetDirectHostname_changed(self, ent):
02232         allowed_chars = string.letters+string.digits+'_-.'
02233         self.entry_changed(ent, allowed_chars)
02234         self.setNPButtons()
02235 
02236     def on_entNPTJetDirectPort_changed(self, ent):
02237         self.entry_changed(ent, string.digits)
02238         self.setNPButtons()
02239 
02240     def on_expNPDeviceURIs_expanded (self, widget, UNUSED):
02241         # When the expanded is not expanded we want its packing to be
02242         # 'expand = false' so that it aligns at the bottom (it packs
02243         # to the end of its vbox).  But when it is expanded we'd like
02244         # it to expand with the window.
02245         #
02246         # Adjust its 'expand' packing state depending on whether the
02247         # widget is expanded.
02248 
02249         parent = widget.get_parent ()
02250         (expand, fill,
02251          padding, pack_type) = parent.query_child_packing (widget)
02252         expand = widget.get_expanded ()
02253         parent.set_child_packing (widget, expand, fill,
02254                                   padding, pack_type)
02255 
02256     def device_row_separator_fn (self, model, iter):
02257         return model.get_value (iter, 2)
02258 
02259     def device_row_activated (self, view, path, column):
02260         if view.row_expanded (path):
02261             view.collapse_row (path)
02262         else:
02263             view.expand_row (path, False)
02264 
02265     def device_select_function (self, path):
02266         """
02267         Allow this path to be selected as long as there
02268         is a device associated with it.  Otherwise, expand or collapse it.
02269         """
02270         model = self.tvNPDevices.get_model ()
02271         iter = model.get_iter (path)
02272         if model.get_value (iter, 1) != None:
02273             return True
02274 
02275         self.device_row_activated (self.tvNPDevices, path, None)
02276         return False
02277 
02278     def on_tvNPDevices_cursor_changed(self, widget):
02279         self.device_selected += 1
02280         path, column = widget.get_cursor ()
02281         if path == None:
02282             return
02283 
02284         model = widget.get_model ()
02285         iter = model.get_iter (path)
02286         physicaldevice = model.get_value (iter, 1)
02287         if physicaldevice == None:
02288             return
02289         for device in physicaldevice.get_devices ():
02290             if device.type == "parallel":
02291                 device.menuentry = _("Parallel Port")
02292             elif device.type == "serial":
02293                 device.menuentry = _("Serial Port")
02294             elif device.type == "usb":
02295                 device.menuentry = _("USB")
02296             elif device.type == "bluetooth":
02297                 device.menuentry = _("Bluetooth")
02298             elif device.type == "hp":
02299                 device.menuentry = _("HP Linux Imaging and Printing (HPLIP)")
02300             elif device.type == "hpfax":
02301                 device.menuentry = _("Fax") + " - " + \
02302                     _("HP Linux Imaging and Printing (HPLIP)")
02303             elif device.type == "hal":
02304                 device.menuentry = _("Hardware Abstraction Layer (HAL)")
02305             elif device.type == "socket":
02306                 device.menuentry = _("AppSocket/HP JetDirect")
02307             elif device.type == "lpd":
02308                 (scheme, rest) = urllib.splittype (device.uri)
02309                 (hostport, rest) = urllib.splithost (rest)
02310                 (queue, rest) = urllib.splitquery (rest)
02311                 if queue != '':
02312                     if queue[0] == '/':
02313                         queue = queue[1:]
02314 
02315                     device.menuentry = _("LPD/LPR queue '%s'") % queue
02316                 else:
02317                     device.menuentry = _("LPD/LPR queue")
02318 
02319             elif device.type == "smb":
02320                 device.menuentry = _("Windows Printer via SAMBA")
02321             elif device.type == "ipp":
02322                 (scheme, rest) = urllib.splittype (device.uri)
02323                 (hostport, rest) = urllib.splithost (rest)
02324                 (queue, rest) = urllib.splitquery (rest)
02325                 if queue != '':
02326                     if queue[0] == '/':
02327                         queue = queue[1:]
02328                     if queue.startswith("printers/"):
02329                         queue = queue[9:]
02330                 if queue != '':
02331                     device.menuentry = _("IPP") + " (%s)" % queue
02332                 else:
02333                     device.menuentry = _("IPP")
02334             elif device.type == "http" or device.type == "https":
02335                 device.menuentry = _("HTTP")
02336             elif device.type == "dnssd" or device.type == "mdns":
02337                 (scheme, rest) = urllib.splittype (device.uri)
02338                 (name, rest) = urllib.splithost (rest)
02339                 (cupsqueue, rest) = urllib.splitquery (rest)
02340                 if cupsqueue[0] == '/':
02341                     cupsqueue = cupsqueue[1:]
02342                 if cupsqueue == 'cups':
02343                     device.menuentry = _("Remote CUPS printer via DNS-SD")
02344                     if device.info != '':
02345                          device.menuentry += " (%s)" % device.info
02346                 else:
02347                     protocol = None
02348                     if name.find("._ipp") != -1:
02349                         protocol = "IPP"
02350                     elif name.find("._printer") != -1:
02351                         protocol = "LPD"
02352                     elif name.find("._pdl-datastream") != -1:
02353                         protocol = "AppSocket/JetDirect"
02354                     if protocol != None:
02355                         device.menuentry = \
02356                             _("%s network printer via DNS-SD") % protocol
02357                     else:
02358                         device.menuentry = \
02359                             _("Network printer via DNS-SD")
02360             else:
02361                 device.menuentry = device.uri
02362 
02363         model = gtk.ListStore (str,                    # URI description
02364                                gobject.TYPE_PYOBJECT)  # cupshelpers.Device
02365         self.tvNPDeviceURIs.set_model (model)
02366 
02367         # If this is a network device, check whether HPLIP can drive it.
02368         if physicaldevice.get_data ('checked-hplip') != True:
02369             hp_drivable = False
02370             hp_scannable = False
02371             is_network = False
02372             remotecups = False
02373             host = None
02374             device_dict = { 'device-class': 'network' }
02375             if physicaldevice._network_host:
02376                 host = physicaldevice._network_host
02377             for device in physicaldevice.get_devices ():
02378                 if device.type == "hp":
02379                     # We already know that HPLIP can drive this device.
02380                     hp_drivable = True
02381 
02382                     # But can we scan using it?
02383                     if self.get_hplip_scan_type_for_uri (device.uri):
02384                         hp_scannable = True
02385 
02386                     break
02387                 elif device.type in ["socket", "lpd", "ipp", "dnssd", "mdns"]:
02388                     # This is a network printer.
02389                     if host == None and device.type in ["socket", "lpd", "ipp"]:
02390                         (scheme, rest) = urllib.splittype (device.uri)
02391                         (hostport, rest) = urllib.splithost (rest)
02392                         if hostport != None:
02393                             (host, port) = urllib.splitport (hostport)
02394                     if host:
02395                         is_network = True
02396                         remotecups = ((device.uri.startswith('dnssd:') or \
02397                                        device.uri.startswith('mdns:')) and \
02398                                       device.uri.endswith('/cups'))
02399                         if (not device.make_and_model or \
02400                             device.make_and_model == "Unknown") and not \
02401                            remotecups:
02402                             self.getNetworkPrinterMakeModel(host=host,
02403                                                             device=device)
02404                         device_dict['device-info'] = device.info
02405                         device_dict['device-make-and-model'] = (device.
02406                                                                 make_and_model)
02407                         device_dict['device-id'] = device.id
02408                         device_dict['device-location'] = device.location
02409 
02410             if not hp_drivable and is_network and not remotecups:
02411                 if (hasattr (physicaldevice, "dnssd_hostname") and \
02412                     physicaldevice.dnssd_hostname):
02413                     hpliphost = physicaldevice.dnssd_hostname
02414                 else:
02415                     hpliphost = host
02416                 hplipuri = self.get_hplip_uri_for_network_printer (hpliphost,
02417                                                                    "print")
02418                 if hplipuri:
02419                     dev = cupshelpers.Device (hplipuri, **device_dict)
02420                     dev.menuentry = "HP Linux Imaging and Printing (HPLIP)"
02421                     physicaldevice.add_device (dev)
02422 
02423                     # Now check to see if we can also send faxes using
02424                     # this device.
02425                     faxuri = self.get_hplip_uri_for_network_printer (hpliphost,
02426                                                                      "fax")
02427                     if faxuri:
02428                         faxdevid = self.get_hpfax_device_id (faxuri)
02429                         device_dict['device-id'] = faxdevid
02430                         device_dict['device-info'] = _("Fax")
02431                         faxdev = cupshelpers.Device (faxuri, **device_dict)
02432                         faxdev.menuentry = _("Fax") + " - " + \
02433                             "HP Linux Imaging and Printing (HPLIP)"
02434                         physicaldevice.add_device (faxdev)
02435 
02436             physicaldevice.set_data ('hp-scannable', True)
02437             physicaldevice.set_data ('checked-hplip', True)
02438 
02439         device.hp_scannable = physicaldevice.get_data ('hp-scannable')
02440 
02441         # Fill the list of connections for this device.
02442         n = 0
02443         for device in physicaldevice.get_devices ():
02444             model.append ((device.menuentry, device))
02445             n += 1
02446         column = self.tvNPDeviceURIs.get_column (0)
02447         self.tvNPDeviceURIs.set_cursor (0, column)
02448         if n > 1:
02449             self.expNPDeviceURIs.show_all ()
02450         else:
02451             self.expNPDeviceURIs.hide ()
02452 
02453     def on_tvNPDeviceURIs_cursor_changed(self, widget):
02454         path, column = widget.get_cursor ()
02455         if path == None:
02456             return
02457 
02458         model = widget.get_model ()
02459         iter = model.get_iter (path)
02460         device = model.get_value(iter, 1)
02461         self.device = device
02462         self.lblNPDeviceDescription.set_text ('')
02463         page = self.new_printer_device_tabs.get(device.type, 1)
02464         self.ntbkNPType.set_current_page(page)
02465 
02466         location = ''
02467         type = device.type
02468         url = device.uri.split(":", 1)[-1]
02469         if page == 0:
02470             # This is the "no options" page, with just a label to describe
02471             # the selected device.
02472             if device.type == "parallel":
02473                 text = _("A printer connected to the parallel port.")
02474             elif device.type == "usb":
02475                 text = _("A printer connected to a USB port.")
02476             elif device.type == "bluetooth":
02477                 text = _("A printer connected via Bluetooth.")
02478             elif device.type == "hp":
02479                 text = _("HPLIP software driving a printer, "
02480                          "or the printer function of a multi-function device.")
02481             elif device.type == "hpfax":
02482                 text = _("HPLIP software driving a fax machine, "
02483                          "or the fax function of a multi-function device.")
02484             elif device.type == "hal":
02485                 text = _("Local printer detected by the "
02486                          "Hardware Abstraction Layer (HAL).")
02487             elif device.type == "dnssd" or device.type == "mdns":
02488                 (scheme, rest) = urllib.splittype (device.uri)
02489                 (name, rest) = urllib.splithost (rest)
02490                 (cupsqueue, rest) = urllib.splitquery (rest)
02491                 if cupsqueue[0] == '/':
02492                     cupsqueue = cupsqueue[1:]
02493                 if cupsqueue == 'cups':
02494                     text = _("Remote CUPS printer via DNS-SD")
02495                 else:
02496                     protocol = None
02497                     if name.find("._ipp") != -1:
02498                         protocol = "IPP"
02499                     elif name.find("._printer") != -1:
02500                         protocol = "LPD"
02501                     elif name.find("._pdl-datastream") != -1:
02502                         protocol = "AppSocket/JetDirect"
02503                     if protocol != None:
02504                         text = _("%s network printer via DNS-SD") % protocol
02505                     else:
02506                         text = _("Network printer via DNS-SD")
02507             else:
02508                 text = device.uri
02509 
02510             self.lblNPDeviceDescription.set_text (text)
02511         elif device.type=="socket":
02512             (scheme, rest) = urllib.splittype (device.uri)
02513             host = ''
02514             port = 9100
02515             if scheme == "socket":
02516                 (hostport, rest) = urllib.splithost (rest)
02517                 (host, port) = urllib.splitnport (hostport, defport=port)
02518                 debugprint ("socket: host is %s, port is %s" % (host,
02519                                                                 repr (port)))
02520                 if device.location != '':
02521                     location = device.location
02522                 else:
02523                     location = host
02524             self.entNPTJetDirectHostname.set_text (host)
02525             self.entNPTJetDirectPort.set_text (str (port))
02526         elif device.type=="serial":
02527             if not device.is_class:
02528                 options = device.uri.split("?")[1]
02529                 options = options.split("+")
02530                 option_dict = {}
02531                 for option in options:
02532                     name, value = option.split("=")
02533                     option_dict[name] = value
02534 
02535                 for widget, name, optionvalues in (
02536                     (self.cmbNPTSerialBaud, "baud", None),
02537                     (self.cmbNPTSerialBits, "bits", None),
02538                     (self.cmbNPTSerialParity, "parity",
02539                      ["none", "odd", "even"]),
02540                     (self.cmbNPTSerialFlow, "flow",
02541                      ["none", "soft", "hard", "hard"])):
02542                     if option_dict.has_key(name): # option given in URI?
02543                         if optionvalues is None: # use text in widget
02544                             model = widget.get_model()
02545                             iter = model.get_iter_first()
02546                             nr = 0
02547                             while iter:
02548                                 value = model.get(iter,0)[0]
02549                                 if value == option_dict[name]:
02550                                     widget.set_active(nr)
02551                                     break
02552                                 iter = model.iter_next(iter)
02553                                 nr += 1
02554 
02555                             if not iter:
02556                                 widget.set_active (0)
02557                         else: # use optionvalues
02558                             nr = optionvalues.index(
02559                                 option_dict[name])
02560                             widget.set_active(nr+1) # compensate "Default"
02561                     else:
02562                         widget.set_active(0)
02563 
02564         # XXX FILL TABS FOR VALID DEVICE URIs
02565         elif device.type=="lpd":
02566             self.entNPTLpdHost.set_text ('')
02567             self.entNPTLpdQueue.set_text ('')
02568             self.entNPTLpdQueue.set_completion (None)
02569             self.btnNPTLpdProbe.set_sensitive (False)
02570             if len (device.uri) > 6:
02571                 host = device.uri[6:]
02572                 i = host.find ("/")
02573                 if i != -1:
02574                     printer = host[i + 1:]
02575                     host = host[:i]
02576                 else:
02577                     printer = ""
02578                 self.entNPTLpdHost.set_text (host)
02579                 self.entNPTLpdQueue.set_text (printer)
02580                 location = host
02581                 self.btnNPTLpdProbe.set_sensitive (True)
02582         elif device.uri == "smb":
02583             self.entSMBURI.set_text('')
02584             self.btnSMBVerify.set_sensitive(False)
02585         elif device.type == "smb":
02586             self.entSMBUsername.set_text ('')
02587             self.entSMBPassword.set_text ('')
02588             self.entSMBURI.set_text(device.uri[6:])
02589             self.btnSMBVerify.set_sensitive(True)
02590         else:
02591             if device.uri:
02592                 self.entNPTDevice.set_text(device.uri)
02593 
02594         try:
02595             if len (location) == 0 and self.device.device_class == "direct":
02596                 # Set location to the name of this host.
02597                 if (self._host == 'localhost' or
02598                     self._host[0] == '/'):
02599                     u = os.uname ()
02600                     location = u[1]
02601                 else:
02602                     location = self._host
02603 
02604             # Pre-fill location field.
02605             self.entNPLocation.set_text (location)
02606         except:
02607             nonfatalException ()
02608 
02609         self.setNPButtons()
02610 
02611     def on_entNPTLpdHost_changed(self, ent):
02612         hostname = ent.get_text()
02613         self.btnNPTLpdProbe.set_sensitive (len (hostname) > 0)
02614         self.setNPButtons()
02615 
02616     def on_entNPTLpdQueue_changed(self, ent):
02617         self.setNPButtons()
02618 
02619     def on_btnNPTLpdProbe_clicked(self, button):
02620         # read hostname, probe, fill printer names
02621         hostname = self.entNPTLpdHost.get_text()
02622         server = probe_printer.LpdServer(hostname)
02623 
02624         self.lblWait.set_markup ('<span weight="bold" size="larger">' +
02625                                  _('Searching') + '</span>\n\n' +
02626                                  _('Searching for printers'))
02627         self.WaitWindow.set_transient_for (self.NewPrinterWindow)
02628         self.WaitWindow.show_now ()
02629         busy (self.WaitWindow)
02630         def stop (widget, event):
02631             server.destroy ()
02632             return True
02633 
02634         self.WaitWindow.disconnect (self.WaitWindow_handler)
02635         signal = self.WaitWindow.connect ("delete-event", stop)
02636         printers = server.probe()
02637         self.WaitWindow.disconnect (signal)
02638         self.WaitWindow_handler = self.WaitWindow.connect ("delete-event",
02639                                                            on_delete_just_hide)
02640         self.WaitWindow.hide ()
02641 
02642         model = gtk.ListStore (gobject.TYPE_STRING)
02643         for printer in printers:
02644             model.append ([printer])
02645 
02646         completion = gtk.EntryCompletion ()
02647         completion.set_model (model)
02648         completion.set_text_column (0)
02649         completion.set_minimum_key_length (0)
02650         self.entNPTLpdQueue.set_completion (completion)
02651 
02652     ### Find Network Printer
02653     def on_entNPTNetworkHostname_changed(self, ent):
02654         text = ent.get_text ()
02655         if text.find (":") != -1:
02656             # The user is typing in a URI.  In that case, switch to URI entry.
02657             ent.set_text ('')
02658             debugprint ("URI detected (%s) -> Enter URI" % text)
02659             self.entNPTDevice.set_text (text)
02660             model = self.tvNPDevices.get_model ()
02661             path = model.get_path (self.devices_uri_iter)
02662             self.tvNPDevices.set_cursor (path)
02663             self.entNPTDevice.select_region (0, 0)
02664             self.entNPTDevice.set_position (-1)
02665             return
02666 
02667         allowed_chars = string.letters+string.digits+'_-.'
02668         self.entry_changed(ent, allowed_chars)
02669         s = ent.get_text ()
02670         self.btnNetworkFind.set_sensitive (len (s) > 0)
02671         self.lblNetworkFindNotFound.hide ()
02672         self.setNPButtons ()
02673 
02674     def on_btnNetworkFind_clicked(self, button):
02675         host = self.entNPTNetworkHostname.get_text ()
02676 
02677         def found_callback (new_device):
02678             if self.printer_finder == None:
02679                 return
02680 
02681             gobject.idle_add (self.found_network_printer_callback, new_device)
02682 
02683         self.btnNetworkFind.set_sensitive (False)
02684         self.entNPTNetworkHostname.set_sensitive (False)
02685         self.network_found = 0
02686         self.lblNetworkFindNotFound.hide ()
02687         self.lblNetworkFindSearching.show_all ()
02688         finder = probe_printer.PrinterFinder ()
02689         self.inc_spinner_task ()
02690         finder.find (host, found_callback)
02691         self.printer_finder = finder
02692 
02693     def found_network_printer_callback (self, new_device):
02694         gtk.gdk.threads_enter ()
02695         if new_device:
02696             self.network_found += 1
02697             dev = PhysicalDevice (new_device)
02698             try:
02699                 i = self.devices.index (dev)
02700 
02701                 # Adding a new URI to an existing physical device.
02702                 self.devices[i].add_device (new_device)
02703 
02704                 (path, column) = self.tvNPDevices.get_cursor ()
02705                 if path:
02706                     model = self.tvNPDevices.get_model ()
02707                     iter = model.get_iter (path)
02708                     if model.get_value (iter, 1) == self.devices[i]:
02709                         self.on_tvNPDevices_cursor_changed (self.tvNPDevices)
02710             except ValueError:
02711                 # New physical device.
02712                 dev.set_data ('checked-hplip', True)
02713                 self.devices.append (dev)
02714                 self.devices.sort ()
02715                 model = self.tvNPDevices.get_model ()
02716                 iter = model.insert_before (None, self.devices_find_nw_iter,
02717                                             row=[dev.get_info (), dev, False])
02718 
02719                 # If this is the first one we've found, select it.
02720                 if self.network_found == 1:
02721                     path = model.get_path (iter)
02722                     self.tvNPDevices.set_cursor (path)
02723         else:
02724             self.printer_finder = None
02725             self.dec_spinner_task ()
02726             self.lblNetworkFindSearching.hide ()
02727             self.entNPTNetworkHostname.set_sensitive (True)
02728             self.btnNetworkFind.set_sensitive (True)
02729             if self.network_found == 0:
02730                 self.lblNetworkFindNotFound.set_markup ('<i>' +
02731                                                         _("No printer was "
02732                                                           "found at that "
02733                                                           "address.") + '</i>')
02734                 self.lblNetworkFindNotFound.show ()
02735 
02736         gtk.gdk.threads_leave ()
02737     ###
02738 
02739     def getDeviceURI(self):
02740         type = self.device.type
02741         page = self.new_printer_device_tabs.get (type, 1)
02742         device = type
02743         if page == 0:
02744             # The "no options page".  We already have the URI.
02745             device = self.device.uri
02746         elif type == "socket": # JetDirect
02747             host = self.entNPTJetDirectHostname.get_text()
02748             port = self.entNPTJetDirectPort.get_text()
02749             if host:
02750                 device += "://" + host
02751                 if port:
02752                     device += ":" + port
02753         elif type == "lpd": # LPD
02754             host = self.entNPTLpdHost.get_text()
02755             printer = self.entNPTLpdQueue.get_text()
02756             if host:
02757                 device += "://" + host
02758                 if printer:
02759                     device += "/" + printer
02760         elif type == "serial": # Serial
02761             options = []
02762             for widget, name, optionvalues in (
02763                 (self.cmbNPTSerialBaud, "baud", None),
02764                 (self.cmbNPTSerialBits, "bits", None),
02765                 (self.cmbNPTSerialParity, "parity",
02766                  ("none", "odd", "even")),
02767                 (self.cmbNPTSerialFlow, "flow",
02768                  ("none", "soft", "hard", "hard"))):
02769                 nr = widget.get_active()
02770                 if nr:
02771                     if optionvalues is not None:
02772                         option = optionvalues[nr-1]
02773                     else:
02774                         option = widget.get_active_text()
02775                     options.append(name + "=" + option)
02776             options = "+".join(options)
02777             device =  self.device.uri.split("?")[0] #"serial:/dev/ttyS%s"
02778             if options:
02779                 device = device + "?" + options
02780         elif type == "smb":
02781             uri = self.entSMBURI.get_text ()
02782             (group, host, share, u, p) = SMBURI (uri=uri).separate ()
02783             user = ''
02784             password = ''
02785             if self.rbtnSMBAuthSet.get_active ():
02786                 user = self.entSMBUsername.get_text ()
02787                 password = self.entSMBPassword.get_text ()
02788             uri = SMBURI (group=group, host=host, share=share,
02789                           user=user, password=password).get_uri ()
02790             if uri:
02791                 device += "://" + uri
02792         else:
02793             device = self.entNPTDevice.get_text()
02794         return device
02795 
02796     # PPD
02797 
02798     def on_rbtnNPFoomatic_toggled(self, widget):
02799         rbtn1 = self.rbtnNPFoomatic.get_active()
02800         rbtn2 = self.rbtnNPPPD.get_active()
02801         rbtn3 = self.rbtnNPDownloadableDriverSearch.get_active()
02802         self.tvNPMakes.set_sensitive(rbtn1)
02803         self.filechooserPPD.set_sensitive(rbtn2)
02804 
02805         if rbtn1:
02806             page = 0
02807         if rbtn2:
02808             page = 1
02809         if rbtn3:
02810             page = 2
02811         self.ntbkPPDSource.set_current_page (page)
02812 
02813         if not rbtn3 and self.openprinting_query_handle:
02814             # Need to cancel a search in progress.
02815             self.openprinting.cancelOperation (self.openprinting_query_handle)
02816             self.openprinting_query_handle = None
02817             self.btnNPDownloadableDriverSearch_label.set_text (_("Search"))
02818             # Clear printer list.
02819             model = gtk.ListStore (str, str)
02820             combobox = self.cmbNPDownloadableDriverFoundPrinters
02821             combobox.set_model (model)
02822             combobox.set_sensitive (False)
02823 
02824         for widget in [self.entNPDownloadableDriverSearch,
02825                        self.cmbNPDownloadableDriverFoundPrinters]:
02826             widget.set_sensitive(rbtn3)
02827         self.btnNPDownloadableDriverSearch.\
02828             set_sensitive (rbtn3 and (self.openprinting_query_handle == None))
02829 
02830         self.setNPButtons()
02831 
02832     def on_filechooserPPD_selection_changed(self, widget):
02833         self.setNPButtons()
02834 
02835     def on_btnNPDownloadableDriverSearch_clicked(self, widget):
02836         if self.openprinting_query_handle != None:
02837             self.openprinting.cancelOperation (self.openprinting_query_handle)
02838             self.openprinting_query_handle = None
02839 
02840         widget.set_sensitive (False)
02841         label = self.btnNPDownloadableDriverSearch_label
02842         label.set_text (_("Searching"))
02843         searchterm = self.entNPDownloadableDriverSearch.get_text ()
02844         debugprint ('Searching for "%s"' % searchterm)
02845         self.drivers_lock.acquire (0)
02846         self.openprinting_query_handle = \
02847             self.openprinting.searchPrinters (searchterm,
02848                                               self.openprinting_printers_found)
02849         self.cmbNPDownloadableDriverFoundPrinters.set_sensitive (False)
02850 
02851     def openprinting_printers_found (self, status, user_data, printers):
02852         if status != 0:
02853             # Should report error.
02854             print "HTTP Status %d" % status
02855             print printers
02856             print traceback.extract_tb(printers[2], limit=None)
02857             self.downloadable_printers = []
02858             self.openprinting_drivers_found ()
02859             return
02860 
02861         self.openprinting_query_handle = None
02862         self.downloadable_printers_unchecked = map (lambda x:
02863                                                         (x, printers[x]),
02864                                                     printers)
02865         self.downloadable_printers = []
02866         self.downloadable_drivers = dict() # by printer id of dict
02867 
02868         # Kick off a search for drivers for each model.
02869         self.openprinting_query_next_printer ()
02870 
02871     def openprinting_query_next_printer (self):
02872         """
02873         If there are more printers to query, kick off a query and
02874         return True.
02875 
02876         Otherwise return False.
02877         """
02878 
02879         try:
02880             a = self.downloadable_printers_unchecked.pop ()
02881             (printer_id, printer_name) = a
02882         except IndexError:
02883             debugprint ("All printer driver queries finished")
02884             return False
02885 
02886         extra_options = dict()
02887         if self.DOWNLOADABLE_ONLYPPD:
02888             extra_options['onlyppdfiles'] = '1'
02889 
02890         debugprint ("Querying drivers for %s" % printer_id)
02891         self.openprinting_query_handle = \
02892             self.openprinting.listDrivers (printer_id,
02893                                            self.openprinting_printer_drivers_found,
02894                                            user_data=(printer_id, printer_name),
02895                                            extra_options=extra_options)
02896 
02897         return True
02898 
02899     def openprinting_printer_drivers_found (self, status, user_data, drivers):
02900         self.openprinting_query_handle = None
02901         if status != 0:
02902             print "HTTP Status %d" % status
02903             print drivers
02904             print traceback.extract_tb(drivers[2], limit=None)
02905             self.downloadable_printers = []
02906             self.openprinting_drivers_found ()
02907             return
02908 
02909         if drivers:
02910             debugprint (" - drivers found")
02911             (printer_id, printer_name) = user_data
02912             self.downloadable_drivers[printer_id] = drivers
02913             self.downloadable_printers.append (user_data)
02914 
02915         if not self.openprinting_query_next_printer ():
02916             self.drivers_lock.release ()
02917             self.openprinting_drivers_found ()
02918 
02919     def openprinting_drivers_found (self):
02920         button = self.btnNPDownloadableDriverSearch
02921         label = self.btnNPDownloadableDriverSearch_label
02922         gtk.gdk.threads_enter ()
02923         try:
02924             label.set_text (_("Search"))
02925             button.set_sensitive (True)
02926             model = gtk.ListStore (str, str)
02927             if len (self.downloadable_printers) != 1:
02928                 if len (self.downloadable_printers) > 1:
02929                     first = _("-- Select from search results --")
02930                 else:
02931                     first = _("-- No matches found --")
02932 
02933                 iter = model.append (None)
02934                 model.set_value (iter, 0, first)
02935                 model.set_value (iter, 1, None)
02936 
02937             sorted_list = []
02938             for printer_id, printer_name in self.downloadable_printers:
02939                 sorted_list.append ((printer_id, printer_name))
02940 
02941             sorted_list.sort (lambda x, y: cups.modelSort (x[1], y[1]))
02942             sought = self.entNPDownloadableDriverSearch.get_text ().lower ()
02943             select_index = 0
02944             for id, name in sorted_list:
02945                 iter = model.append (None)
02946                 model.set_value (iter, 0, name)
02947                 model.set_value (iter, 1, id)
02948                 if name.lower () == sought:
02949                     select_index = model.get_path (iter)[0]
02950             combobox = self.cmbNPDownloadableDriverFoundPrinters
02951             combobox.set_model (model)
02952             combobox.set_active (select_index)
02953             combobox.set_sensitive (True)
02954             self.setNPButtons ()
02955         except:
02956             nonfatalException()
02957         gtk.gdk.threads_leave ()
02958 
02959     def on_cmbNPDownloadableDriverFoundPrinters_changed(self, widget):
02960         self.setNPButtons ()
02961 
02962         if self.openprinting_query_handle != None:
02963             self.openprinting.cancelOperation (self.openprinting_query_handle)
02964             self.openprinting_query_handle = None
02965             self.drivers_lock.release()
02966 
02967     def fillDownloadableDrivers(self):
02968         # Clear out the properties.
02969         self.lblNPDownloadableDriverSupplier.set_text ('')
02970         self.lblNPDownloadableDriverLicense.set_text ('')
02971         self.lblNPDownloadableDriverDescription.set_text ('')
02972         self.lblNPDownloadableDriverSupportContacts.set_text ('')
02973         self.rbtnNPDownloadLicenseNo.set_active (True)
02974         self.frmNPDownloadableDriverLicenseTerms.hide ()
02975 
02976         widget = self.cmbNPDownloadableDriverFoundPrinters
02977         model = widget.get_model ()
02978         iter = widget.get_active_iter ()
02979         if iter:
02980             printer_id = model.get_value (iter, 1)
02981         else:
02982             printer_id = None
02983 
02984         if printer_id == None:
02985             return
02986 
02987         drivers = self.downloadable_drivers[printer_id]
02988         model = gtk.ListStore (str,                     # driver name
02989                                gobject.TYPE_PYOBJECT)   # driver data
02990         recommended_iter = None
02991         first_iter = None
02992         for driver in drivers.values ():
02993             iter = model.append (None)
02994             if first_iter == None:
02995                 first_iter = iter
02996 
02997             model.set_value (iter, 0, driver['name'])
02998             model.set_value (iter, 1, driver)
02999 
03000             if driver['recommended']:
03001                 recommended_iter = iter
03002 
03003         if recommended_iter == None:
03004             recommended_iter = first_iter
03005 
03006         treeview = self.tvNPDownloadableDrivers
03007         treeview.set_model (model)
03008         if recommended_iter != None:
03009             treeview.get_selection ().select_iter (recommended_iter)
03010 
03011     def on_rbtnNPDownloadLicense_toggled(self, widget):
03012         self.setNPButtons ()
03013 
03014     # PPD from foomatic
03015 
03016     def fillMakeList(self):
03017         self.recommended_make_selected = False
03018         makes = self.ppds.getMakes()
03019         model = self.tvNPMakes.get_model()
03020         model.clear()
03021         found = False
03022         if self.auto_make:
03023             auto_make_norm = cupshelpers.ppds.normalize (self.auto_make)
03024         else:
03025             auto_make_norm = None
03026 
03027         for make in makes:
03028             recommended = (auto_make_norm and
03029                            cupshelpers.ppds.normalize (make) == auto_make_norm)
03030             if self.device and self.device.make_and_model and recommended:
03031                 text = make + _(" (recommended)")
03032             else:
03033                 text = make
03034 
03035             iter = model.append((text, make,))
03036             if recommended:
03037                 path = model.get_path(iter)
03038                 self.tvNPMakes.set_cursor (path)
03039                 self.tvNPMakes.scroll_to_cell(path, None,
03040                                               True, 0.5, 0.5)
03041                 found = True
03042 
03043         if not found:
03044             self.tvNPMakes.set_cursor (0,)
03045             self.tvNPMakes.scroll_to_cell(0, None, True, 0.0, 0.0)
03046 
03047         # Also pre-fill the OpenPrinting.org search box.
03048         search = ''
03049         if self.device and self.device.id_dict:
03050             devid_dict = self.device.id_dict
03051             if devid_dict["MFG"] and devid_dict["MDL"]:
03052                 search = devid_dict["MFG"] + " " + devid_dict["MDL"]
03053             elif devid_dict["DES"]:
03054                 search = devid_dict["DES"]
03055             elif devid_dict["MFG"]:
03056                 search = devid_dict["MFG"]
03057         if search == '' and self.auto_make != None:
03058             search += self.auto_make
03059             if self.auto_model != None:
03060                 search += " " + self.auto_model
03061         self.entNPDownloadableDriverSearch.set_text (search)
03062 
03063     def on_tvNPMakes_cursor_changed(self, tvNPMakes):
03064         path, column = tvNPMakes.get_cursor()
03065         if path != None:
03066             model = tvNPMakes.get_model ()
03067             iter = model.get_iter (path)
03068             self.NPMake = model.get(iter, 1)[0]
03069             recommended_make = (self.auto_make and
03070                                 cupshelpers.ppds.normalize (self.auto_make) ==
03071                                 cupshelpers.ppds.normalize (self.NPMake))
03072             self.recommended_make_selected = recommended_make
03073             self.fillModelList()
03074 
03075     def fillModelList(self):
03076         self.recommended_model_selected = False
03077         models = self.ppds.getModels(self.NPMake)
03078         model = self.tvNPModels.get_model()
03079         model.clear()
03080         selected = False
03081         is_auto_make = (cupshelpers.ppds.normalize (self.NPMake) ==
03082                         cupshelpers.ppds.normalize (self.auto_make))
03083         if is_auto_make:
03084             auto_model_norm = cupshelpers.ppds.normalize (self.auto_model)
03085 
03086         for pmodel in models:
03087             recommended = (is_auto_make and
03088                            cupshelpers.ppds.normalize (pmodel) ==
03089                            auto_model_norm)
03090             if self.device and self.device.make_and_model and recommended:
03091                 text = pmodel + _(" (recommended)")
03092             else:
03093                 text = pmodel
03094 
03095             iter = model.append((text, pmodel,))
03096             if recommended:
03097                 path = model.get_path(iter)
03098                 self.tvNPModels.set_cursor (path)
03099                 self.tvNPModels.scroll_to_cell(path, None,
03100                                                True, 0.5, 0.5)
03101                 selected = True
03102         if not selected:
03103             self.tvNPModels.set_cursor (0,)
03104             self.tvNPModels.scroll_to_cell(0, None, True, 0.0, 0.0)
03105         self.tvNPModels.columns_autosize()
03106 
03107     def fillDriverList(self, pmake, pmodel):
03108         self.NPModel = pmodel
03109         model = self.tvNPDrivers.get_model()
03110         model.clear()
03111 
03112         if self.device:
03113             devid = self.device.id_dict
03114         else:
03115             devid = None
03116 
03117         if (self.device and self.device.make_and_model and
03118             self.recommended_model_selected and
03119             self.id_matched_ppdnames):
03120             # Use the actual device-make-and-model string.
03121             make_and_model = self.device.make_and_model
03122 
03123             # and the ID-matched list of PPDs.
03124             self.NPDrivers = self.id_matched_ppdnames
03125             debugprint ("ID matched PPDs: %s" % repr (self.NPDrivers))
03126         elif self.ppds:
03127             # Use a generic make and model string for generating the
03128             # driver preference list.
03129             make_and_model = pmake + " " + pmodel
03130             ppds = self.ppds.getInfoFromModel(pmake, pmodel)
03131             ppdnames = ppds.keys ()
03132 
03133             files = self.jockey_installed_files
03134             try:
03135                 self.NPDrivers = self.ppds.orderPPDNamesByPreference(ppdnames,
03136                                                                      files,
03137                                                                      make_and_model,
03138                                                                      devid)
03139             except:
03140                 nonfatalException ()
03141                 self.NPDrivers = ppdnames
03142 
03143             # Put the current driver first.
03144             if self.auto_driver and self.device:
03145                 drivers = []
03146                 for driver in self.NPDrivers:
03147                     if driver == self.auto_driver:
03148                         drivers.insert (0, driver)
03149                     else:
03150                         drivers.append (driver)
03151 
03152                 self.NPDrivers = drivers
03153         else:
03154             # No available PPDs for some reason(!)
03155             debugprint ("No PPDs available?")
03156             self.NPDrivers = []
03157 
03158         driverlist = []
03159         NPDrivers = []
03160         i = 0
03161         for ppdname in self.NPDrivers:
03162             ppd = self.ppds.getInfoFromPPDName (ppdname)
03163             driver = _singleton (ppd["ppd-make-and-model"])
03164             driver = driver.replace(" (recommended)", "")
03165 
03166             try:
03167                 lpostfix = " [%s]" % _singleton (ppd["ppd-natural-language"])
03168                 driver += lpostfix
03169             except KeyError:
03170                 pass
03171 
03172             duplicate = driver in driverlist
03173 
03174             if (not (self.device and self.device.make_and_model) and
03175                 self.auto_driver == ppdname):
03176                 driverlist.append (driver)
03177                 NPDrivers.append (ppdname)
03178                 i += 1
03179                 iter = model.append ((driver + _(" (Current)"),))
03180                 path = model.get_path (iter)
03181                 self.tvNPDrivers.get_selection().select_path(path)
03182                 self.tvNPDrivers.scroll_to_cell(path, None, True, 0.5, 0.0)
03183             elif self.device and i == 0:
03184                 driverlist.append (driver)
03185                 NPDrivers.append (ppdname)
03186                 i += 1
03187                 iter = model.append ((driver + _(" (recommended)"),))
03188                 path = model.get_path (iter)
03189                 self.tvNPDrivers.get_selection().select_path(path)
03190                 self.tvNPDrivers.scroll_to_cell(path, None, True, 0.5, 0.0)
03191             else:
03192                 if duplicate:
03193                     continue
03194                 driverlist.append (driver)
03195                 NPDrivers.append (ppdname)
03196                 i += 1
03197                 model.append((driver, ))
03198 
03199         self.NPDrivers = NPDrivers
03200         self.tvNPDrivers.columns_autosize()
03201 
03202     def on_NPDrivers_query_tooltip(self, tv, x, y, keyboard_mode, tooltip):
03203         if keyboard_mode:
03204             path = tv.get_cursor()[0]
03205             if path is None:
03206                 return False
03207         else:
03208             bin_x, bin_y = tv.convert_widget_to_bin_window_coords(x, y)
03209             ret = tv.get_path_at_pos (bin_x, bin_y)
03210             if ret is None:
03211                 return False
03212             path = ret[0]
03213 
03214         drivername = self.NPDrivers[path[0]]
03215         ppddict = self.ppds.getInfoFromPPDName(drivername)
03216         markup = _singleton (ppddict['ppd-make-and-model'])
03217         if (drivername.startswith ("foomatic:")):
03218             markup += " "
03219             markup += _("This PPD is generated by foomatic.")
03220         tooltip.set_markup(markup)
03221         return True
03222 
03223     def on_tvNPModels_cursor_changed(self, widget):
03224         path, column = widget.get_cursor()
03225         if path != None:
03226             model = widget.get_model ()
03227             iter = model.get_iter (path)
03228             pmodel = model.get(iter, 1)[0]
03229 
03230             # Find out if this is the auto-detected make and model
03231             recommended_model = (self.recommended_make_selected and
03232                                  self.auto_model and
03233                                  self.auto_model.lower () == pmodel.lower ())
03234             self.recommended_model_selected = recommended_model
03235             self.fillDriverList(self.NPMake, pmodel)
03236             self.on_tvNPDrivers_cursor_changed(self.tvNPDrivers)
03237 
03238     def on_tvNPDrivers_cursor_changed(self, widget):
03239         self.setNPButtons()
03240 
03241     def on_tvNPDownloadableDrivers_cursor_changed(self, widget):
03242         model, iter = widget.get_selection ().get_selected ()
03243         if not iter:
03244             path, column = widget.get_cursor()
03245             iter = model.get_iter (path)
03246         driver = model.get_value (iter, 1)
03247         import pprint
03248         pprint.pprint (driver)
03249         self.ntbkNPDownloadableDriverProperties.set_current_page(1)
03250         supplier = driver.get('supplier', _("OpenPrinting"))
03251         vendor = self.cbNPDownloadableDriverSupplierVendor
03252         active = driver['manufacturersupplied']
03253 
03254         def set_protect_active (widget, active):
03255             widget.set_active (active)
03256             widget.set_data ('protect_active', active)
03257 
03258         set_protect_active (vendor, active)
03259         self.lblNPDownloadableDriverSupplier.set_text (supplier)
03260 
03261         license = driver.get('license', _("Distributable"))
03262         patents = self.cbNPDownloadableDriverLicensePatents
03263         free = self.cbNPDownloadableDriverLicenseFree
03264         set_protect_active (patents, driver['patents'])
03265         set_protect_active (free, driver['freesoftware'])
03266         self.lblNPDownloadableDriverLicense.set_text (license)
03267 
03268         description = driver.get('shortdescription', _("None"))
03269         self.lblNPDownloadableDriverDescription.set_markup (description)
03270 
03271         functionality = driver['functionality']
03272         for field in ["Graphics", "LineArt", "Photo", "Text"]:
03273             key = field.lower ()
03274             value = None
03275             hs = self.__dict__.get ("hsDownloadableDriverPerf%s" % field)
03276             unknown = self.__dict__.get ("lblDownloadableDriverPerf%sUnknown"
03277                                          % field)
03278             if functionality.has_key (key):
03279                 if hs:
03280                     try:
03281                         value = int (functionality[key])
03282                         hs.set_value (value)
03283                         hs.show_all ()
03284                         unknown.hide ()
03285                     except:
03286                         pass
03287 
03288             if value == None:
03289                 hs.hide ()
03290                 unknown.show_all ()
03291         supportcontacts = ""
03292         if driver.has_key ('supportcontacts'):
03293             for supportentry in driver['supportcontacts']:
03294                 if supportentry['name']:
03295                     supportcontact = " - " + supportentry['name']
03296                     supportcontact_extra = ""
03297                     if supportentry['url']:
03298                         supportcontact_extra = supportcontact_extra + \
03299                             supportentry['url']
03300                     if supportentry['level']:
03301                         if supportcontact_extra:
03302                             supportcontact_extra = supportcontact_extra + _(", ")
03303                         supportcontact_extra = supportcontact_extra + \
03304                             supportentry['level']
03305                     if supportcontact_extra:
03306                         supportcontact = supportcontact + \
03307                             _("\n(%s)") % supportcontact_extra
03308                         if supportcontacts:
03309                             supportcontacts = supportcontacts + "\n"
03310                         supportcontacts = supportcontacts + supportcontact
03311         if not supportcontacts:
03312             supportcontacts = _("No support contacts known")
03313         self.lblNPDownloadableDriverSupportContacts.set_text (supportcontacts)
03314         if driver.has_key ('licensetext'):
03315             self.frmNPDownloadableDriverLicenseTerms.show ()
03316             terms = driver.get('licensetext', _("Not specified."))
03317             self.tvNPDownloadableDriverLicense.get_buffer ().set_text (terms)
03318         else:
03319             self.frmNPDownloadableDriverLicenseTerms.hide ()
03320         if not driver['nonfreesoftware'] and not driver['patents']:
03321             self.rbtnNPDownloadLicenseYes.set_active (True)
03322             self.rbtnNPDownloadLicenseYes.hide ()
03323             self.rbtnNPDownloadLicenseNo.hide ()
03324         else:
03325             self.rbtnNPDownloadLicenseNo.set_active (True)
03326             self.rbtnNPDownloadLicenseYes.show ()
03327             self.rbtnNPDownloadLicenseNo.show ()
03328             self.frmNPDownloadableDriverLicenseTerms.show ()
03329             terms = driver.get('licensetext', _("Not specified."))
03330             self.tvNPDownloadableDriverLicense.get_buffer ().set_text (terms)
03331 
03332         self.setNPButtons()
03333 
03334     def getNPPPD(self):
03335         try:
03336             if self.rbtnNPFoomatic.get_active():
03337                 model, iter = self.tvNPDrivers.get_selection().get_selected()
03338                 nr = model.get_path(iter)[0]
03339                 ppd = self.NPDrivers[nr]
03340             elif self.rbtnNPPPD.get_active():
03341                 ppd = cups.PPD(self.filechooserPPD.get_filename())
03342             else:
03343                 # PPD of the driver downloaded from OpenPrinting XXX
03344                 treeview = self.tvNPDownloadableDrivers
03345                 model, iter = treeview.get_selection ().get_selected ()
03346                 driver = model.get_value (iter, 1)
03347                 if driver.has_key ('ppds'):
03348                     # Only need to download a PPD.
03349                     if (len(driver['ppds']) > 0):
03350                         file_to_download = driver['ppds'][0]
03351                         debugprint ("ppd file to download [" + file_to_download+ "]")
03352                         file_to_download = file_to_download.strip()
03353                         if (len(file_to_download) > 0):
03354                             ppdurlobj = urllib.urlopen(file_to_download)
03355                             ppdcontent = ppdurlobj.read()
03356                             ppdurlobj.close()
03357                             (tmpfd, ppdname) = tempfile.mkstemp()
03358                             debugprint(ppdname)
03359                             ppdfile = os.fdopen(tmpfd, 'w')
03360                             ppdfile.write(ppdcontent)
03361                             ppdfile.close()
03362                             ppd = cups.PPD(ppdname)
03363                             os.unlink(ppdname)
03364 
03365         except RuntimeError, e:
03366             debugprint ("RuntimeError: " + str(e))
03367             if self.rbtnNPFoomatic.get_active():
03368                 # Foomatic database problem of some sort.
03369                 err_title = _('Database error')
03370                 err_text = _("The '%s' driver cannot be "
03371                              "used with printer '%s %s'.")
03372                 model, iter = (self.tvNPDrivers.get_selection().
03373                                get_selected())
03374                 nr = model.get_path(iter)[0]
03375                 driver = self.NPDrivers[nr]
03376                 if driver.startswith ("gutenprint"):
03377                     # This printer references some XML that is not
03378                     # installed by default.  Point the user at the
03379                     # package they need to install.
03380                     err = _("You will need to install the '%s' package "
03381                             "in order to use this driver.") % \
03382                             "gutenprint-foomatic"
03383                 else:
03384                     err = err_text % (driver, self.NPMake, self.NPModel)
03385             elif self.rbtnNPPPD.get_active():
03386                 # This error came from trying to open the PPD file.
03387                 err_title = _('PPD error')
03388                 filename = self.filechooserPPD.get_filename()
03389                 err = _('Failed to read PPD file.  Possible reason '
03390                         'follows:') + '\n'
03391                 try:
03392                     # We want this to be in the current natural language,
03393                     # so we intentionally don't set LC_ALL=C here.
03394                     p = subprocess.Popen (['/usr/bin/cupstestppd',
03395                                            '-rvv', filename],
03396                                           close_fds=True,
03397                                           stdin=file("/dev/null"),
03398                                           stdout=subprocess.PIPE,
03399                                           stderr=subprocess.PIPE)
03400                     (stdout, stderr) = p.communicate ()
03401                     err += stdout
03402                 except:
03403                     # Problem executing command.
03404                     raise
03405             else:
03406                 # Failed to get PPD downloaded from OpenPrinting XXX
03407                 err_title = _('Downloadable drivers')
03408                 err = _("Failed to download PPD.")
03409 
03410             show_error_dialog (err_title, err, self.NewPrinterWindow)
03411             return None
03412 
03413         debugprint("ppd: " + repr(ppd))
03414         if isinstance(ppd, str) or isinstance(ppd, unicode):
03415             self.cups._begin_operation (_("fetching PPD"))
03416             try:
03417                 if ppd != "raw":
03418                     f = self.cups.getServerPPD(ppd)
03419                     ppd = cups.PPD(f)
03420                     os.unlink(f)
03421             except RuntimeError:
03422                 nonfatalException()
03423                 debugprint ("libcups from CUPS 1.3 not available: never mind")
03424             except cups.IPPError:
03425                 nonfatalException()
03426                 debugprint ("CUPS 1.3 server not available: never mind")
03427 
03428             self.cups._end_operation ()
03429 
03430         return ppd
03431 
03432     # Installable Options
03433 
03434     def fillNPInstallableOptions(self):
03435         debugprint ("Examining installable options")
03436         self.installable_options = False
03437         self.options = { }
03438 
03439         container = self.vbNPInstallOptions
03440         for child in container.get_children():
03441             container.remove(child)
03442 
03443         if not self.ppd:
03444             l = gtk.Label(_("No Installable Options"))
03445             container.add(l)
03446             l.show()
03447             debugprint ("No PPD so no installable options")
03448             return
03449 
03450         # build option tabs
03451         for group in self.ppd.optionGroups:
03452             if group.name != "InstallableOptions":
03453                 continue
03454             self.installable_options = True
03455 
03456             table = gtk.Table(1, 3, False)
03457             table.set_col_spacings(6)
03458             table.set_row_spacings(6)
03459             container.add(table)
03460             rows = 0
03461 
03462             for nr, option in enumerate(group.options):
03463                 if option.keyword == "PageRegion":
03464                     continue
03465                 rows += 1
03466                 table.resize (rows, 3)
03467                 o = OptionWidget(option, self.ppd, self)
03468                 table.attach(o.conflictIcon, 0, 1, nr, nr+1, 0, 0, 0, 0)
03469 
03470                 hbox = gtk.HBox()
03471                 if o.label:
03472                     a = gtk.Alignment (0.5, 0.5, 1.0, 1.0)
03473                     a.set_padding (0, 0, 0, 6)
03474                     a.add (o.label)
03475                     table.attach(a, 1, 2, nr, nr+1, gtk.FILL, 0, 0, 0)
03476                     table.attach(hbox, 2, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
03477                 else:
03478                     table.attach(hbox, 1, 3, nr, nr+1, gtk.FILL, 0, 0, 0)
03479                 hbox.pack_start(o.selector, False)
03480                 self.options[option.keyword] = o
03481         if not self.installable_options:
03482             l = gtk.Label(_("No Installable Options"))
03483             container.add(l)
03484             l.show()
03485         self.scrNPInstallableOptions.hide()
03486         self.scrNPInstallableOptions.show_all()
03487 
03488 
03489     # Create new Printer
03490     def on_btnNPApply_clicked(self, widget):
03491         if self.fetchDevices_conn:
03492             self.fetchDevices_conn.destroy ()
03493             self.fetchDevices_conn = None
03494             self.dec_spinner_task ()
03495 
03496         if self.ppdsloader:
03497             self.ppdsloader.destroy ()
03498             self.ppdsloader = None
03499 
03500         if self.printer_finder:
03501             self.printer_finder.cancel ()
03502             self.printer_finder = None
03503             self.dec_spinner_task ()
03504 
03505         if self.dialog_mode in ("class", "printer", "printer_with_uri"):
03506             name = unicode (self.entNPName.get_text())
03507             location = unicode (self.entNPLocation.get_text())
03508             info = unicode (self.entNPDescription.get_text())
03509         else:
03510             name = self._name
03511 
03512         # Whether to check for missing drivers.
03513         check = False
03514         checkppd = None
03515         ppd = self.ppd
03516 
03517         if self.dialog_mode == "class":
03518             members = getCurrentClassMembers(self.tvNCMembers)
03519             try:
03520                 for member in members:
03521                     self.cups.addPrinterToClass(member, name)
03522             except cups.IPPError, (e, msg):
03523                 self.show_IPP_Error(e, msg)
03524                 return
03525         elif self.dialog_mode == "printer" or \
03526                 self.dialog_mode == "printer_with_uri":
03527             uri = None
03528             if self.device.uri:
03529                 uri = self.device.uri
03530             else:
03531                 uri = self.getDeviceURI()
03532             if not self.ppd: # XXX needed?
03533                 # Go back to previous page to re-select driver.
03534                 self.nextNPTab(-1)
03535                 return
03536 
03537             # write Installable Options to ppd
03538             for option in self.options.itervalues():
03539                 option.writeback()
03540 
03541             busy (self.NewPrinterWindow)
03542             while gtk.events_pending ():
03543                 gtk.main_iteration ()
03544             self.cups._begin_operation (_("adding printer %s") % name)
03545             try:
03546                 if isinstance(ppd, str) or isinstance(ppd, unicode):
03547                     self.cups.addPrinter(name, ppdname=ppd,
03548                          device=uri, info=info, location=location)
03549                     check = True
03550                 elif ppd is None: # raw queue
03551                     self.cups.addPrinter(name, device=uri,
03552                                          info=info, location=location)
03553                 else:
03554                     cupshelpers.setPPDPageSize(ppd, self.language[0])
03555                     self.cups.addPrinter(name, ppd=ppd, device=uri,
03556                                          info=info, location=location)
03557                     check = True
03558                     checkppd = ppd
03559             except cups.IPPError, (e, msg):
03560                 ready (self.NewPrinterWindow)
03561                 self.show_IPP_Error(e, msg)
03562                 self.cups._end_operation()
03563                 return
03564             except:
03565                 ready (self.NewPrinterWindow)
03566                 self.cups._end_operation()
03567                 fatalException (1)
03568             self.cups._end_operation()
03569             ready (self.NewPrinterWindow)
03570         if self.dialog_mode in ("class", "printer", "printer_with_uri"):
03571             self.cups._begin_operation (_("modifying printer %s") % name)
03572             try:
03573                 cupshelpers.activateNewPrinter (self.cups, name)
03574                 self.cups.setPrinterLocation(name, location)
03575                 self.cups.setPrinterInfo(name, info)
03576             except cups.IPPError, (e, msg):
03577                 self.show_IPP_Error(e, msg)
03578                 self.cups._end_operation ()
03579                 return
03580             self.cups._end_operation ()
03581         elif self.dialog_mode == "device":
03582             self.cups._begin_operation (_("modifying printer %s") % name)
03583             try:
03584                 uri = self.getDeviceURI()
03585                 self.cups.addPrinter(name, device=uri)
03586             except cups.IPPError, (e, msg):
03587                 self.show_IPP_Error(e, msg)
03588                 self.cups._end_operation ()
03589                 return
03590             self.cups._end_operation ()
03591         elif self.dialog_mode == "ppd":
03592             if not ppd:
03593                 ppd = self.ppd = self.getNPPPD()
03594                 if not ppd:
03595                     # Go back to previous page to re-select driver.
03596                     self.nextNPTab(-1)
03597                     return
03598 
03599             self.cups._begin_operation (_("modifying printer %s") % name)
03600             # set ppd on server and retrieve it
03601             # cups doesn't offer a way to just download a ppd ;(=
03602             raw = False
03603             if isinstance(ppd, str) or isinstance(ppd, unicode):
03604                 if self.rbtnChangePPDasIs.get_active():
03605                     # To use the PPD as-is we need to prevent CUPS copying
03606                     # the old options over.  Do this by setting it to a
03607                     # raw queue (no PPD) first.
03608                     try:
03609                         self.cups.addPrinter(name, ppdname='raw')
03610                     except cups.IPPError, (e, msg):
03611                         self.show_IPP_Error(e, msg)
03612                 try:
03613                     self.cups.addPrinter(name, ppdname=ppd)
03614                 except cups.IPPError, (e, msg):
03615                     self.show_IPP_Error(e, msg)
03616                     self.cups._end_operation ()
03617                     return
03618 
03619                 try:
03620                     filename = self.cups.getPPD(name)
03621                     ppd = cups.PPD(filename)
03622                     os.unlink(filename)
03623                 except cups.IPPError, (e, msg):
03624                     if e == cups.IPP_NOT_FOUND:
03625                         raw = True
03626                     else:
03627                         self.show_IPP_Error(e, msg)
03628                         self.cups._end_operation ()
03629                         return
03630             else:
03631                 # We have an actual PPD to upload, not just a name.
03632                 if ((not self.rbtnChangePPDasIs.get_active()) and
03633                     isinstance (self.orig_ppd, cups.PPD)):
03634                     cupshelpers.copyPPDOptions(self.orig_ppd, ppd)
03635                 else:
03636                     cupshelpers.setPPDPageSize(ppd, self.language[0])
03637 
03638                 # write Installable Options to ppd
03639                 for option in self.options.itervalues():
03640                     option.writeback()
03641 
03642                 try:
03643                     self.cups.addPrinter(name, ppd=ppd)
03644                 except cups.IPPError, (e, msg):
03645                     self.show_IPP_Error(e, msg)
03646                     self.cups._end_operation ()
03647                     return
03648 
03649             self.cups._end_operation ()
03650 
03651             if not raw:
03652                 check = True
03653                 checkppd = ppd
03654 
03655         self.NewPrinterWindow.hide()
03656         if self.dialog_mode in ["printer", "printer_with_uri", "class"]:
03657             self.emit ('printer-added', name)
03658         else:
03659             self.emit ('printer-modified', name, self.orig_ppd != self.ppd)
03660 
03661         self.device = None
03662         self.printers = {}
03663 
03664 def show_help():
03665     print ("\nThis is the test/debug mode of the new-printer dialog of " \
03666            "system-config-printer.\n\n"
03667            "Options:\n\n"
03668            "  --setup-printer URI\n"
03669            "            Select the (detected) CUPS device URI on start up\n"
03670            "            and run the new-printer wizard for it.\n\n"
03671            "  --devid   Supply a device ID which should be used for the\n"
03672            "            setup of the new printer with \"--setup-printer\".\n"
03673            "            This can be any printer's ID, so that driver \n"
03674            "            selection can be tested for printers which are not\n"
03675            "            physically available.\n")
03676 
03677 gobject.type_register (NewPrinterGUI)
03678 
03679 if __name__ == '__main__':
03680     import getopt
03681     try:
03682         opts, args = getopt.gnu_getopt (sys.argv[1:], '',
03683                                         ['setup-printer=',
03684                                          'devid='])
03685     except getopt.GetoptError:
03686         show_help ()
03687         sys.exit (1)
03688 
03689     setup_printer = None
03690     devid = ""
03691     for opt, optarg in opts:
03692         if opt == '--setup-printer':
03693             setup_printer = optarg
03694         elif opt == '--devid':
03695             devid = optarg
03696 
03697     os.environ["SYSTEM_CONFIG_PRINTER_UI"] = "ui"
03698     import ppdippstr
03699     import locale
03700     locale.setlocale (locale.LC_ALL, "")
03701     ppdippstr.init ()
03702     gobject.threads_init ()
03703     set_debugging (True)
03704 
03705     n = NewPrinterGUI ()
03706     def on_signal (*args):
03707         gtk.main_quit ()
03708 
03709     n.connect ("printer-added", on_signal)
03710     n.connect ("printer-modified", on_signal)
03711     n.connect ("dialog-canceled", on_signal)
03712     if setup_printer != None:
03713         n.init ("printer_with_uri", device_uri=setup_printer, devid=devid)
03714     else:
03715         n.init ("printer")
03716     gtk.main ()