Back to index

system-config-printer  1.3.9+20120706
system-config-printer.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 sys, os, time, re
00028 import thread
00029 import dbus
00030 try:
00031     import gtk
00032 except RuntimeError, e:
00033     print "system-config-printer:", e
00034     print "This is a graphical application and requires DISPLAY to be set."
00035     sys.exit (1)
00036 
00037 def show_uri (uri):
00038     gtk.show_uri (gtk.gdk.screen_get_default (),
00039                   uri,
00040                   gtk.get_current_event_time ())
00041 
00042 gtk.about_dialog_set_url_hook (lambda x, y: show_uri (y))
00043 gtk.about_dialog_set_email_hook (lambda x, y: show_uri ("mailto:" + y))
00044 
00045 def show_help():
00046     print ("\nThis is system-config-printer, " \
00047            "a CUPS server configuration program.\n\n"
00048            "Options:\n\n"
00049            "  --debug   Enable debugging output.\n")
00050 
00051 if len(sys.argv)>1 and sys.argv[1] == '--help':
00052     show_help ()
00053     sys.exit (0)
00054 
00055 import cups
00056 cups.require ("1.9.46")
00057 cups.ppdSetConformance (cups.PPD_CONFORM_RELAXED)
00058 
00059 import locale
00060 try:
00061     locale.setlocale (locale.LC_ALL, "")
00062 except locale.Error:
00063     os.environ['LC_ALL'] = 'C'
00064     locale.setlocale (locale.LC_ALL, "")
00065 import gettext
00066 from gettext import gettext as _
00067 gettext.textdomain (config.PACKAGE)
00068 gettext.bindtextdomain (config.PACKAGE, config.localedir)
00069 
00070 import cupshelpers
00071 import gobject # for TYPE_STRING and TYPE_PYOBJECT
00072 from gui import GtkGUI
00073 from debug import *
00074 import gtk_label_autowrap
00075 import urllib
00076 import troubleshoot
00077 import installpackage
00078 import jobviewer
00079 import authconn
00080 import monitor
00081 import errordialogs
00082 from errordialogs import *
00083 import userdefault
00084 from serversettings import ServerSettings
00085 from ToolbarSearchEntry import *
00086 from SearchCriterion import *
00087 import statereason
00088 import firewall
00089 import newprinter
00090 from newprinter import busy, ready
00091 import printerproperties
00092 
00093 import ppdippstr
00094 ppdippstr.init ()
00095 pkgdata = config.pkgdatadir
00096 iconpath = os.path.join (pkgdata, 'icons/')
00097 sys.path.append (pkgdata)
00098 
00099 def CUPS_server_hostname ():
00100     host = cups.getServer ()
00101     if host[0] == '/':
00102         return 'localhost'
00103     return host
00104 
00105 class ServiceStart:
00106     NAME="org.fedoraproject.Config.Services"
00107     PATH="/org/fedoraproject/Config/Services/ServiceHerders/SysVServiceHerder/Services/cups"
00108     IFACE="org.fedoraproject.Config.Services.SysVService"
00109     def _get_iface (self, iface):
00110         bus = dbus.SystemBus ()
00111         obj = bus.get_object (self.NAME, self.PATH)
00112         proxy = dbus.Interface (obj, iface)
00113         return proxy
00114 
00115     def can_start (self):
00116         try:
00117             proxy = self._get_iface (dbus.INTROSPECTABLE_IFACE)
00118             introspect = proxy.Introspect ()
00119         except:
00120             return False
00121 
00122         return True
00123 
00124     def start (self, reply_handler, error_handler):
00125         proxy = self._get_iface (self.IFACE)
00126         proxy.start (reply_handler=reply_handler,
00127                      error_handler=error_handler)
00128 
00129 class GUI(GtkGUI):
00130 
00131     printer_states = { cups.IPP_PRINTER_IDLE: _("Idle"),
00132                        cups.IPP_PRINTER_PROCESSING: _("Processing"),
00133                        cups.IPP_PRINTER_BUSY: _("Busy"),
00134                        cups.IPP_PRINTER_STOPPED: _("Stopped") }
00135 
00136     DESTS_PAGE_DESTS=0
00137     DESTS_PAGE_NO_PRINTERS=1
00138     DESTS_PAGE_NO_SERVICE=2
00139 
00140     def __init__(self):
00141 
00142         try:
00143             self.language = locale.getlocale(locale.LC_MESSAGES)
00144             self.encoding = locale.getlocale(locale.LC_CTYPE)
00145         except:
00146             nonfatalException()
00147             os.environ['LC_ALL'] = 'C'
00148             locale.setlocale (locale.LC_ALL, "")
00149             self.language = locale.getlocale(locale.LC_MESSAGES)
00150             self.encoding = locale.getlocale(locale.LC_CTYPE)
00151 
00152         self.printers = {}
00153         self.connect_server = cups.getServer()
00154         self.connect_encrypt = cups.getEncryption ()
00155         self.connect_user = cups.getUser()
00156         self.monitor = None
00157         self.populateList_timer = None
00158 
00159         self.servers = set((self.connect_server,))
00160         self.server_is_publishing = None # not known
00161         self.changed = set() # of options
00162 
00163         # WIDGETS
00164         # =======
00165         self.updating_widgets = False
00166         self.getWidgets({"PrintersWindow":
00167                              ["PrintersWindow",
00168                               "view_area_vbox",
00169                               "view_area_scrolledwindow",
00170                               "dests_notebook",
00171                               "dests_iconview",
00172                               "btnAddFirstPrinter",
00173                               "btnStartService",
00174                               "btnConnectNoService",
00175                               "statusbarMain",
00176                               "toolbar",
00177                               "server_menubar_item",
00178                               "printer_menubar_item",
00179                               "view_discovered_printers"],
00180                          "AboutDialog":
00181                              ["AboutDialog"],
00182                          "ConnectDialog":
00183                              ["ConnectDialog",
00184                               "chkEncrypted",
00185                               "cmbServername",
00186                               "btnConnect"],
00187                          "ConnectingDialog":
00188                              ["ConnectingDialog",
00189                               "lblConnecting",
00190                               "pbarConnecting"],
00191                          "NewPrinterName":
00192                              ["NewPrinterName",
00193                               "entDuplicateName",
00194                               "btnDuplicateOk"],
00195                          "InstallDialog":
00196                              ["InstallDialog",
00197                               "lblInstall"]},
00198 
00199                         domain=config.PACKAGE)
00200 
00201 
00202         # Since some dialogs are reused we can't let the delete-event's
00203         # default handler destroy them
00204         self.ConnectingDialog.connect ("delete-event",
00205                                        self.on_connectingdialog_delete)
00206 
00207         gtk.window_set_default_icon_name ('printer')
00208 
00209         # Printer Actions
00210         printer_manager_action_group = \
00211             gtk.ActionGroup ("PrinterManagerActionGroup")
00212         printer_manager_action_group.add_actions ([
00213                 ("connect-to-server", gtk.STOCK_CONNECT, _("_Connect..."),
00214                  None, _("Choose a different CUPS server"),
00215                  self.on_connect_activate),
00216                 ("server-settings", gtk.STOCK_PREFERENCES, _("_Settings..."),
00217                  None, _("Adjust server settings"),
00218                  self.on_server_settings_activate),
00219                 ("new-printer", gtk.STOCK_PRINT, _("_Printer"),
00220                  None, None, self.on_new_printer_activate),
00221                 ("new-class", gtk.STOCK_DND_MULTIPLE, _("_Class"),
00222                  None, None, self.on_new_class_activate),
00223                 ("quit", gtk.STOCK_QUIT, None, None, None,
00224                  self.on_quit_activate)])
00225         printer_manager_action_group.add_actions ([
00226                 ("rename-printer", None, _("_Rename"),
00227                  None, None, self.on_rename_activate),
00228                 ("duplicate-printer", gtk.STOCK_COPY, _("_Duplicate"),
00229                  "<Ctrl>d", None, self.on_duplicate_activate),
00230                 ("delete-printer", gtk.STOCK_DELETE, None,
00231                  None, None, self.on_delete_activate),
00232                 ("set-default-printer", gtk.STOCK_HOME, _("Set As De_fault"),
00233                  None, None, self.on_set_as_default_activate),
00234                 ("edit-printer", gtk.STOCK_PROPERTIES, None,
00235                  None, None, self.on_edit_activate),
00236                 ("create-class", gtk.STOCK_DND_MULTIPLE, _("_Create class"),
00237                  None, None, self.on_create_class_activate),
00238                 ("view-print-queue", gtk.STOCK_FIND, _("View Print _Queue"),
00239                  None, None, self.on_view_print_queue_activate),
00240                 ])
00241         printer_manager_action_group.add_toggle_actions ([
00242                 ("enable-printer", None, _("E_nabled"),
00243                  None, None, self.on_enabled_activate),
00244                 ("share-printer", None, _("_Shared"),
00245                  None, None, self.on_shared_activate),
00246                 ])
00247         printer_manager_action_group.add_radio_actions ([
00248                 ("filter-name", None, _("Name")),
00249                 ("filter-description", None, _("Description")),
00250                 ("filter-location", None, _("Location")),
00251                 ("filter-manufacturer", None, _("Manufacturer / Model")),
00252                 ], 1, self.on_filter_criterion_changed)
00253         for action in printer_manager_action_group.list_actions ():
00254             action.set_sensitive (False)
00255         for action in ["connect-to-server",
00256                        "quit",
00257                        "view-print-queue",
00258                        "filter-name",
00259                        "filter-description",
00260                        "filter-location",
00261                        "filter-manufacturer"]:
00262             act = printer_manager_action_group.get_action (action)
00263             act.set_sensitive (True)
00264 
00265         self.ui_manager = gtk.UIManager ()
00266         self.ui_manager.insert_action_group (printer_manager_action_group, -1)
00267         self.ui_manager.add_ui_from_string (
00268 """
00269 <ui>
00270  <accelerator action="connect-to-server"/>
00271  <accelerator action="server-settings"/>
00272  <accelerator action="new-printer"/>
00273  <accelerator action="new-class"/>
00274  <accelerator action="quit"/>
00275 
00276  <accelerator action="rename-printer"/>
00277  <accelerator action="duplicate-printer"/>
00278  <accelerator action="delete-printer"/>
00279  <accelerator action="set-default-printer"/>
00280  <accelerator action="edit-printer"/>
00281  <accelerator action="create-class"/>
00282  <accelerator action="view-print-queue"/>
00283  <accelerator action="enable-printer"/>
00284  <accelerator action="share-printer"/>
00285  <accelerator action="filter-name"/>
00286  <accelerator action="filter-description"/>
00287  <accelerator action="filter-location"/>
00288  <accelerator action="filter-manufacturer"/>
00289 </ui>
00290 """
00291 )
00292         self.ui_manager.ensure_update ()
00293         self.PrintersWindow.add_accel_group (self.ui_manager.get_accel_group ())
00294 
00295         # Toolbar
00296         # Glade-2 doesn't have support for MenuToolButton, so we do that here.
00297         self.btnNew = gtk.MenuToolButton (gtk.STOCK_ADD)
00298         self.btnNew.set_is_important (True)
00299         newmenu = gtk.Menu ()
00300         action = self.ui_manager.get_action ("/new-printer")
00301         newprinteritem = action.create_menu_item ()
00302         action = self.ui_manager.get_action ("/new-class")
00303         newclassitem = action.create_menu_item ()
00304         newprinteritem.show ()
00305         newclassitem.show ()
00306         newmenu.attach (newprinteritem, 0, 1, 0, 1)
00307         newmenu.attach (newclassitem, 0, 1, 1, 2)
00308         self.btnNew.set_menu (newmenu)
00309         self.btnNew.connect ('clicked', self.on_new_printer_activate)
00310         self.toolbar.add (self.btnNew)
00311         self.toolbar.add (gtk.SeparatorToolItem ())
00312         refreshbutton = gtk.ToolButton (gtk.STOCK_REFRESH)
00313         refreshbutton.connect ('clicked', self.on_btnRefresh_clicked)
00314         self.toolbar.add (refreshbutton)
00315         self.toolbar.show_all ()
00316 
00317         server_context_menu = gtk.Menu ()
00318         for action_name in ["connect-to-server",
00319                             "server-settings",
00320                             None,
00321                             "new",
00322                             None,
00323                             "quit"]:
00324             if action_name == "new":
00325                 item = gtk.MenuItem (_("_New"))
00326                 item.set_sensitive (True)
00327                 self.menuItemNew = item
00328             elif not action_name:
00329                 item = gtk.SeparatorMenuItem ()
00330             else:
00331                 action = printer_manager_action_group.get_action (action_name)
00332                 item = action.create_menu_item ()
00333             item.show ()
00334             server_context_menu.append (item)
00335         self.server_menubar_item.set_submenu (server_context_menu)
00336 
00337         new_menu = gtk.Menu ()
00338         for action_name in ["new-printer",
00339                             "new-class"]:
00340             action = printer_manager_action_group.get_action (action_name)
00341             item = action.create_menu_item ()
00342             item.show ()
00343             new_menu.append (item)
00344         self.menuItemNew.set_submenu (new_menu)
00345 
00346         self.printer_context_menu = gtk.Menu ()
00347         for action_name in ["edit-printer",
00348                             "duplicate-printer",
00349                             "rename-printer",
00350                             "delete-printer",
00351                             None,
00352                             "enable-printer",
00353                             "share-printer",
00354                             "create-class",
00355                             "set-default-printer",
00356                             None,
00357                             "view-print-queue"]:
00358             if not action_name:
00359                 item = gtk.SeparatorMenuItem ()
00360             else:
00361                 action = printer_manager_action_group.get_action (action_name)
00362                 item = action.create_menu_item ()
00363             item.show ()
00364             self.printer_context_menu.append (item)
00365         self.printer_menubar_item.set_submenu (self.printer_context_menu)
00366 
00367         self.jobviewers = [] # to keep track of jobviewer windows
00368 
00369         # New Printer Dialog
00370         self.newPrinterGUI = np = newprinter.NewPrinterGUI()
00371         np.connect ("printer-added", self.on_new_printer_added)
00372         np.connect ("printer-modified", self.on_printer_modified)
00373 
00374         # Set up "About" dialog
00375         self.AboutDialog.set_program_name(config.PACKAGE)
00376         self.AboutDialog.set_version(config.VERSION)
00377         self.AboutDialog.set_icon_name('printer')
00378 
00379         try:
00380             slip.gtk.label_set_autowrap(self.PrintersWindow)
00381         except: # no slip.gtk module
00382             gtk_label_autowrap.set_autowrap(self.PrintersWindow)
00383 
00384         try:
00385             self.cups = authconn.Connection(self.PrintersWindow)
00386         except RuntimeError:
00387             self.cups = None
00388 
00389         self.status_context_id = self.statusbarMain.get_context_id(
00390             "Connection")
00391 
00392         # Setup search
00393         self.setup_toolbar_for_search_entry ()
00394         self.current_filter_text = ""
00395         self.current_filter_mode = "filter-name"
00396 
00397         # Search entry drop down menu
00398         menu = gtk.Menu ()
00399         for action_name in ["filter-name",
00400                             "filter-description",
00401                             "filter-location",
00402                             "filter-manufacturer"]:
00403             action = printer_manager_action_group.get_action (action_name)
00404             item = action.create_menu_item ()
00405             menu.append (item)
00406         menu.show_all ()
00407         self.search_entry.set_drop_down_menu (menu)
00408 
00409         self.servicestart = ServiceStart ()
00410 
00411         # Setup icon view
00412         self.mainlist = gtk.ListStore(gobject.TYPE_PYOBJECT, # Object
00413                                       gtk.gdk.Pixbuf,        # Pixbuf
00414                                       gobject.TYPE_STRING,   # Name
00415                                       gobject.TYPE_STRING)   # Tooltip
00416 
00417         self.dests_iconview.set_model(self.mainlist)
00418         self.dests_iconview.set_column_spacing (30)
00419         self.dests_iconview.set_row_spacing (20)
00420         self.dests_iconview.set_pixbuf_column (1)
00421         self.dests_iconview.set_text_column (2)
00422         self.dests_iconview.set_tooltip_column (3)
00423         self.dests_iconview.set_has_tooltip(True)
00424         self.dests_iconview.connect ('key-press-event',
00425                                      self.dests_iconview_key_press_event)
00426         self.dests_iconview.connect ('item-activated',
00427                                      self.dests_iconview_item_activated)
00428         self.dests_iconview.connect ('selection-changed',
00429                                      self.dests_iconview_selection_changed)
00430         self.dests_iconview.connect ('button-press-event',
00431                                      self.dests_iconview_button_press_event)
00432         self.dests_iconview.connect ('popup-menu',
00433                                      self.dests_iconview_popup_menu)
00434         self.dests_iconview_selection_changed (self.dests_iconview)
00435         self.dests_iconview.enable_model_drag_source (gtk.gdk.BUTTON1_MASK,
00436                                                       # should use a variable
00437                                                       # instead of 0
00438                                                       [("queue", 0, 0)],
00439                                                       gtk.gdk.ACTION_COPY)
00440         self.dests_iconview.connect ("drag-data-get",
00441                                      self.dests_iconview_drag_data_get)
00442         self.btnStartService.connect ('clicked', self.on_start_service_clicked)
00443         self.btnConnectNoService.connect ('clicked', self.on_connect_activate)
00444         self.btnAddFirstPrinter.connect ('clicked',
00445                                          self.on_new_printer_activate)
00446 
00447         # Printer Properties dialog
00448         self.propertiesDlg = printerproperties.PrinterPropertiesDialog ()
00449 
00450         try:
00451             self.populateList()
00452         except cups.HTTPError, (s,):
00453             self.cups = None
00454             self.populateList()
00455             show_HTTP_Error(s, self.PrintersWindow)
00456 
00457         self.setConnected()
00458 
00459         if len (self.printers) > 3:
00460             self.PrintersWindow.set_default_size (550, 400)
00461 
00462         self.PrintersWindow.show()
00463 
00464     def display_properties_dialog_for (self, queue):
00465         model = self.dests_iconview.get_model ()
00466         iter = model.get_iter_first ()
00467         while iter != None:
00468             name = unicode (model.get_value (iter, 2))
00469             if name == queue:
00470                 path = model.get_path (iter)
00471                 self.dests_iconview.scroll_to_path (path, True, 0.5, 0.5)
00472                 self.dests_iconview.set_cursor (path)
00473                 self.dests_iconview.item_activated (path)
00474                 break
00475             iter = model.iter_next (iter)
00476 
00477         if iter == None:
00478             raise RuntimeError
00479 
00480     def setup_toolbar_for_search_entry (self):
00481         separator = gtk.SeparatorToolItem ()
00482         separator.set_draw (False)
00483 
00484         self.toolbar.insert (separator, -1)
00485         self.toolbar.child_set_property (separator, "expand", True)
00486 
00487         self.search_entry = ToolbarSearchEntry ()
00488         self.search_entry.connect ('search', self.on_search_entry_search)
00489 
00490         tool_item = gtk.ToolItem ()
00491         tool_item.add (self.search_entry)
00492         self.toolbar.insert (tool_item, -1)
00493         self.toolbar.show_all ()
00494 
00495     def on_search_entry_search (self, UNUSED, text):
00496         self.current_filter_text = text
00497         self.populateList ()
00498 
00499     def on_filter_criterion_changed (self, UNUSED, selected_action):
00500         self.current_filter_mode = selected_action.get_name ()
00501         self.populateList ()
00502 
00503     def dests_iconview_item_activated (self, iconview, path):
00504         model = iconview.get_model ()
00505         iter = model.get_iter (path)
00506         name = unicode (model.get_value (iter, 2))
00507         object = model.get_value (iter, 0)
00508 
00509         try:
00510             self.propertiesDlg.show (name, host=self.connect_server,
00511                                      encryption=self.connect_encrypt,
00512                                      parent=self.PrintersWindow)
00513         except cups.IPPError, (e, m):
00514             show_IPP_Error (e, m, self.PrintersWindow)
00515             if e == cups.IPP_SERVICE_UNAVAILABLE:
00516                 self.cups = None
00517                 self.setConnected ()
00518                 self.populateList ()
00519             return
00520         except RuntimeError:
00521             # Perhaps cupsGetPPD2 failed for a browsed printer.
00522 
00523             # Check that we're still connected.
00524             self.monitor.update ()
00525             return
00526 
00527     def dests_iconview_selection_changed (self, iconview):
00528         self.updating_widgets = True
00529         paths = iconview.get_selected_items ()
00530         any_disabled = False
00531         any_enabled = False
00532         any_discovered = False
00533         any_shared = False
00534         any_unshared = False
00535         model = iconview.get_model ()
00536         for path in paths:
00537             iter = model.get_iter (path)
00538             object = model.get_value (iter, 0)
00539             name = unicode (model.get_value (iter, 2))
00540             if object.discovered:
00541                 any_discovered = True
00542             if object.enabled:
00543                 any_enabled = True
00544             else:
00545                 any_disabled = True
00546             if object.is_shared:
00547                 any_shared = True
00548             else:
00549                 any_unshared = True
00550 
00551         n = len (paths)
00552         self.ui_manager.get_action ("/edit-printer").set_sensitive (n == 1)
00553 
00554         self.ui_manager.get_action ("/duplicate-printer").set_sensitive (n == 1)
00555 
00556         self.ui_manager.get_action ("/rename-printer").set_sensitive (
00557             n == 1 and not any_discovered)
00558 
00559         userdef = userdefault.UserDefaultPrinter ().get ()
00560         if (n != 1 or
00561             (userdef == None and self.default_printer == name)):
00562             set_default_sensitivity = False
00563         else:
00564             set_default_sensitivity = True
00565 
00566         self.ui_manager.get_action ("/set-default-printer").set_sensitive (
00567             set_default_sensitivity)
00568 
00569         action = self.ui_manager.get_action ("/enable-printer")
00570         action.set_sensitive (n > 0 and not any_discovered)
00571         for widget in action.get_proxies ():
00572             if isinstance (widget, gtk.CheckMenuItem):
00573                 widget.set_inconsistent (n > 1 and any_enabled and any_disabled)
00574         action.set_active (any_discovered or not any_disabled)
00575 
00576         action = self.ui_manager.get_action ("/share-printer")
00577         action.set_sensitive (n > 0 and not any_discovered)
00578         for widget in action.get_proxies ():
00579             if isinstance (widget, gtk.CheckMenuItem):
00580                 widget.set_inconsistent (n > 1 and any_shared and any_unshared)
00581         action.set_active (any_discovered or not any_unshared)
00582 
00583         self.ui_manager.get_action ("/delete-printer").set_sensitive (
00584             n > 0 and not any_discovered)
00585 
00586         self.ui_manager.get_action ("/create-class").set_sensitive (n > 1)
00587 
00588         self.updating_widgets = False
00589 
00590     def dests_iconview_popup_menu (self, iconview):
00591         self.printer_context_menu.popup (None, None, None, 0, 0L)
00592 
00593     def dests_iconview_button_press_event (self, iconview, event):
00594         if event.button > 1:
00595             click_path = iconview.get_path_at_pos (int (event.x),
00596                                                    int (event.y))
00597             paths = iconview.get_selected_items ()
00598             if click_path == None:
00599                 iconview.unselect_all ()
00600             elif click_path not in paths:
00601                 iconview.unselect_all ()
00602                 iconview.select_path (click_path)
00603                 cells = iconview.get_cells ()
00604                 for cell in cells:
00605                     if type (cell) == gtk.CellRendererText:
00606                         break
00607                 iconview.set_cursor (click_path, cell)
00608             self.printer_context_menu.popup (None, None, None,
00609                                              event.button, event.time)
00610         return False
00611 
00612     def dests_iconview_key_press_event (self, iconview, event):
00613         modifiers = gtk.accelerator_get_default_mod_mask ()
00614 
00615         if ((event.keyval == gtk.keysyms.BackSpace or
00616              event.keyval == gtk.keysyms.Delete or
00617              event.keyval == gtk.keysyms.KP_Delete) and
00618             ((event.state & modifiers) == 0)):
00619 
00620             self.ui_manager.get_action ("/delete-printer").activate ()
00621             return True
00622 
00623         if ((event.keyval == gtk.keysyms.F2) and
00624             ((event.state & modifiers) == 0)):
00625 
00626             self.ui_manager.get_action ("/rename-printer").activate ()
00627             return True
00628 
00629         return False
00630 
00631     def dests_iconview_drag_data_get (self, iconview, context,
00632                                       selection_data, info, timestamp):
00633         if info == 0: # FIXME: should use an "enum" here
00634             model = iconview.get_model ()
00635             paths = iconview.get_selected_items ()
00636             selected_printer_names = ""
00637             for path in paths:
00638                 selected_printer_names += \
00639                     model.get_value (model.get_iter (path), 2) + "\n"
00640 
00641             if len (selected_printer_names) > 0:
00642                 selection_data.set ("queue", 8, selected_printer_names)
00643         else:
00644             nonfatalException ()
00645 
00646     def on_server_settings_activate (self, menuitem):
00647         try:
00648             self.serverSettings = ServerSettings (host=self.connect_server,
00649                                                   encryption=self.connect_encrypt,
00650                                                   parent=self.PrintersWindow)
00651             self.serverSettings.connect ('problems-clicked',
00652                                          self.on_problems_button_clicked)
00653         except (cups.IPPError, cups.HTTPError):
00654             # Not authorized.
00655             return
00656         except RuntimeError:
00657             self.monitor.update ()
00658 
00659     def setConnected(self):
00660         connected = bool(self.cups)
00661 
00662         host = CUPS_server_hostname ()
00663         self.PrintersWindow.set_title(_("Printing - %s") % host)
00664 
00665         if connected:
00666             status_msg = _("Connected to %s") % host
00667         else:
00668             status_msg = _("Not connected")
00669         self.statusbarMain.push(self.status_context_id, status_msg)
00670 
00671         for widget in (self.btnNew,
00672                        self.menuItemNew):
00673             widget.set_sensitive(connected)
00674 
00675         for action_name in ["/server-settings",
00676                             "/new-printer",
00677                             "/new-class"]:
00678             action = self.ui_manager.get_action (action_name)
00679             action.set_sensitive (connected)
00680 
00681         if connected:
00682             if self.monitor:
00683                 self.monitor.cleanup ()
00684 
00685             self.monitor = monitor.Monitor (monitor_jobs=False,
00686                                             host=self.connect_server,
00687                                             encryption=self.connect_encrypt)
00688             self.monitor.connect ('printer-added', self.printer_added)
00689             self.monitor.connect ('printer-event', self.printer_event)
00690             self.monitor.connect ('printer-removed', self.printer_removed)
00691             self.monitor.connect ('cups-connection-error',
00692                                   self.cups_connection_error)
00693             self.monitor.connect ('cups-connection-recovered',
00694                                   self.cups_connection_recovered)
00695             self.monitor.refresh ()
00696             self.propertiesDlg.set_monitor (self.monitor)
00697 
00698     def getServers(self):
00699         self.servers.discard(None)
00700         known_servers = list(self.servers)
00701         known_servers.sort()
00702         return known_servers
00703 
00704     def populateList(self, prompt_allowed=True):
00705         # Save selection of printers.
00706         selected_printers = set()
00707         paths = self.dests_iconview.get_selected_items ()
00708         model = self.dests_iconview.get_model ()
00709         for path in paths:
00710             iter = model.get_iter (path)
00711             name = unicode (model.get_value (iter, 2))
00712             selected_printers.add (name)
00713 
00714         if self.cups:
00715             kill_connection = False
00716             self.cups._set_prompt_allowed (prompt_allowed)
00717             self.cups._begin_operation (_("obtaining queue details"))
00718             try:
00719                 # get Printers
00720                 self.printers = cupshelpers.getPrinters(self.cups)
00721 
00722                 # Get default printer.
00723                 self.default_printer = self.cups.getDefault ()
00724             except cups.IPPError, (e, m):
00725                 show_IPP_Error(e, m, self.PrintersWindow)
00726                 self.printers = {}
00727                 self.default_printer = None
00728                 if e == cups.IPP_SERVICE_UNAVAILABLE:
00729                     kill_connection = True
00730 
00731             self.cups._end_operation ()
00732             self.cups._set_prompt_allowed (True)
00733             if kill_connection:
00734                 self.cups = None
00735         else:
00736             self.printers = {}
00737             self.default_printer = None
00738 
00739         for name, printer in self.printers.iteritems():
00740             self.servers.add(printer.getServer())
00741 
00742         userdef = userdefault.UserDefaultPrinter ().get ()
00743 
00744         local_printers = []
00745         local_classes = []
00746         remote_printers = []
00747         remote_classes = []
00748 
00749         delete_action = self.ui_manager.get_action ("/delete-printer")
00750         delete_action.set_properties (label = None)
00751         printers_set = self.printers
00752 
00753         # Filter printers
00754         if len (self.current_filter_text) > 0:
00755             printers_subset = {}
00756             pattern = re.compile (self.current_filter_text, re.I) # ignore case
00757 
00758             if self.current_filter_mode == "filter-name":
00759                 for name in printers_set.keys ():
00760                     if pattern.search (name) != None:
00761                         printers_subset[name] = printers_set[name]
00762             elif self.current_filter_mode == "filter-description":
00763                 for name, printer in printers_set.iteritems ():
00764                     if pattern.search (printer.info) != None:
00765                         printers_subset[name] = printers_set[name]
00766             elif self.current_filter_mode == "filter-location":
00767                 for name, printer in printers_set.iteritems ():
00768                     if pattern.search (printer.location) != None:
00769                         printers_subset[name] = printers_set[name]
00770             elif self.current_filter_mode == "filter-manufacturer":
00771                 for name, printer in printers_set.iteritems ():
00772                     if pattern.search (printer.make_and_model) != None:
00773                         printers_subset[name] = printers_set[name]
00774             else:
00775                 nonfatalException ()
00776 
00777             printers_set = printers_subset
00778 
00779         if not self.view_discovered_printers.get_active ():
00780             printers_subset = {}
00781             for name, printer in printers_set.iteritems ():
00782                 if not printer.discovered:
00783                     printers_subset[name] = printer
00784 
00785             printers_set = printers_subset
00786 
00787         for name, printer in printers_set.iteritems():
00788             if printer.remote:
00789                 if printer.is_class: remote_classes.append(name)
00790                 else: remote_printers.append(name)
00791             else:
00792                 if printer.is_class: local_classes.append(name)
00793                 else: local_printers.append(name)
00794 
00795         local_printers.sort()
00796         local_classes.sort()
00797         remote_printers.sort()
00798         remote_classes.sort()
00799 
00800         # remove old printers/classes
00801         self.mainlist.clear ()
00802 
00803         # add new
00804         PRINTER_TYPE = { 'discovered-printer':
00805                              (_("Network printer (discovered)"),
00806                               'i-network-printer'),
00807                          'discovered-class':
00808                              (_("Network class (discovered)"),
00809                               'i-network-printer'),
00810                          'local-printer':
00811                              (_("Printer"),
00812                               'printer'),
00813                          'local-fax':
00814                              (_("Fax"),
00815                               'printer'),
00816                          'local-class':
00817                              (_("Class"),
00818                               'printer'),
00819                          'ipp-printer':
00820                              (_("Network printer"),
00821                               'i-network-printer'),
00822                          'smb-printer':
00823                              (_("Network print share"),
00824                               'printer'),
00825                          'network-printer':
00826                              (_("Network printer"),
00827                               'i-network-printer'),
00828                          }
00829         theme = gtk.icon_theme_get_default ()
00830         for printers in (local_printers,
00831                          local_classes,
00832                          remote_printers,
00833                          remote_classes):
00834             if not printers: continue
00835             for name in printers:
00836                 type = 'local-printer'
00837                 object = printers_set[name]
00838                 if object.discovered:
00839                     if object.is_class:
00840                         type = 'discovered-class'
00841                     else:
00842                         type = 'discovered-printer'
00843                 elif object.is_class:
00844                     type = 'local-class'
00845                 else:
00846                     (scheme, rest) = urllib.splittype (object.device_uri)
00847                     if scheme == 'ipp':
00848                         type = 'ipp-printer'
00849                     elif scheme == 'smb':
00850                         type = 'smb-printer'
00851                     elif scheme == 'hpfax':
00852                         type = 'local-fax'
00853                     elif scheme in ['socket', 'lpd']:
00854                         type = 'network-printer'
00855 
00856                 (tip, icon) = PRINTER_TYPE[type]
00857                 (w, h) = gtk.icon_size_lookup (gtk.ICON_SIZE_DIALOG)
00858                 try:
00859                     pixbuf = theme.load_icon (icon, w, 0)
00860                 except gobject.GError:
00861                     # Not in theme.
00862                     pixbuf = None
00863                     for p in [iconpath, 'icons/']:
00864                         try:
00865                             pixbuf = gtk.gdk.pixbuf_new_from_file ("%s%s.png" %
00866                                                                    (p, icon))
00867                             break
00868                         except gobject.GError:
00869                             pass
00870 
00871                     if pixbuf == None:
00872                         try:
00873                             pixbuf = theme.load_icon ('printer', w, 0)
00874                         except:
00875                             # Just create an empty pixbuf.
00876                             pixbuf = gtk.gdk.Pixbuf (gtk.gdk.COLORSPACE_RGB,
00877                                                      True, 8, w, h)
00878                             pixbuf.fill (0)
00879 
00880                 def_emblem = None
00881                 emblem = None
00882                 if name == self.default_printer:
00883                     def_emblem = 'emblem-default'
00884                 elif name == userdef:
00885                     def_emblem = 'emblem-favorite'
00886 
00887                 if not emblem:
00888                     attrs = object.other_attributes
00889                     reasons = attrs.get ('printer-state-reasons', [])
00890                     worst_reason = None
00891                     for reason in reasons:
00892                         if reason == "none":
00893                             break
00894 
00895                         if reason == "paused":
00896                             emblem = gtk.STOCK_MEDIA_PAUSE
00897                             continue
00898 
00899                         r = statereason.StateReason (object.name, reason)
00900                         if worst_reason == None:
00901                             worst_reason = r
00902                         elif r > worst_reason:
00903                             worst_reason = r
00904 
00905                     if worst_reason:
00906                         level = worst_reason.get_level ()
00907                         emblem = worst_reason.LEVEL_ICON[level]
00908 
00909                 if not emblem and not object.enabled:
00910                     emblem = gtk.STOCK_MEDIA_PAUSE
00911 
00912                 if object.rejecting:
00913                     # Show the icon as insensitive
00914                     copy = pixbuf.copy ()
00915                     copy.fill (0)
00916                     pixbuf.composite (copy, 0, 0,
00917                                       copy.get_width(), copy.get_height(),
00918                                       0, 0, 1.0, 1.0,
00919                                       gtk.gdk.INTERP_BILINEAR, 127)
00920                     pixbuf = copy
00921 
00922                 if def_emblem:
00923                     (w, h) = gtk.icon_size_lookup (gtk.ICON_SIZE_DIALOG)
00924                     try:
00925                         default_emblem = theme.load_icon (def_emblem, w/2, 0)
00926                         copy = pixbuf.copy ()
00927                         default_emblem.composite (copy, 0, 0,
00928                                                   copy.get_width (),
00929                                                   copy.get_height (),
00930                                                   0, 0,
00931                                                   1.0, 1.0,
00932                                                   gtk.gdk.INTERP_NEAREST, 255)
00933                         pixbuf = copy
00934                     except gobject.GError:
00935                         debugprint ("No %s icon available" % def_emblem)
00936 
00937                 if emblem:
00938                     (w, h) = gtk.icon_size_lookup (gtk.ICON_SIZE_DIALOG)
00939                     try:
00940                         other_emblem = theme.load_icon (emblem, w/2, 0)
00941                         copy = pixbuf.copy ()
00942                         other_emblem.composite (copy, 0, 0,
00943                                                 copy.get_width (),
00944                                                 copy.get_height (),
00945                                                 copy.get_width () / 2,
00946                                                 copy.get_height () / 2,
00947                                                 1.0, 1.0,
00948                                                 gtk.gdk.INTERP_NEAREST, 255)
00949                         pixbuf = copy
00950                     except gobject.GError:
00951                         debugprint ("No %s icon available" % emblem)
00952 
00953                 self.mainlist.append (row=[object, pixbuf, name, tip])
00954 
00955         # Restore selection of printers.
00956         model = self.dests_iconview.get_model ()
00957         def maybe_select (model, path, iter):
00958             name = unicode (model.get_value (iter, 2))
00959             if name in selected_printers:
00960                 self.dests_iconview.select_path (path)
00961         model.foreach (maybe_select)
00962 
00963         # Set up the dests_notebook page.
00964         page = self.DESTS_PAGE_DESTS
00965         if self.cups:
00966             if (not self.current_filter_text and
00967                 not self.mainlist.get_iter_first ()):
00968                 page = self.DESTS_PAGE_NO_PRINTERS
00969         else:
00970             page = self.DESTS_PAGE_NO_SERVICE
00971             can_start = (self.connect_server == 'localhost' or
00972                          self.connect_server[0] != '/')
00973             tooltip_text = None
00974             if can_start:
00975                 can_start = self.servicestart.can_start ()
00976                 if not can_start:
00977                     tooltip_text = _("Service framework not available")
00978             else:
00979                 tooltip_text = _("Cannot start service on remote server")
00980 
00981             self.btnStartService.set_sensitive (can_start)
00982             self.btnStartService.set_tooltip_text (tooltip_text)
00983 
00984         self.dests_notebook.set_current_page (page)
00985 
00986     # Connect to Server
00987 
00988     def on_connect_servername_changed(self, widget):
00989         self.btnConnect.set_sensitive (len (widget.get_active_text ()) > 0)
00990 
00991     def on_connect_activate(self, widget):
00992         # Use browsed queues to build up a list of known IPP servers
00993         servers = self.getServers()
00994         current_server = (self.propertiesDlg.printer and
00995                           self.propertiesDlg.printer.getServer()) \
00996                           or cups.getServer()
00997 
00998         store = gtk.ListStore (gobject.TYPE_STRING)
00999         self.cmbServername.set_model(store)
01000         self.cmbServername.set_text_column (0)
01001         for server in servers:
01002             self.cmbServername.append_text(server)
01003         self.cmbServername.show()
01004 
01005         self.cmbServername.child.set_text (current_server)
01006         self.chkEncrypted.set_active (cups.getEncryption() ==
01007                                       cups.HTTP_ENCRYPT_ALWAYS)
01008 
01009         self.cmbServername.child.set_activates_default (True)
01010         self.cmbServername.grab_focus ()
01011         self.ConnectDialog.set_transient_for (self.PrintersWindow)
01012         response = self.ConnectDialog.run()
01013 
01014         self.ConnectDialog.hide()
01015 
01016         if response != gtk.RESPONSE_OK:
01017             return
01018 
01019         if self.chkEncrypted.get_active():
01020             cups.setEncryption(cups.HTTP_ENCRYPT_ALWAYS)
01021         else:
01022             cups.setEncryption(cups.HTTP_ENCRYPT_IF_REQUESTED)
01023         self.connect_encrypt = cups.getEncryption ()
01024 
01025         servername = self.cmbServername.child.get_text()
01026 
01027         self.lblConnecting.set_markup(_("<i>Opening connection to %s</i>") %
01028                                       servername)
01029         self.ConnectingDialog.set_transient_for(self.PrintersWindow)
01030         self.ConnectingDialog.show()
01031         gobject.timeout_add (40, self.update_connecting_pbar)
01032         self.connect_server = servername
01033         # We need to set the connecting user in this thread as well.
01034         cups.setServer(self.connect_server)
01035         cups.setUser('')
01036         self.connect_user = cups.getUser()
01037         # Now start a new thread for connection.
01038         self.connect_thread = thread.start_new_thread(self.connect,
01039                                                       (self.PrintersWindow,))
01040 
01041     def update_connecting_pbar (self):
01042         ret = True
01043         gtk.gdk.threads_enter ()
01044         if not self.ConnectingDialog.get_property ("visible"):
01045             ret = False # stop animation
01046         else:
01047             self.pbarConnecting.pulse ()
01048 
01049         gtk.gdk.threads_leave ()
01050         return ret
01051 
01052     def on_connectingdialog_delete (self, widget, event):
01053         self.on_cancel_connect_clicked (widget)
01054         return True
01055 
01056     def on_cancel_connect_clicked(self, widget):
01057         """
01058         Stop connection to new server
01059         (Doesn't really stop but sets flag for the connecting thread to
01060         ignore the connection)
01061         """
01062         self.connect_thread = None
01063         self.ConnectingDialog.hide()
01064 
01065     def connect(self, parent=None):
01066         """
01067         Open a connection to a new server. Is executed in a separate thread!
01068         """
01069         cups.setUser(self.connect_user)
01070         if self.connect_server[0] == '/':
01071             # UNIX domain socket.  This may potentially fail if the server
01072             # settings have been changed and cupsd has written out a
01073             # configuration that does not include a Listen line for the
01074             # UNIX domain socket.  To handle this special case, try to
01075             # connect once and fall back to "localhost" on failure.
01076             try:
01077                 connection = cups.Connection (host=self.connect_server,
01078                                               encryption=self.connect_encrypt)
01079 
01080                 # Worked fine.  Disconnect, and we'll connect for real
01081                 # shortly.
01082                 del connection
01083             except RuntimeError:
01084                 # When we connect, avoid the domain socket.
01085                 cups.setServer ("localhost")
01086             except:
01087                 nonfatalException ()
01088 
01089         try:
01090             connection = authconn.Connection(parent,
01091                                              host=self.connect_server,
01092                                              encryption=self.connect_encrypt)
01093         except RuntimeError, s:
01094             if self.connect_thread != thread.get_ident(): return
01095             gtk.gdk.threads_enter()
01096             self.ConnectingDialog.hide()
01097             self.cups = None
01098             self.setConnected()
01099             self.populateList()
01100             show_IPP_Error(None, s, parent)
01101             gtk.gdk.threads_leave()
01102             return
01103         except cups.IPPError, (e, s):
01104             if self.connect_thread != thread.get_ident(): return
01105             gtk.gdk.threads_enter()
01106             self.ConnectingDialog.hide()
01107             self.cups = None
01108             self.setConnected()
01109             self.populateList()
01110             show_IPP_Error(e, s, parent)
01111             gtk.gdk.threads_leave()
01112             return
01113         except:
01114             nonfatalException ()
01115 
01116         if self.connect_thread != thread.get_ident(): return
01117         gtk.gdk.threads_enter()
01118 
01119         try:
01120             self.ConnectingDialog.hide()
01121             self.cups = connection
01122             self.setConnected()
01123             self.populateList()
01124        except cups.HTTPError, (s,):
01125             self.cups = None
01126             self.setConnected()
01127             self.populateList()
01128             show_HTTP_Error(s, parent)
01129         except:
01130             nonfatalException ()
01131 
01132         gtk.gdk.threads_leave()
01133 
01134     def reconnect (self):
01135         """Reconnect to CUPS after the server has reloaded."""
01136         # libcups would handle the reconnection if we just told it to
01137         # do something, for example fetching a list of classes.
01138         # However, our local authentication certificate would be
01139         # invalidated by a server restart, so it is better for us to
01140         # handle the reconnection ourselves.
01141 
01142         attempt = 1
01143         while attempt <= 5:
01144             try:
01145                 time.sleep(1)
01146                 self.cups._connect ()
01147                 break
01148             except RuntimeError:
01149                 # Connection failed.
01150                 attempt += 1
01151 
01152     def on_btnCancelConnect_clicked(self, widget):
01153         """Close Connect dialog"""
01154         self.ConnectWindow.hide()
01155 
01156     # refresh
01157 
01158     def on_btnRefresh_clicked(self, button):
01159         if self.cups == None:
01160             try:
01161                 self.cups = authconn.Connection(self.PrintersWindow)
01162             except RuntimeError:
01163                 pass
01164 
01165             self.setConnected()
01166 
01167         self.populateList()
01168 
01169     # set default printer
01170     def set_system_or_user_default_printer (self, name):
01171         # First, decide if this is already the system default, in which
01172         # case we only need to clear the user default.
01173         userdef = userdefault.UserDefaultPrinter ()
01174         if name == self.default_printer:
01175             userdef.clear ()
01176             self.populateList ()
01177             return
01178 
01179         userdefault.UserDefaultPrompt (self.set_default_printer,
01180                                        self.populateList,
01181                                        name,
01182                                        _("Set Default Printer"),
01183                                        self.PrintersWindow,
01184                                        _("Do you want to set this as "
01185                                          "the system-wide default printer?"),
01186                                        _("Set as the _system-wide "
01187                                          "default printer"),
01188                                        _("_Clear my personal default setting"),
01189                                        _("Set as my _personal default printer"))
01190 
01191     def set_default_printer (self, name):
01192         printer = self.printers[name]
01193         reload = False
01194         self.cups._begin_operation (_("setting default printer"))
01195         try:
01196             reload = printer.setAsDefault ()
01197         except cups.HTTPError, (s,):
01198             show_HTTP_Error (s, self.PrintersWindow)
01199             self.cups._end_operation ()
01200             return
01201         except cups.IPPError, (e, msg):
01202             show_IPP_Error(e, msg, self.PrintersWindow)
01203             self.cups._end_operation ()
01204             return
01205 
01206         self.cups._end_operation ()
01207 
01208         # Now reconnect in case the server needed to reload.  This may
01209         # happen if we replaced the lpoptions file.
01210         if reload:
01211             self.reconnect ()
01212 
01213         try:
01214             self.populateList()
01215         except cups.HTTPError, (s,):
01216             self.cups = None
01217             self.setConnected()
01218             self.populateList()
01219             show_HTTP_Error(s, self.PrintersWindow)
01220 
01221     # Quit
01222 
01223     def on_quit_activate(self, widget, event=None):
01224         if self.monitor:
01225             self.monitor.cleanup ()
01226 
01227         while len (self.jobviewers) > 0:
01228             # this will call on_jobviewer_exit
01229             self.jobviewers[0].on_delete_event ()
01230         del self.mainlist
01231         del self.printers
01232         self.propertiesDlg.destroy ()
01233         self.newPrinterGUI.destroy ()
01234         gtk.main_quit()
01235 
01236     # Rename
01237     def is_rename_possible (self, name):
01238         jobs = self.printers[name].jobsQueued (limit=1)
01239         if len (jobs) > 0:
01240             show_error_dialog (_("Cannot Rename"),
01241                                _("There are queued jobs."),
01242                                parent=self.PrintersWindow)
01243             return False
01244 
01245         return True
01246 
01247     def rename_confirmed_by_user (self, name):
01248         """
01249         Renaming deletes job history. So if we have some completed jobs,
01250         inform the user and let him confirm the renaming.
01251         """
01252         preserved_jobs = self.printers[name].jobsPreserved(limit=1)
01253         if len (preserved_jobs) > 0:
01254             dialog = gtk.MessageDialog (self.PrintersWindow,
01255                                         gtk.DIALOG_MODAL |
01256                                         gtk.DIALOG_DESTROY_WITH_PARENT,
01257                                         gtk.MESSAGE_WARNING,
01258                                         gtk.BUTTONS_OK_CANCEL,
01259                                         _("Renaming will lose history"))
01260 
01261             dialog.format_secondary_text (_("Completed jobs will no longer "
01262                                             "be available for re-printing."))
01263             result = dialog.run()
01264             dialog.destroy ()
01265             if result == gtk.RESPONSE_CANCEL:
01266                 return False
01267 
01268         return True
01269 
01270     def on_rename_activate(self, UNUSED):
01271         tuple = self.dests_iconview.get_cursor ()
01272         if tuple == None:
01273             return
01274 
01275         (path, cell) = tuple
01276         if type (cell) != gtk.CellRendererText:
01277             cells = self.dests_iconview.get_cells ()
01278             for cell in cells:
01279                 if type (cell) == gtk.CellRendererText:
01280                     break
01281             if type (cell) != gtk.CellRendererText:
01282                 return
01283 
01284         model = self.dests_iconview.get_model ()
01285         iter = model.get_iter (path)
01286         name = unicode (model.get_value (iter, 2))
01287         if not self.is_rename_possible (name):
01288             return
01289         if not self.rename_confirmed_by_user (name):
01290             return
01291         cell.set_property ('editable', True)
01292         ids = []
01293         ids.append (cell.connect ('editing-started',
01294                                  self.printer_name_edit_start))
01295         ids.append (cell.connect ('edited', self.printer_name_edited))
01296         ids.append (cell.connect ('editing-canceled',
01297                                  self.printer_name_edit_cancel))
01298         self.rename_sigids = ids
01299         self.rename_entry_sigid = None
01300         self.dests_iconview.set_cursor (path, cell, start_editing=True)
01301 
01302     def printer_name_edit_start (self, cell, editable, path):
01303         debugprint ("editing-started")
01304         if isinstance(editable, gtk.Entry):
01305             id = editable.connect('changed', self.printer_name_editing)
01306             self.rename_entry_sigid = editable, id
01307 
01308     def printer_name_editing (self, entry):
01309         newname = origname = unicode (entry.get_text())
01310         newname = newname.replace("/", "")
01311         newname = newname.replace("#", "")
01312         newname = newname.replace(" ", "")
01313         if origname != newname:
01314             debugprint ("removed disallowed character %s" % origname[-1])
01315             entry.set_text(newname)
01316 
01317     def printer_name_edited (self, cell, path, newname):
01318         model = self.dests_iconview.get_model ()
01319         iter = model.get_iter (path)
01320         name = unicode (model.get_value (iter, 2))
01321         debugprint ("edited: %s -> %s" % (name, newname))
01322         try:
01323             self.rename_printer (name, newname)
01324         finally:
01325             cell.stop_editing (canceled=False)
01326             cell.set_property ('editable', False)
01327             for id in self.rename_sigids:
01328                 cell.disconnect (id)
01329             if self.rename_entry_sigid != None:
01330                 self.rename_entry_sigid[0].disconnect(self.rename_entry_sigid[1])
01331 
01332     def printer_name_edit_cancel (self, cell):
01333         debugprint ("editing-canceled")
01334         cell.stop_editing (canceled=True)
01335         cell.set_property ('editable', False)
01336         for id in self.rename_sigids:
01337             cell.disconnect (id)
01338         if self.rename_entry_sigid != None:
01339             self.rename_entry_sigid[0].disconnect(self.rename_entry_sigid[1])
01340 
01341 
01342     def rename_printer (self, old_name, new_name):
01343         if old_name.lower() == new_name.lower():
01344             return
01345 
01346         try:
01347             self.propertiesDlg.load (old_name,
01348                                      host=self.connect_server,
01349                                      encryption=self.connect_encrypt,
01350                                      parent=self.PrintersWindow)
01351         except RuntimeError:
01352             # Perhaps cupsGetPPD2 failed for a browsed printer
01353             pass
01354         except cups.IPPError, (e, m):
01355             show_IPP_Error (e, m, self.PrintersWindow)
01356             self.populateList ()
01357             return
01358 
01359         if not self.is_rename_possible (old_name):
01360             return
01361 
01362         self.cups._begin_operation (_("renaming printer"))
01363         rejecting = self.propertiesDlg.printer.rejecting
01364         if not rejecting:
01365             try:
01366                 self.propertiesDlg.printer.setAccepting (False)
01367                 if not self.is_rename_possible (old_name):
01368                     self.propertiesDlg.printer.setAccepting (True)
01369                     self.cups._end_operation ()
01370                     return
01371             except cups.IPPError, (e, msg):
01372                 show_IPP_Error (e, msg, self.PrintersWindow)
01373                 self.cups._end_operation ()
01374                 return
01375 
01376         if self.duplicate_printer (new_name):
01377             # Failure.
01378             self.monitor.update ()
01379 
01380             # Restore original accepting/rejecting state.
01381             if not rejecting and self.propertiesDlg.printer:
01382                 try:
01383                     self.propertiesDlg.printer.name = old_name
01384                     self.propertiesDlg.printer.setAccepting (True)
01385                 except cups.HTTPError, (s,):
01386                     show_HTTP_Error (s, self.PrintersWindow)
01387                 except cups.IPPError, (e, msg):
01388                     show_IPP_Error (e, msg, self.PrintersWindow)
01389 
01390             self.cups._end_operation ()
01391             self.populateList ()
01392             return
01393 
01394         if not self.propertiesDlg.printer:
01395             self.cups._end_operation ()
01396             self.populateList ()
01397             return
01398 
01399         # Restore rejecting state.
01400         if not rejecting:
01401             try:
01402                 self.propertiesDlg.printer.setAccepting (True)
01403             except cups.HTTPError, (s,):
01404                 show_HTTP_Error (s, self.PrintersWindow)
01405                 # Not fatal.
01406             except cups.IPPError, (e, msg):
01407                 show_IPP_Error (e, msg, self.PrintersWindow)
01408                 # Not fatal.
01409 
01410         # Fix up default printer.
01411         if self.default_printer == old_name:
01412             reload = False
01413             try:
01414                 reload = self.propertiesDlg.printer.setAsDefault ()
01415             except cups.HTTPError, (s,):
01416                 show_HTTP_Error (s, self.PrintersWindow)
01417                 # Not fatal.
01418             except cups.IPPError, (e, msg):
01419                 show_IPP_Error (e, msg, self.PrintersWindow)
01420                 # Not fatal.
01421 
01422             if reload:
01423                 self.reconnect ()
01424 
01425         # Finally, delete the old printer.
01426         try:
01427             self.cups.deletePrinter (old_name)
01428         except cups.HTTPError, (s,):
01429             show_HTTP_Error (s, self.PrintersWindow)
01430             # Not fatal
01431         except cups.IPPError, (e, msg):
01432             show_IPP_Error (e, msg, self.PrintersWindow)
01433             # Not fatal.
01434 
01435         self.cups._end_operation ()
01436 
01437         # ..and select the new printer.
01438         def select_new_printer (model, path, iter):
01439             name = unicode (model.get_value (iter, 2))
01440             if name == new_name:
01441                 self.dests_iconview.select_path (path)
01442         self.populateList ()
01443         model = self.dests_iconview.get_model ()
01444         model.foreach (select_new_printer)
01445 
01446     # Duplicate
01447 
01448     def duplicate_printer (self, new_name):
01449         self.propertiesDlg.printer.name = new_name
01450         self.propertiesDlg.printer.class_members = [] # for classes make sure all members
01451                                         # will get added
01452 
01453         ret = self.propertiesDlg.save_printer(self.propertiesDlg.printer,
01454                                               saveall=True,
01455                                               parent=self.PrintersWindow)
01456         return ret
01457 
01458     def on_duplicate_activate(self, UNUSED):
01459         iconview = self.dests_iconview
01460         paths = iconview.get_selected_items ()
01461         model = self.dests_iconview.get_model ()
01462         iter = model.get_iter (paths[0])
01463         name = unicode (model.get_value (iter, 2))
01464         self.entDuplicateName.set_text(name)
01465         self.NewPrinterName.set_transient_for (self.PrintersWindow)
01466         result = self.NewPrinterName.run()
01467         self.NewPrinterName.hide()
01468 
01469         if result == gtk.RESPONSE_CANCEL:
01470             return
01471 
01472         try:
01473             self.propertiesDlg.load (name,
01474                                      host=self.connect_server,
01475                                      encryption=self.connect_encrypt,
01476                                      parent=self.PrintersWindow)
01477         except RuntimeError:
01478             # Perhaps cupsGetPPD2 failed for a browsed printer
01479             pass
01480         except cups.IPPError, (e, m):
01481             show_IPP_Error (e, m, self.PrintersWindow)
01482             self.populateList ()
01483             return
01484 
01485         self.duplicate_printer (self.entDuplicateName.get_text ())
01486         self.monitor.update ()
01487 
01488     def on_entDuplicateName_changed(self, widget):
01489         # restrict
01490         text = unicode (widget.get_text())
01491         new_text = text
01492         new_text = new_text.replace("/", "")
01493         new_text = new_text.replace("#", "")
01494         new_text = new_text.replace(" ", "")
01495         if text!=new_text:
01496             widget.set_text(new_text)
01497         self.btnDuplicateOk.set_sensitive(
01498             newprinter.checkNPName(self.printers, new_text))
01499 
01500     # Delete
01501 
01502     def on_delete_activate(self, UNUSED):
01503         self.delete_selected_printer_queues ()
01504 
01505     def delete_selected_printer_queues (self):
01506         paths = self.dests_iconview.get_selected_items ()
01507         model = self.dests_iconview.get_model ()
01508         n = len (paths)
01509         if n == 1:
01510             iter = model.get_iter (paths[0])
01511             object = model.get_value (iter, 0)
01512             name = model.get_value (iter, 2)
01513             if object.is_class:
01514                 message_format = _("Really delete class '%s'?") % name
01515             else:
01516                 message_format = _("Really delete printer '%s'?") % name
01517         else:
01518             message_format = _("Really delete selected destinations?")
01519 
01520         dialog = gtk.MessageDialog(self.PrintersWindow,
01521                                    gtk.DIALOG_DESTROY_WITH_PARENT |
01522                                    gtk.DIALOG_MODAL,
01523                                    gtk.MESSAGE_WARNING,
01524                                    gtk.BUTTONS_NONE,
01525                                    message_format)
01526         dialog.add_buttons (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
01527                             gtk.STOCK_DELETE, gtk.RESPONSE_ACCEPT)
01528         dialog.set_default_response (gtk.RESPONSE_REJECT)
01529         result = dialog.run()
01530         dialog.destroy()
01531 
01532         if result != gtk.RESPONSE_ACCEPT:
01533             return
01534 
01535         try:
01536             for path in paths:
01537                 iter = model.get_iter (path)
01538                 name = model.get_value (iter, 2)
01539                 self.cups._begin_operation (_("deleting printer %s") % name)
01540                 name = unicode (name)
01541                 self.cups.deletePrinter (name)
01542                 self.cups._end_operation ()
01543         except cups.IPPError, (e, msg):
01544             self.cups._end_operation ()
01545             show_IPP_Error(e, msg, self.PrintersWindow)
01546 
01547         self.monitor.update ()
01548 
01549     # Enable/disable
01550     def on_enabled_activate(self, toggle_action):
01551         if self.updating_widgets:
01552             return
01553         enable = toggle_action.get_active ()
01554         iconview = self.dests_iconview
01555         paths = iconview.get_selected_items ()
01556         model = iconview.get_model ()
01557         for path in paths:
01558             iter = model.get_iter (path)
01559             printer = model.get_value (iter, 0)
01560             name = unicode (model.get_value (iter, 2), 'utf-8')
01561             self.cups._begin_operation (_("modifying printer %s") % name)
01562             try:
01563                 printer.setEnabled (enable)
01564             except cups.IPPError, (e, m):
01565                 errordialogs.show_IPP_Error (e, m, self.PrintersWindow)
01566                 # Give up on this operation.
01567                 self.cups._end_operation ()
01568                 break
01569 
01570             self.cups._end_operation ()
01571 
01572         self.monitor.update ()
01573 
01574     # Shared
01575     def on_shared_activate(self, menuitem):
01576         if self.updating_widgets:
01577             return
01578         share = menuitem.get_active ()
01579         iconview = self.dests_iconview
01580         paths = iconview.get_selected_items ()
01581         model = iconview.get_model ()
01582         success = False
01583         for path in paths:
01584             iter = model.get_iter (path)
01585             printer = model.get_value (iter, 0)
01586             self.cups._begin_operation (_("modifying printer %s") %
01587                                         printer.name)
01588             try:
01589                 printer.setShared (share)
01590                 success = True
01591             except cups.IPPError, (e, m):
01592                 show_IPP_Error(e, m, self.PrintersWindow)
01593                 self.cups._end_operation ()
01594                 # Give up on this operation.
01595                 break
01596 
01597             self.cups._end_operation ()
01598 
01599         if success and share:
01600             if self.server_is_publishing == None:
01601                 # We haven't yet seen a server-is-sharing-printers attribute.
01602                 # Assuming CUPS 1.4, this means we haven't opened a
01603                 # properties dialog yet.  Fetch the attributes now and
01604                 # look for it.
01605                 try:
01606                     printer.getAttributes ()
01607                     p = printer.other_attributes['server-is-sharing-printers']
01608                     self.server_is_publishing = p
01609                 except (cups.IPPError, KeyError):
01610                     pass
01611 
01612             self.advise_publish ()
01613 
01614         # For some reason CUPS doesn't give us a notification about
01615         # printers changing 'shared' state, so refresh instead of
01616         # update.  We have to defer this to prevent signal problems.
01617         def deferred_refresh ():
01618             gtk.gdk.threads_enter ()
01619             self.populateList ()
01620             gtk.gdk.threads_leave ()
01621             return False
01622         gobject.idle_add (deferred_refresh)
01623 
01624     def advise_publish(self):
01625         if not self.server_is_publishing:
01626             show_info_dialog (_("Publish Shared Printers"),
01627                               _("Shared printers are not available "
01628                                 "to other people unless the "
01629                                 "'Publish shared printers' option is "
01630                                 "enabled in the server settings."),
01631                               parent=self.PrintersWindow)
01632 
01633     # Set As Default
01634     def on_set_as_default_activate(self, UNUSED):
01635         iconview = self.dests_iconview
01636         paths = iconview.get_selected_items ()
01637         model = iconview.get_model ()
01638         iter = model.get_iter (paths[0])
01639         name = unicode (model.get_value (iter, 2))
01640         self.set_system_or_user_default_printer (name)
01641 
01642     def on_edit_activate (self, UNUSED):
01643         paths = self.dests_iconview.get_selected_items ()
01644         self.dests_iconview_item_activated (self.dests_iconview, paths[0])
01645 
01646     def on_create_class_activate (self, UNUSED):
01647         paths = self.dests_iconview.get_selected_items ()
01648         class_members = []
01649         model = self.dests_iconview.get_model ()
01650         for path in paths:
01651             iter = model.get_iter (path)
01652             name = unicode (model.get_value (iter, 2), 'utf-8')
01653             class_members.append (name)
01654         if not self.newPrinterGUI.init ("class",
01655                                         host=self.connect_server,
01656                                         encryption=self.connect_encrypt,
01657                                         parent=self.PrintersWindow):
01658             self.monitor.update ()
01659             return
01660 
01661         out_model = self.newPrinterGUI.tvNCNotMembers.get_model ()
01662         in_model = self.newPrinterGUI.tvNCMembers.get_model ()
01663         iter = out_model.get_iter_first ()
01664         while iter != None:
01665             next = out_model.iter_next (iter)
01666             data = out_model.get (iter, 0)
01667             if data[0] in class_members:
01668                 in_model.append (data)
01669                 out_model.remove (iter)
01670             iter = next
01671 
01672     def on_view_print_queue_activate (self, UNUSED):
01673         paths = self.dests_iconview.get_selected_items ()
01674         if len (paths):
01675             specific_dests = []
01676             model = self.dests_iconview.get_model ()
01677             for path in paths:
01678                 iter = model.get_iter (path)
01679                 name = unicode (model.get_value (iter, 2), 'utf-8')
01680                 specific_dests.append (name)
01681             viewer = jobviewer.JobViewer (None, None, my_jobs=False,
01682                                           specific_dests=specific_dests,
01683                                           parent=self.PrintersWindow)
01684             viewer.connect ('finished', self.on_jobviewer_exit)
01685         else:
01686             viewer = jobviewer.JobViewer (None, None, my_jobs=False,
01687                                           parent=self.PrintersWindow)
01688             viewer.connect ('finished', self.on_jobviewer_exit)
01689 
01690         self.jobviewers.append (viewer)
01691 
01692     def on_jobviewer_exit (self, viewer):
01693         try:
01694             i = self.jobviewers.index (viewer)
01695             del self.jobviewers[i]
01696         except ValueError:
01697             # This shouldn't happen, but does (bug #757520).
01698             debugprint ("Jobviewer exited but not in list:\n"
01699                         "%s\n%s" % (repr (viewer), repr (self.jobviewers)))
01700 
01701     def on_view_discovered_printers_activate (self, UNUSED):
01702         self.populateList ()
01703 
01704     def on_troubleshoot_activate(self, widget):
01705         if not self.__dict__.has_key ('troubleshooter'):
01706             self.troubleshooter = troubleshoot.run (self.on_troubleshoot_quit)
01707 
01708     def on_troubleshoot_quit(self, troubleshooter):
01709         del self.troubleshooter
01710 
01711     # About dialog
01712     def on_about_activate(self, widget):
01713         self.AboutDialog.set_transient_for (self.PrintersWindow)
01714         self.AboutDialog.run()
01715         self.AboutDialog.hide()
01716 
01717     ##########################################################################
01718     ### Server settings
01719     ##########################################################################
01720 
01721     ### The "Problems?" clickable label
01722     def on_problems_button_clicked (self, serversettings):
01723         if not self.__dict__.has_key ('troubleshooter'):
01724             self.troubleshooter = troubleshoot.run (self.on_troubleshoot_quit,
01725                                                     parent=serversettings.get_dialog ())
01726 
01727     # ====================================================================
01728     # == New Printer Dialog ==============================================
01729     # ====================================================================
01730 
01731     # new printer
01732     def on_new_printer_activate(self, widget):
01733         busy (self.PrintersWindow)
01734         if not self.newPrinterGUI.init("printer",
01735                                        host=self.connect_server,
01736                                        encryption=self.connect_encrypt,
01737                                        parent=self.PrintersWindow):
01738             self.monitor.update ()
01739         ready (self.PrintersWindow)
01740 
01741     # new class
01742     def on_new_class_activate(self, widget):
01743         if not self.newPrinterGUI.init("class",
01744                                        host=self.connect_server,
01745                                        encryption=self.connect_encrypt,
01746                                        parent=self.PrintersWindow):
01747             self.monitor.update ()
01748 
01749     def on_new_printer_added (self, obj, name):
01750         debugprint ("New printer added: %s" % name)
01751         self.populateList ()
01752 
01753         if not self.printers.has_key (name):
01754             # At this stage the printer has disappeared even though we
01755             # only added it moments ago.
01756             debugprint ("New printer disappeared")
01757             return
01758 
01759         # Now select it.
01760         model = self.dests_iconview.get_model ()
01761         iter = model.get_iter_first ()
01762         while iter != None:
01763             queue = unicode (model.get_value (iter, 2))
01764             if queue == name:
01765                 path = model.get_path (iter)
01766                 self.dests_iconview.scroll_to_path (path, True, 0.5, 0.5)
01767                 self.dests_iconview.unselect_all ()
01768                 self.dests_iconview.set_cursor (path)
01769                 self.dests_iconview.select_path (path)
01770                 break
01771 
01772             iter = model.iter_next (iter)
01773 
01774         # Finally, suggest printing a test page.
01775         self.propertiesDlg.load (name)
01776         if self.propertiesDlg.ppd:
01777             try:
01778                 self.checkDriverExists (self.PrintersWindow, name,
01779                                         ppd=self.propertiesDlg.ppd)
01780             except:
01781                 nonfatalException()
01782 
01783             q = gtk.MessageDialog (self.PrintersWindow,
01784                                    gtk.DIALOG_DESTROY_WITH_PARENT |
01785                                    gtk.DIALOG_MODAL,
01786                                    gtk.MESSAGE_QUESTION,
01787                                    gtk.BUTTONS_NONE,
01788                                    _("Would you like to print a test page?"))
01789             q.add_buttons (gtk.STOCK_CANCEL, gtk.RESPONSE_NO,
01790                            _("Print Test Page"), gtk.RESPONSE_YES)
01791             response = q.run ()
01792             q.destroy ()
01793             if response == gtk.RESPONSE_YES:
01794                 self.propertiesDlg.dialog.hide ()
01795 
01796                 properties_shown = False
01797                 try:
01798                     # Load the printer details but hide the properties dialog.
01799                     self.display_properties_dialog_for (name)
01800                     properties_shown = True
01801                 except RuntimeError:
01802                     pass
01803 
01804                 if properties_shown:
01805                     # Click the test button.
01806                     self.propertiesDlg.btnPrintTestPage.clicked ()
01807 
01808     ## Service start-up
01809     def on_start_service_clicked (self, button):
01810         button.set_sensitive (False)
01811         self.servicestart.start (reply_handler=self.on_start_service_reply,
01812                                  error_handler=self.on_start_service_reply)
01813 
01814     def on_start_service_reply (self, *args):
01815         gobject.timeout_add_seconds (1, self.service_started_try)
01816 
01817     def service_started_try (self):
01818         gtk.gdk.threads_enter ()
01819         self.on_btnRefresh_clicked (None)
01820         gtk.gdk.threads_leave ()
01821         gobject.timeout_add_seconds (1, self.service_started_retry)
01822         return False
01823 
01824     def service_started_retry (self):
01825         if not self.cups:
01826             gtk.gdk.threads_enter ()
01827             self.on_btnRefresh_clicked (None)
01828             self.btnStartService.set_sensitive (True)
01829             gtk.gdk.threads_leave ()
01830 
01831         return False
01832 
01833     def checkDriverExists(self, parent, name, ppd=None):
01834         """Check that the driver for an existing queue actually
01835         exists, and prompt to install the appropriate package
01836         if not.
01837 
01838         ppd: cups.PPD object, if already created"""
01839 
01840         # Is this queue on the local machine?  If not, we can't check
01841         # anything at all.
01842         server = cups.getServer ()
01843         if not (self.connect_server == 'localhost' or
01844                 self.connect_server[0] == '/'):
01845             return
01846 
01847         # Fetch the PPD if we haven't already.
01848         if not ppd:
01849             try:
01850                 filename = self.cups.getPPD(name)
01851             except cups.IPPError, (e, msg):
01852                 if e == cups.IPP_NOT_FOUND:
01853                     # This is a raw queue.  Nothing to check.
01854                     return
01855                 else:
01856                     self.show_IPP_Error(e, msg)
01857                     return
01858 
01859             ppd = cups.PPD(filename)
01860             os.unlink(filename)
01861 
01862         (pkgs, exes) = cupshelpers.missingPackagesAndExecutables (ppd)
01863         if len (pkgs) > 0 or len (exes) > 0:
01864             # We didn't find a necessary executable.  Complain.
01865             can_install = False
01866             if len (pkgs) > 0:
01867                 try:
01868                     pk = installpackage.PackageKit ()
01869                     can_install = True
01870                 except:
01871                     pass
01872 
01873             if can_install and len (pkgs) > 0:
01874                 pkg = pkgs[0]
01875                 install_text = ('<span weight="bold" size="larger">' +
01876                                 _('Install driver') + '</span>\n\n' +
01877                                 _("Printer '%s' requires the %s package but "
01878                                   "it is not currently installed.") %
01879                                 (name, pkg))
01880                 dialog = self.InstallDialog
01881                 self.lblInstall.set_markup(install_text)
01882                 dialog.set_transient_for (parent)
01883                 response = dialog.run ()
01884                 dialog.hide ()
01885                 if response == gtk.RESPONSE_OK:
01886                     # Install the package.
01887                     try:
01888                         xid = parent.window.xid
01889                         pk.InstallPackageName (xid, 0, pkg)
01890                     except:
01891                         pass # should handle error
01892             else:
01893                 show_error_dialog (_('Missing driver'),
01894                                    _("Printer '%s' requires the '%s' program "
01895                                      "but it is not currently installed.  "
01896                                      "Please install it before using this "
01897                                      "printer.") % (name, (exes + pkgs)[0]),
01898                                    parent)
01899 
01900     def on_printer_modified (self, obj, name, ppd_has_changed):
01901         debugprint ("Printer modified by user: %s" % name)
01902         # Load information about the printer,
01903         # e.g. self.propertiesDlg.server_side_options and self.propertiesDlg.ppd
01904         # (both used below).
01905         self.propertiesDlg.load (name)
01906 
01907         if self.propertiesDlg.ppd:
01908             try:
01909                 self.checkDriverExists (self.propertiesDlg.dialog,
01910                                         name, ppd=self.propertiesDlg.ppd)
01911             except:
01912                 nonfatalException()
01913 
01914             # Also check to see whether the media option has become
01915             # invalid.  This can happen if it had previously been
01916             # explicitly set to a page size that is not offered with
01917             # the new PPD (see bug #441836).
01918             try:
01919                 option = self.propertiesDlg.server_side_options['media']
01920                 if option.get_current_value () == None:
01921                     debugprint ("Invalid media option: resetting")
01922                     option.reset ()
01923                     self.propertiesDlg.changed.add (option)
01924                     self.propertiesDlg.save_printer (self.printer)
01925             except KeyError:
01926                 pass
01927             except:
01928                 nonfatalException()
01929 
01930     ## Monitor signal helpers
01931     def printer_added_or_removed (self):
01932         # Just fetch the list of printers again.  This is too simplistic.
01933         def deferred_refresh ():
01934             gtk.gdk.threads_enter ()
01935             self.populateList (prompt_allowed=False)
01936             gtk.gdk.threads_leave ()
01937             return False
01938 
01939         if self.populateList_timer:
01940             gobject.source_remove (self.populateList_timer)
01941 
01942         self.populateList_timer = gobject.timeout_add (200, deferred_refresh)
01943         debugprint ("Deferred populateList by 200ms")
01944 
01945     ## Monitor signal handlers
01946     def printer_added (self, mon, printer):
01947         self.printer_added_or_removed ()
01948 
01949     def printer_event (self, mon, printer, eventname, event):
01950         if self.printers.has_key (printer):
01951             self.printers[printer].update (**event)
01952             self.dests_iconview_selection_changed (self.dests_iconview)
01953             self.printer_added_or_removed ()
01954 
01955     def printer_removed (self, mon, printer):
01956         self.printer_added_or_removed ()
01957 
01958     def cups_connection_error (self, mon):
01959         self.cups = None
01960         self.setConnected ()
01961         self.populateList (prompt_allowed=False)
01962 
01963     def cups_connection_recovered (self, mon):
01964         debugprint ("Trying to recover connection")
01965         gobject.idle_add (self.service_started_try)
01966 
01967 def main():
01968     cups.setUser (os.environ.get ("CUPS_USER", cups.getUser()))
01969     gtk.gdk.threads_init ()
01970     gobject.threads_init()
01971     from dbus.glib import DBusGMainLoop
01972     DBusGMainLoop (set_as_default=True)
01973 
01974     mainwindow = GUI()
01975 
01976     if gtk.__dict__.has_key("main"):
01977         gtk.main()
01978     else:
01979         gtk.mainloop()
01980 
01981 
01982 if __name__ == "__main__":
01983     import getopt
01984     try:
01985         opts, args = getopt.gnu_getopt (sys.argv[1:], '',
01986                                         ['debug'])
01987     except getopt.GetoptError:
01988         show_help ()
01989         sys.exit (1)
01990 
01991     for opt, optarg in opts:
01992         if opt == '--debug':
01993             set_debugging (True)
01994             cupshelpers.ppds.set_debugprint_fn (debugprint)
01995 
01996     main()