Back to index

system-config-printer  1.3.9+20120706
options.py
Go to the documentation of this file.
00001 ## system-config-printer
00002 
00003 ## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
00004 ## Authors:
00005 ##  Tim Waugh <twaugh@redhat.com>
00006 ##  Florian Festi <ffesti@redhat.com>
00007 
00008 ## This program is free software; you can redistribute it and/or modify
00009 ## it under the terms of the GNU General Public License as published by
00010 ## the Free Software Foundation; either version 2 of the License, or
00011 ## (at your option) any later version.
00012 
00013 ## This program is distributed in the hope that it will be useful,
00014 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 ## GNU General Public License for more details.
00017 
00018 ## You should have received a copy of the GNU General Public License
00019 ## along with this program; if not, write to the Free Software
00020 ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00021 
00022 import gobject
00023 import gtk
00024 import cups
00025 import ppdippstr
00026 import re
00027 
00028 cups.require ("1.9.55")
00029 
00030 # Special IPP type
00031 class IPPResolution(tuple):
00032     def __new__ (cls, values):
00033         cls.UNITS_BY_VAL = { cups.IPP_RES_PER_INCH: "dpi",
00034                              cups.IPP_RES_PER_CM: "dpc" }
00035         cls.UNITS_DEFAULT = cups.IPP_RES_PER_INCH
00036 
00037         cls.UNITS_BY_STR = {}
00038         for v, s in cls.UNITS_BY_VAL.iteritems ():
00039             cls.UNITS_BY_STR[s] = v
00040 
00041         if isinstance (values, str):
00042             matches = re.match ("(\d+)\D+(\d+)(.*)", values).groups ()
00043             xres = int (matches[0])
00044             yres = int (matches[1])
00045             units = cls.UNITS_BY_STR.get (matches[2], cls.UNITS_DEFAULT)
00046         else:
00047             xres = values[0]
00048             yres = values[1]
00049             units = values[2]
00050 
00051         self = tuple.__new__ (cls, (xres, yres, units))
00052         self.xres = xres
00053         self.yres = yres
00054         self.units = units
00055         return self
00056 
00057     def __init__ (self, values):
00058         return tuple.__init__ (self, (self.xres, self.yres, self.units))
00059 
00060     def __str__ (self):
00061         return "%sx%s%s" % (self.xres, self.yres,
00062                             self.UNITS_BY_VAL.get (self.units,
00063                                                    self.UNITS_DEFAULT))
00064 
00065 def OptionWidget(name, v, s, on_change):
00066     if isinstance(v, list):
00067         # XXX
00068         if isinstance(s, list):
00069             for vv in v + s:
00070                 if not isinstance(vv, str): raise ValueError
00071             return OptionSelectMany(name, v, s, on_change)
00072         print v, s
00073         raise NotImplemented
00074     else:
00075         if (isinstance(s, int) or
00076             isinstance(s, float) or
00077             (isinstance(s, tuple) and
00078              len(s) == 2 and
00079              ((isinstance(s[0], int) and isinstance(s[1], int)) or
00080               (isinstance(s[0], float) and isinstance(s[1], float))))):
00081             try:
00082                 if (isinstance(s, int) or
00083                     isinstance(s, tuple) and isinstance(s[0], int)):
00084                     v = int(v)
00085                 else:
00086                     v = float(v)
00087             except ValueError:
00088                 return OptionText(name, v, "", on_change)
00089             return OptionNumeric(name, v, s, on_change)
00090         elif isinstance(s, list):
00091             for sv in s:
00092                 if isinstance(sv, tuple) and len (sv) == 3:
00093                     return OptionSelectOneResolution(name, v, s, on_change)
00094                 elif not isinstance(sv, int):
00095                     return OptionSelectOne(name, v, s, on_change)
00096             try:
00097                 v = int(v)
00098             except ValueError:
00099                 return OptionSelectOne(name, v, s, on_change)
00100             return OptionSelectOneNumber(name, v, s, on_change)
00101         elif isinstance(s, str):
00102             return OptionText(name, v, s, on_change)
00103         else:
00104             raise ValueError
00105 
00106 # ---------------------------------------------------------------------------
00107 
00108 class OptionInterface:
00109     def get_default(self):
00110         return None
00111 
00112     def get_current_value(self):
00113         raise NotImplemented
00114 
00115     def is_changed(self):
00116         raise NotImplemented
00117 
00118 class OptionAlwaysShown(OptionInterface):
00119     # States
00120     STATE_UNCHANGED=0
00121     STATE_RESET=1
00122     STATE_ADJUSTED=2
00123 
00124     def __init__(self, name, ipp_type, system_default,
00125                  widget, button, combobox_map = None, use_supported = False):
00126         self.name = name
00127         self.widget = widget
00128         self.button = button
00129         if ipp_type == bool:
00130             def bool_type (x):
00131                 if type (x) == str:
00132                     if x.lower () in ("false", "no", "off"):
00133                         return False
00134                     # Even the empty string is true.
00135                     return True
00136                 return bool (x)
00137             ipp_type = bool_type
00138         self.ipp_type = ipp_type
00139         self.set_default (system_default)
00140         self.combobox_map = combobox_map
00141 
00142         if (type(self.widget) == gtk.ComboBox and
00143             self.widget.get_model () == None):
00144             print "No ComboBox model for %s" % self.name
00145             model = gtk.ListStore (gobject.TYPE_STRING)
00146             self.widget.set_model (model)
00147 
00148         if combobox_map != None and ipp_type == int:
00149             model = self.widget.get_model ()
00150             i = 0
00151             dict = {}
00152             iter = model.get_iter_first ()
00153             while iter:
00154                 dict[combobox_map[i]] = model.get_value (iter, 0)
00155                 i += 1
00156                 iter = model.iter_next (iter)
00157             self.combobox_dict = dict
00158         self.use_supported = use_supported
00159         self.reinit (None)
00160 
00161     def get_default(self):
00162         return self.system_default
00163 
00164     def set_default(self, system_default):
00165         # For the media option, the system default depends on the printer's
00166         # PageSize setting.  This method allows the main module to tell us
00167         # what that is.
00168         self.system_default = self.ipp_type (system_default)
00169 
00170     def reinit(self, original_value, supported=None):
00171         """Set the original value of the option and the supported choices.
00172         The special value None for original_value resets the option to the
00173         system default."""
00174         if (supported != None and
00175             self.use_supported):
00176             if (type(self.widget) == gtk.ComboBox and
00177                 (self.ipp_type == str or self.ipp_type == IPPResolution)):
00178                 model = self.widget.get_model ()
00179                 model.clear ()
00180                 translations = ppdippstr.job_options.get (self.name)
00181                 if translations:
00182                     self.combobox_map = []
00183                     self.combobox_dict = dict()
00184                     i = 0
00185 
00186                 for each in supported:
00187                     txt = each
00188                     if self.ipp_type != str:
00189                         txt = str (self.ipp_type (each))
00190 
00191                     if translations:
00192                         self.combobox_map.append (txt)
00193                         text = translations.get (txt)
00194                         self.combobox_dict[each] = text
00195                         i += 1
00196                     else:
00197                         text = txt
00198 
00199                     iter = model.append ()
00200                     model.set_value (iter, 0, text)
00201             elif (type(self.widget) == gtk.ComboBox and
00202                   self.ipp_type == int and
00203                   self.combobox_map != None):
00204                 model = self.widget.get_model ()
00205                 model.clear ()
00206                 for each in supported:
00207                     iter = model.append ()
00208                     model.set_value (iter, 0, self.combobox_dict[each])
00209 
00210         if original_value != None:
00211             self.original_value = self.ipp_type (original_value)
00212             self.set_widget_value (self.original_value)
00213             self.button.set_sensitive (True)
00214         else:
00215             self.original_value = None
00216             self.set_widget_value (self.system_default)
00217             self.button.set_sensitive (False)
00218         self.state = self.STATE_UNCHANGED
00219 
00220     def set_widget_value(self, ipp_value):
00221         t = type(self.widget)
00222         if t == gtk.SpinButton:
00223             return self.widget.set_value (ipp_value)
00224         elif t == gtk.ComboBox:
00225             if ((self.ipp_type == str or self.ipp_type == IPPResolution)
00226                 and self.combobox_map == None):
00227                 model = self.widget.get_model ()
00228                 iter = model.get_iter_first ()
00229                 while (iter != None and
00230                        self.ipp_type (model.get_value (iter, 0)) != ipp_value):
00231                     iter = model.iter_next (iter)
00232                 if iter:
00233                     self.widget.set_active_iter (iter)
00234             else:
00235                 # It's an int.
00236                 if self.combobox_map:
00237                     index = self.combobox_map.index (ipp_value)
00238                 else:
00239                     index = ipp_value
00240                 return self.widget.set_active (index)
00241         elif t == gtk.CheckButton:
00242             return self.widget.set_active (ipp_value)
00243         else:
00244             raise NotImplemented
00245 
00246     def get_widget_value(self):
00247         t = type(self.widget)
00248         if t == gtk.SpinButton:
00249             # Ideally we would use self.widget.get_value() here, but
00250             # it doesn't work if the value has been typed in and then
00251             # the Apply button immediately clicked.  To handle this,
00252             # we use self.widget.get_text() and fall back to
00253             # get_value() if the result cannot be interpreted as the
00254             # type we expect.
00255             try:
00256                 return self.ipp_type (self.widget.get_text ())
00257             except ValueError:
00258                 # Can't convert result of get_text() to ipp_type.
00259                 return self.ipp_type (self.widget.get_value ())
00260         elif t == gtk.ComboBox:
00261             if self.combobox_map:
00262                 return self.combobox_map[self.widget.get_active()]
00263             if self.ipp_type == str or self.ipp_type == IPPResolution:
00264                 return self.widget.get_active_text ()
00265             return self.ipp_type (self.widget.get_active ())
00266         elif t == gtk.CheckButton:
00267             return self.ipp_type (self.widget.get_active ())
00268 
00269         print t
00270         raise NotImplemented
00271 
00272     def get_current_value(self):
00273         return self.get_widget_value ()
00274 
00275     def is_changed(self):
00276         if self.original_value != None:
00277             # There was a value set previously.
00278             if self.state == self.STATE_RESET:
00279                 # It's been removed.
00280                 return True
00281             if self.state == self.STATE_ADJUSTED:
00282                 if self.get_current_value () != self.original_value:
00283                     return True
00284                 return False
00285 
00286             # The value is the same as before, and not reset.
00287             return False
00288 
00289         # There was no original value set.
00290         if self.state == self.STATE_ADJUSTED:
00291             # It's been adjusted.
00292             return True
00293 
00294         # It's been left alone, or possible adjusted and then reset.
00295         return False
00296 
00297     def reset(self):
00298         self.set_widget_value (self.system_default)
00299         self.state = self.STATE_RESET
00300         self.button.set_sensitive (False)
00301 
00302     def changed(self):
00303         self.state = self.STATE_ADJUSTED
00304         self.button.set_sensitive (True)
00305 
00306 class OptionAlwaysShownSpecial(OptionAlwaysShown):
00307     def __init__(self, name, ipp_type, system_default,
00308                  widget, button, combobox_map = None, use_supported = False,
00309                  special_choice = "System default"):
00310         self.special_choice = special_choice
00311         self.special_choice_shown = False
00312         OptionAlwaysShown.__init__ (self, name, ipp_type, system_default,
00313                                     widget, button,
00314                                     combobox_map=combobox_map,
00315                                     use_supported=use_supported)
00316 
00317     def show_special_choice (self):
00318         if self.special_choice_shown:
00319             return
00320 
00321         self.special_choice_shown = True
00322         # Only works for ComboBox widgets
00323         model = self.widget.get_model ()
00324         iter = model.insert (0)
00325         model.set_value (iter, 0, self.special_choice)
00326         self.widget.set_active_iter (model.get_iter_first ())
00327 
00328     def hide_special_choice (self):
00329         if not self.special_choice_shown:
00330             return
00331 
00332         self.special_choice_shown = False
00333         # Only works for ComboBox widgets
00334         model = self.widget.get_model ()
00335         model.remove (model.get_iter_first ())
00336 
00337     def reinit(self, original_value, supported=None):
00338         if original_value != None:
00339             self.hide_special_choice ()
00340         else:
00341             self.show_special_choice ()
00342 
00343         OptionAlwaysShown.reinit (self, original_value, supported=supported)
00344 
00345     def reset(self):
00346         self.show_special_choice ()
00347         OptionAlwaysShown.reset (self)
00348 
00349     def changed(self):
00350         OptionAlwaysShown.changed (self)
00351         if self.widget.get_active () > 0:
00352             self.hide_special_choice ()
00353 
00354 class Option(OptionInterface):
00355 
00356     conflicts = None
00357 
00358     def __init__(self, name, value, supported, on_change):
00359         self.name = name
00360         self.value = value
00361         self.supported = supported
00362         self.on_change = on_change
00363         self.is_new = False
00364 
00365         label = name
00366         if not label.endswith (':'):
00367             label += ':'
00368         self.label = gtk.Label(label)
00369         self.label.set_alignment(0.0, 0.5)
00370 
00371     def get_current_value(self):
00372         raise NotImplemented
00373 
00374     def is_changed(self):
00375         return (self.is_new or
00376                 str (self.get_current_value()) != str (self.value))
00377 
00378     def changed(self, widget, *args):
00379         self.on_change(self)
00380     
00381 # ---------------------------------------------------------------------------
00382 
00383 class OptionSelectOne(Option):
00384 
00385     def __init__(self, name, value, supported, on_change):
00386         Option.__init__(self, name, value, supported, on_change)
00387 
00388         self.selector = gtk.combo_box_new_text()
00389         
00390         selected = None
00391         for nr, choice in enumerate(supported):
00392             self.selector.append_text(str(choice))
00393             if str (value) == str (choice):
00394                 selected = nr
00395         if selected is not None:
00396             self.selector.set_active(selected)
00397         else:
00398             print "Unknown value for %s: %s" % (name, value)
00399             print "Choices:", supported
00400             if len(supported) > 0:
00401                 print "Selecting from choices:", supported[0]
00402                 self.selector.set_active(0)
00403         self.selector.connect("changed", self.changed)
00404 
00405     def get_current_value(self):
00406         return self.selector.get_active_text()
00407 
00408 # ---------------------------------------------------------------------------
00409 
00410 class OptionSelectOneResolution(OptionSelectOne):
00411     def __init__(self, name, value, supported, on_change):
00412         self.UNITS_BY_VAL = { cups.IPP_RES_PER_INCH: "dpi",
00413                               cups.IPP_RES_PER_CM: "dpc" }
00414         self.UNITS_DEFAULT = cups.IPP_RES_PER_INCH
00415         self.UNITS_BY_STR = {}
00416         for v, s in self.UNITS_BY_VAL.iteritems ():
00417             self.UNITS_BY_STR[s] = v
00418 
00419         value = self.string (value)
00420         supported = map (self.string, supported)
00421         OptionSelectOne.__init__ (self, name, value, supported, on_change)
00422 
00423     def string(self, value):
00424         return "%sx%s%s" % (value[0], value[1],
00425                             self.UNITS_BY_VAL.get (value[2], ""))
00426 
00427     def value(self, string):
00428         matches = re.match ("(\d+)\D+(\d+)(.*)", string).groups ()
00429         return (int (matches[0]), int (matches[1]),
00430                 self.UNITS_BY_STR.get (matches[2], self.UNITS_DEFAULT))
00431 
00432     def get_current_value(self):
00433         return self.value (self.selector.get_active_text())
00434 
00435 # ---------------------------------------------------------------------------
00436 
00437 class OptionSelectOneNumber(OptionSelectOne):
00438 
00439     def get_current_value(self):
00440         return int(self.selector.get_active_text())
00441 
00442 # ---------------------------------------------------------------------------
00443 
00444 class OptionSelectMany(Option):
00445 
00446     def __init__(self, name, value, supported, on_change):
00447         Option.__init__(self, name, value, supported, on_change)
00448         self.checkboxes = []
00449         vbox = gtk.VBox()
00450 
00451         for s in supported:
00452             checkbox = gtk.CheckButton(label=s)
00453             checkbox.set_active(s in value)
00454             vbox.add(checkbox)
00455             checkbox.connect("toggled", self.changed)
00456             self.checkboxes.append(checkbox)
00457         self.selector = vbox
00458             
00459     def get_current_value(self):
00460         return[s for s, chk in zip(self.supported, self.checkboxes)
00461                if chk.get_active()]
00462 
00463 # ---------------------------------------------------------------------------
00464 
00465 class OptionNumeric(Option):
00466     def __init__(self, name, value, supported, on_change):
00467         self.is_float = (isinstance(supported, float) or
00468                          (isinstance(supported, tuple) and
00469                           isinstance(supported[0], float)))
00470         if self.is_float:
00471             digits = 2
00472         else:
00473             digits = 0
00474 
00475         if not isinstance(supported, tuple):
00476             supported = (0, supported)
00477         Option.__init__(self, name, value, supported, on_change)
00478         adj = gtk.Adjustment(value, supported[0], supported[1], 1.0, 5.0, 0.0)
00479         self.selector = gtk.SpinButton(adj, climb_rate=1.0, digits=digits)
00480         if not self.is_float:
00481             self.selector.set_numeric(True)
00482         self.selector.connect("changed", self.changed)
00483 
00484     def get_current_value(self):
00485         if self.is_float:
00486             return self.selector.get_value()
00487         return self.selector.get_value_as_int()
00488 
00489 # ---------------------------------------------------------------------------
00490 
00491 class OptionText(Option):
00492     def __init__(self, name, value, supported, on_change):
00493         Option.__init__(self, name, value, supported, on_change)
00494 
00495         self.selector = gtk.Entry()
00496         self.selector.set_text(value)
00497         self.selector.connect("changed", self.changed)
00498 
00499     def get_current_value(self):
00500         return self.selector.get_text()