## system-config-printer
## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc.
## Authors:
## Tim Waugh <twaugh@redhat.com>
## Florian Festi <ffesti@redhat.com>
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from debug import *
from gi.repository import Gtk
import cups
import ppdippstr
import re
cups.require ("1.9.55")
# Special IPP type
class IPPResolution(tuple):
def __new__ (cls, values):
cls.UNITS_BY_VAL = { cups.IPP_RES_PER_INCH: "dpi",
cups.IPP_RES_PER_CM: "dpc" }
cls.UNITS_DEFAULT = cups.IPP_RES_PER_INCH
cls.UNITS_BY_STR = {}
for v, s in cls.UNITS_BY_VAL.items ():
cls.UNITS_BY_STR[s] = v
if isinstance (values, str):
matches = re.match (r"(\d+)\D+(\d+)(.*)", values).groups ()
xres = int (matches[0])
yres = int (matches[1])
units = cls.UNITS_BY_STR.get (matches[2], cls.UNITS_DEFAULT)
else:
xres = values[0]
yres = values[1]
units = values[2]
self = tuple.__new__ (cls, (xres, yres, units))
self.xres = xres
self.yres = yres
self.units = units
return self
def __init__ (self, values):
return tuple.__init__ ((self.xres, self.yres, self.units))
def __str__ (self):
return "%sx%s%s" % (self.xres, self.yres,
self.UNITS_BY_VAL.get (self.units,
self.UNITS_DEFAULT))
def OptionWidget(name, v, s, on_change):
if isinstance(v, list):
# XXX
if isinstance(s, list):
for vv in v + s:
if not isinstance(vv, str): raise ValueError
return OptionSelectMany(name, v, s, on_change)
print(v, s)
raise NotImplementedError
else:
if (isinstance(s, int) or
isinstance(s, float) or
(isinstance(s, tuple) and
len(s) == 2 and
((isinstance(s[0], int) and isinstance(s[1], int)) or
(isinstance(s[0], float) and isinstance(s[1], float))))):
try:
if (isinstance(s, int) or
isinstance(s, tuple) and isinstance(s[0], int)):
v = int(v)
else:
v = float(v)
except ValueError:
return OptionText(name, v, "", on_change)
return OptionNumeric(name, v, s, on_change)
elif isinstance(s, list):
for sv in s:
if isinstance(sv, tuple) and len (sv) == 3:
return OptionSelectOneResolution(name, v, s, on_change)
elif not isinstance(sv, int):
return OptionSelectOne(name, v, s, on_change)
try:
v = int(v)
except ValueError:
return OptionSelectOne(name, v, s, on_change)
return OptionSelectOneNumber(name, v, s, on_change)
elif isinstance(s, str):
return OptionText(name, v, s, on_change)
else:
raise ValueError
# ---------------------------------------------------------------------------
class OptionInterface:
def get_default(self):
return None
def get_current_value(self):
raise NotImplementedError
def is_changed(self):
raise NotImplementedError
class OptionAlwaysShown(OptionInterface):
# States
STATE_UNCHANGED=0
STATE_RESET=1
STATE_ADJUSTED=2
def __init__(self, name, ipp_type, system_default,
widget, button, combobox_map = None, use_supported = False):
self.name = name
self.widget = widget
self.button = button
if ipp_type == bool:
def bool_type (x):
if type (x) == str:
if x.lower () in ("false", "no", "off"):
return False
# Even the empty string is true.
return True
return bool (x)
ipp_type = bool_type
self.ipp_type = ipp_type
self.set_default (system_default)
self.combobox_map = combobox_map
if (type(self.widget) == Gtk.ComboBox and
self.widget.get_model () is None):
debugprint("No ComboBox model for %s" % self.name)
model = Gtk.ListStore (str)
self.widget.set_model (model)
if combobox_map is not None and ipp_type == int:
model = self.widget.get_model ()
i = 0
dict = {}
iter = model.get_iter_first ()
while iter:
dict[combobox_map[i]] = model.get_value (iter, 0)
i += 1
iter = model.iter_next (iter)
self.combobox_dict = dict
self.use_supported = use_supported
self.reinit (None)
def get_default(self):
return self.system_default
def set_default(self, system_default):
# For the media option, the system default depends on the printer's
# PageSize setting. This method allows the main module to tell us
# what that is.
self.system_default = self.ipp_type (system_default)
def reinit(self, original_value, supported=None):
"""Set the original value of the option and the supported choices.
The special value None for original_value resets the option to the
system default."""
if (supported is not None and
self.use_supported):
if (type(self.widget) == Gtk.ComboBox and
self.ipp_type == str):
model = self.widget.get_model ()
model.clear ()
translations = ppdippstr.job_options.get (self.name)
if translations:
self.combobox_map = []
self.combobox_dict = dict()
i = 0
for each in supported:
txt = str (self.ipp_type (each))
if translations:
self.combobox_map.append (txt)
text = translations.get (txt)
self.combobox_dict[each] = text
i += 1
else:
text = txt
iter = model.append ()
model.set_value (iter, 0, text)
elif type(self.widget) == Gtk.ComboBoxText:
self.widget.remove_all () # emits 'changed'
translations = ppdippstr.job_options.get (self.name)
if translations:
self.combobox_map = []
self.combobox_dict = dict()
i = 0
for each in supported:
txt = str (self.ipp_type (each))
if translations:
self.combobox_map.append (txt)
text = translations.get (txt)
self.combobox_dict[each] = text
i += 1
else:
text = txt
self.widget.append_text (text)
elif (type(self.widget) == Gtk.ComboBox and
self.ipp_type == int and
self.combobox_map is not None):
model = self.widget.get_model ()
model.clear ()
for each in supported:
iter = model.append ()
model.set_value (iter, 0, self.combobox_dict[each])
if original_value is not None:
self.original_value = self.ipp_type (original_value)
self.set_widget_value (self.original_value)
self.button.set_sensitive (True)
else:
self.original_value = None
self.set_widget_value (self.system_default)
self.button.set_sensitive (False)
self.state = self.STATE_UNCHANGED
def set_widget_value(self, ipp_value):
t = type(self.widget)
if t == Gtk.SpinButton:
return self.widget.set_value (ipp_value)
elif t == Gtk.ComboBox or t == Gtk.ComboBoxText:
if ((self.ipp_type == str or self.ipp_type == IPPResolution)
and self.combobox_map is None):
model = self.widget.get_model ()
iter = model.get_iter_first ()
while (iter is not None and
self.ipp_type (model.get_value (iter, 0)) != ipp_value):
iter = model.iter_next (iter)
if iter:
self.widget.set_active_iter (iter)
else:
# It's an int.
if self.combobox_map:
index = self.combobox_map.index (ipp_value)
else:
index = ipp_value
return self.widget.set_active (index)
elif t == Gtk.CheckButton:
return self.widget.set_active (ipp_value)
else:
raise NotImplementedError(t, self.name)
def get_widget_value(self):
t = type(self.widget)
if t == Gtk.SpinButton:
# Ideally we would use self.widget.get_value() here, but
# it doesn't work if the value has been typed in and then
# the Apply button immediately clicked. To handle this,
# we use self.widget.get_text() and fall back to
# get_value() if the result cannot be interpreted as the
# type we expect.
try:
return self.ipp_type (self.widget.get_text ())
except ValueError:
# Can't convert result of get_text() to ipp_type.
return self.ipp_type (self.widget.get_value ())
elif t == Gtk.ComboBox:
if self.combobox_map:
return self.combobox_map[self.widget.get_active()]
return self.ipp_type (self.widget.get_active ())
elif t == Gtk.ComboBoxText:
s = self.widget.get_active_text ()
if s is None:
# If the widget is being re-initialised, there will be
# a changed signal emitted at the point where there
# are no entries to select from.
s = self.system_default
if self.combobox_map:
return self.combobox_map (s)
return self.ipp_type (s)
elif t == Gtk.CheckButton:
return self.ipp_type (self.widget.get_active ())
print(t, self.widget, self.ipp_type)
raise NotImplementedError
def get_current_value(self):
return self.get_widget_value ()
def is_changed(self):
if self.original_value is not None:
# There was a value set previously.
if self.state == self.STATE_RESET:
# It's been removed.
return True
if self.state == self.STATE_ADJUSTED:
if self.get_current_value () != self.original_value:
return True
return False
# The value is the same as before, and not reset.
return False
# There was no original value set.
if self.state == self.STATE_ADJUSTED:
# It's been adjusted.
return True
# It's been left alone, or possible adjusted and then reset.
return False
def reset(self):
self.set_widget_value (self.system_default)
self.state = self.STATE_RESET
self.button.set_sensitive (False)
def changed(self):
self.state = self.STATE_ADJUSTED
self.button.set_sensitive (True)
class OptionAlwaysShownSpecial(OptionAlwaysShown):
def __init__(self, name, ipp_type, system_default,
widget, button, combobox_map = None, use_supported = False,
special_choice = "System default"):
self.special_choice = special_choice
self.special_choice_shown = False
OptionAlwaysShown.__init__ (self, name, ipp_type, system_default,
widget, button,
combobox_map=combobox_map,
use_supported=use_supported)
def show_special_choice (self):
if self.special_choice_shown:
return
self.special_choice_shown = True
# Only works for ComboBox widgets
model = self.widget.get_model ()
iter = model.insert (0)
model.set_value (iter, 0, self.special_choice)
self.widget.set_active_iter (model.get_iter_first ())
def hide_special_choice (self):
if not self.special_choice_shown:
return
self.special_choice_shown = False
# Only works for ComboBox widgets
model = self.widget.get_model ()
model.remove (model.get_iter_first ())
def reinit(self, original_value, supported=None):
if original_value is not None:
self.hide_special_choice ()
else:
self.show_special_choice ()
OptionAlwaysShown.reinit (self, original_value, supported=supported)
def reset(self):
self.show_special_choice ()
OptionAlwaysShown.reset (self)
def changed(self):
OptionAlwaysShown.changed (self)
if self.widget.get_active () > 0:
self.hide_special_choice ()
class Option(OptionInterface):
conflicts = None
def __init__(self, name, value, supported, on_change):
self.name = name
self.value = value
self.supported = supported
self.on_change = on_change
self.is_new = False
label = name
if not label.endswith (':'):
label += ':'
self.label = Gtk.Label(label=label)
self.label.set_alignment(0.0, 0.5)
def get_current_value(self):
raise NotImplementedError
def is_changed(self):
return (self.is_new or
str (self.get_current_value()) != str (self.value))
def changed(self, widget, *args):
self.on_change(self)
# ---------------------------------------------------------------------------
class OptionSelectOne(Option):
def __init__(self, name, value, supported, on_change):
Option.__init__(self, name, value, supported, on_change)
self.selector = Gtk.ComboBoxText()
selected = None
for nr, choice in enumerate(supported):
self.selector.append_text(str(choice))
if str (value) == str (choice):
selected = nr
if selected is not None:
self.selector.set_active(selected)
else:
debugprint("Unknown value for %s: %s" % (name, value))
debugprint("Choices: %s" % (supported))
if len(supported) > 0:
debugprint("Selecting from choices:", supported[0])
self.selector.set_active(0)
self.selector.connect("changed", self.changed)
def get_current_value(self):
return self.selector.get_active_text()
# ---------------------------------------------------------------------------
class OptionSelectOneResolution(OptionSelectOne):
def __init__(self, name, value, supported, on_change):
self.UNITS_BY_VAL = { cups.IPP_RES_PER_INCH: "dpi",
cups.IPP_RES_PER_CM: "dpc" }
self.UNITS_DEFAULT = cups.IPP_RES_PER_INCH
self.UNITS_BY_STR = {}
for v, s in self.UNITS_BY_VAL.items ():
self.UNITS_BY_STR[s] = v
value = self.string (value)
supported = list(map (self.string, supported))
OptionSelectOne.__init__ (self, name, value, supported, on_change)
def string(self, value):
return "%sx%s%s" % (value[0], value[1],
self.UNITS_BY_VAL.get (value[2], ""))
def value(self, string):
matches = re.match (r"(\d+)\D+(\d+)(.*)", string).groups ()
return (int (matches[0]), int (matches[1]),
self.UNITS_BY_STR.get (matches[2], self.UNITS_DEFAULT))
def get_current_value(self):
return self.value (self.selector.get_active_text())
# ---------------------------------------------------------------------------
class OptionSelectOneNumber(OptionSelectOne):
def get_current_value(self):
return int(self.selector.get_active_text() or 0)
# ---------------------------------------------------------------------------
class OptionSelectMany(Option):
def __init__(self, name, value, supported, on_change):
Option.__init__(self, name, value, supported, on_change)
self.checkboxes = []
vbox = Gtk.VBox()
for s in supported:
checkbox = Gtk.CheckButton.new_with_label(s)
checkbox.set_active(s in value)
vbox.add(checkbox)
checkbox.connect("toggled", self.changed)
self.checkboxes.append(checkbox)
self.selector = vbox
def get_current_value(self):
return[s for s, chk in zip(self.supported, self.checkboxes)
if chk.get_active()]
# ---------------------------------------------------------------------------
class OptionNumeric(Option):
def __init__(self, name, value, supported, on_change):
self.is_float = (isinstance(supported, float) or
(isinstance(supported, tuple) and
isinstance(supported[0], float)))
if self.is_float:
digits = 2
else:
digits = 0
if not isinstance(supported, tuple):
supported = (0, supported)
Option.__init__(self, name, value, supported, on_change)
adj = Gtk.Adjustment(value=value, lower=supported[0],
upper=supported[1], step_increment=1.0,
page_increment=5.0, page_size=0.0)
self.selector = Gtk.SpinButton()
self.selector.set_adjustment(adj)
self.selector.set_digits(digits)
if not self.is_float:
self.selector.set_numeric(True)
self.selector.connect("changed", self.changed)
def get_current_value(self):
if self.is_float:
return self.selector.get_value()
return self.selector.get_value_as_int()
# ---------------------------------------------------------------------------
class OptionText(Option):
def __init__(self, name, value, supported, on_change):
Option.__init__(self, name, value, supported, on_change)
self.selector = Gtk.Entry()
self.selector.set_text(value)
self.selector.connect("changed", self.changed)
def get_current_value(self):
return self.selector.get_text()