class Base64CookieFile(object): implements(ICookieFile) def __init__(self, filename): self._filename = filename def get(self): if not os.path.exists(self._filename): raise CookieError("%s does not exist" % self._filename) cookiedata = open(self._filename).read() if not ':' in cookiedata: log.info("invalid cookie file") raise CookieError("%s is invalid" % self._filename) data = cookiedata.split(":", 1) try: return data[0], binascii.a2b_base64(data[1]) except binascii.Error: raise CookieError("invalid format") def clear(self): try: os.remove(self._filename) log.info("Removed cookie %s" % self._filename) except OSError as e: log.info("Could not remove file %s: %r" % (self._filename, e)) def store(self, username, password): if not username: raise CookieError("a username is required") try: fd = open(self._filename, "w") except IOError as e: raise CookieError("Could open file %s: %r" % (self._filename, e)) # obfuscate password to avoid it being easily identified when # editing file on screen. this is *NOT* encryption! fd.write("%s:%s" % (username, binascii.b2a_base64(password or ''))) fd.close() log.info("Saved cookie %s" % self._filename)
class SearchFilter(gtk.HBox): """ A base classed used by common search filters """ label = gobject.property(type=str, flags=(gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY)) gsignal('changed') gsignal('removed') implements(ISearchFilter) def __init__(self, label=''): self.__gobject_init__(label=label) self._label = label self._remove_button = None def _add_remove_button(self): self._remove_button = SearchFilterButton(stock=gtk.STOCK_REMOVE) self._remove_button.set_relief(gtk.RELIEF_NONE) self._remove_button.set_label_visible(False) self._remove_button.connect('clicked', self._on_remove_clicked) self._remove_button.show() self.pack_start(self._remove_button, False, False) def _on_remove_clicked(self, button): self.emit('removed') def do_set_property(self, pspec, value): if pspec.name == 'label': self._label = value else: raise AssertionError(pspec.name) def do_get_property(self, child, property_id, pspec): if pspec.name == 'label': return self._label else: raise AssertionError(pspec.name) def set_label(self, label): self._label = label def get_state(self): """ Implement this in a subclass """ raise NotImplementedError def get_title_label(self): raise NotImplementedError def get_mode_combo(self): raise NotImplementedError def get_description(self): """Returns a description of the search filter. @returns: a string describing the search filter. """ raise NotImplementedError def set_removable(self): if self._remove_button is None: self._add_remove_button()
class ProxyWidgetMixin(object): """This class is a mixin that provide a common interface for KiwiWidgets. Usually the Proxy class need to set and get data from the widgets. It also need a validation framework. :cvar allowed_data_types: A list of types which we are allowed to use in this class. """ implements(IProxyWidget) allowed_data_types = () # To be able to call the as/from_string without setting the data_type # property and still receiving a good warning. _converter = None def __init__(self): if not type(self.allowed_data_types) == tuple: raise TypeError("%s.allowed_data_types must be a tuple" % (self.allowed_data_types)) self._data_format = None self._converter_options = {} self._data_type = None # Properties def get_data_type(self): return self._data_type def set_data_type(self, data_type): """Set the data type for the widget :param data_type: can be None, a type object or a string with the name of the type object, so None, "<type 'str'>" or 'str' """ if data_type is None: return data_type elif data_type == '': return None # This may convert from string to type, # A type object will always be returned data_type = converter.check_supported(data_type) self._converter = converter.get_converter(data_type) self._data_type = self._converter.type.__name__ return self._converter.type.__name__ # Public API def set_data_format(self, format): self._data_format = format def set_options_for_datatype(self, datatype, **options): """Set some options to be passed to the datatype converter. Any additional parameter will be passed the the converter when converting an object to a string, for displaying in the widget. Note that the converter.as_string method should be able to handle such parameters. :param datatype: the datatype. """ if not options: raise ValueError self._converter_options[datatype] = options def read(self): """Get the content of the widget. The type of the return value :returns: None if the user input a invalid value :rtype: Must matche the data-type property. """ raise NotImplementedError def update(self, value): """ Update the content value of the widget. :param value: """ raise NotImplementedError # Private # FIXME: This should be public. All the callsites are outside this module def _as_string(self, data): """Convert a value to a string :param data: data to convert """ conv = self._converter if conv is None: conv = converter.get_converter(str) return conv.as_string(data, format=self._data_format, **self._converter_options.get(conv.type, {})) # FIXME: This should be public. All the callsites are outside this module def _from_string(self, data): """Convert a string to the data type of the widget This may raise a :class:`kiwi.datatypes.ValidationError` if conversion failed :param data: data to convert """ conv = self._converter if conv is None: conv = converter.get_converter(str) return conv.from_string(data)
class ValidatableProxyWidgetMixin(ProxyWidgetMixin): """Class used by some Kiwi Widgets that need to support mandatory input and validation features such as custom validation and data-type validation. Mandatory support provides a way to warn the user when input is necessary. The validatation feature provides a way to check the data entered and to display information about what is wrong. """ implements(IValidatableProxyWidget) def __init__(self, widget=None): ProxyWidgetMixin.__init__(self) # Inicial valid state is unkown (None), so that when _set_valid_state is # called for the first time, the signal gets emitted self._valid = None self._fade = FadeOut(self) self._fade.connect('color-changed', self._on_fadeout__color_changed) self.connect('notify::mandatory', self._on_notify__mandatory) self.connect('notify::sensitive', self._on_notify__sensitive) self.connect('notify::visible', self._on_notify__visible) # Override in subclass def update_background(self, color): "Implement in subclass" def get_background(self): "Implement in subclass" def set_pixbuf(self, pixbuf): "Implement in subclass" def set_tooltip(self, text): "Implement in subclass" # Public API def is_valid(self): """ Verify the widget state. :returns: True if the widget is in validated state """ return self._valid def validate(self, force=False): """Checks if the data is valid. Validates data-type and custom validation. :param force: if True, force validation :returns: validated data or ValueUnset if it failed """ # If we're not visible or sensitive return a blank value, except # when forcing the validation if not force and (not self.get_property('visible') or not self.get_property('sensitive')): self._set_pixbuf(None) return ValueUnset try: data = self.read() log.debug('Read %r for %s' % (data, self.model_attribute)) self.validate_value(data) # check if we should draw the mandatory icon # this need to be done before any data conversion because we # we don't want to end drawing two icons if self.mandatory and (data is None or data == '' or data == ValueUnset): self.set_blank() return ValueUnset else: # The widgets themselves have now valid the data # Next step is to call the application specificed # checks, which are found in the view. if data is not None and data is not ValueUnset: # this signal calls the on_widgetname__validate method # of the view class and gets the exception (if any). error = self.emit("validate", data) if error: raise error self.set_valid() return data except ValidationError, e: self.set_invalid(str(e)) return ValueUnset
class ComboEntry(gtk.VBox): implements(IEasyCombo) gsignal('changed') gsignal('activate') def __init__(self, entry=None): """ Create a new ComboEntry object. @param entry: a gtk.Entry subclass to use """ gtk.VBox.__init__(self) self._popping_down = False if not entry: entry = KiwiEntry() self.hbox = gtk.HBox() self.pack_start(gtk.EventBox()) self.pack_start(self.hbox, expand=False) self.pack_start(gtk.EventBox()) self.mode = ComboMode.UNKNOWN self.entry = entry self.entry.connect('activate', self._on_entry__activate) self.entry.connect('changed', self._on_entry__changed) self.entry.connect('scroll-event', self._on_entry__scroll_event) self.entry.connect('key-press-event', self._on_entry__key_press_event) self.entry.connect('focus-out-event', self._on_entry__focus_out_event) self.hbox.pack_start(self.entry, True, True) self.hbox.show_all() self._button = gtk.ToggleButton() self._button.connect('scroll-event', self._on_entry__scroll_event) self._button.connect('toggled', self._on_button__toggled) self._button.set_focus_on_click(False) self.hbox.pack_end(self._button, False, False) self._button.show() arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE) self._button.add(arrow) arrow.show() self._popup = _ComboEntryPopup(self) self._popup.connect('text-selected', self._on_popup__text_selected) self._popup.connect('hide', self._on_popup__hide) self._popup.set_size_request(-1, 24) completion = KiwiEntryCompletion() completion.set_popup_window(self._popup) completion.set_treeview(self._popup._treeview) self.entry.set_completion(completion) self.set_model(completion.get_model()) # Virtual methods def do_grab_focus(self): self.entry.grab_focus() # Callbacks def _on_entry__focus_out_event(self, widget, event): # The popup window should be hidden if the entry loses the focus, # unless we have a combo entry and the user clicked the toggle button # to show the popup window if not self._button.get_active(): self.popdown() def _on_entry_completion__match_selected(self, completion, model, iter): # the iter we receive is specific to the tree model filter used # In the entry completion, convert it to an iter in the real model if isinstance(model, gtk.TreeModelFilter): iter = model.convert_iter_to_child_iter(iter) self.set_active_iter(iter) def _on_entry__activate(self, entry): self.emit('activate') def _on_entry__changed(self, entry): self.emit('changed') def _on_entry__scroll_event(self, entry, event): model = self.get_model() if not len(model): return treeiter = self._popup.get_selected_iter() # If nothing is selected, select the first one if not treeiter: self.set_active_iter(model[0].iter) return curr = model[treeiter].path[0] # Scroll up, select the previous item if event.direction == gdk.SCROLL_UP: curr -= 1 if curr >= 0: self.set_active_iter(model[curr].iter) # Scroll down, select the next item elif event.direction == gdk.SCROLL_DOWN: curr += 1 if curr < len(model): self.set_active_iter(model[curr].iter) def _on_entry__key_press_event(self, entry, event): """ Mimics Combobox behavior Alt+Down: Open popup """ keyval, state = event.keyval, event.state state &= gtk.accelerator_get_default_mod_mask() if ((keyval == keysyms.Down or keyval == keysyms.KP_Down) and state == gdk.MOD1_MASK): self.popup() return True def _on_popup__hide(self, popup): self._popping_down = True self._button.set_active(False) self._popping_down = False def _on_popup__text_selected(self, popup, text): self.set_text(text) popup.popdown() self.entry.grab_focus() self.entry.set_position(len(self.entry.get_text())) self.emit('changed') def _on_button__toggled(self, button): if self._popping_down: return self.popup() # Private def _update(self): model = self._model if not len(model): return iter = self._popup.get_selected_iter() if not iter: iter = model[0].iter self._popup.set_selected_iter(iter) # Public API def clicked(self): pass def popup(self): """ Show the popup window """ self._popup.popup(self.entry.get_text()) def popdown(self): """ Hide the popup window """ self._popup.popdown() def set_text(self, text): """ Sets the text. @param text: """ self.entry.set_text(text) self.emit('changed') def get_text(self): """ Gets the current text. @returns: the text. """ return self.entry.get_text() def set_model(self, model): """ Set the tree model to model @param model: new model @type model: gtk.TreeModel """ self._model = model self._popup.set_model(model) completion = self.entry.get_completion() completion.connect('match-selected', self._on_entry_completion__match_selected) completion.set_model(model) self._update() def get_model(self): """ Gets our model. @returns: model @rtype: gtk.TreeModel """ return self._model def set_active_iter(self, iter): """ Set the iter selected. @param iter: iter to select @type iter: gtk.TreeIter """ self._popup.set_selected_iter(iter) text = self._model[iter][0] if text is not None: self.set_text(text) def get_active_iter(self): """ Gets the selected iter. @returns: iter selected. @rtype: gtk.TreeIter """ return self._popup.get_selected_iter() def get_mode(self): return self.mode def set_label_text(self, text): self._popup.set_label_text(text) def set_active(self, rowno): self.set_active_iter(self._model[rowno].iter) def set_details_callback(self, callable): """Display some details as a second line on each entry @param callable: a callable that expects an object and returns a string """ self._popup.set_details_callback(callable) # IEasyCombo interface def clear(self): """Removes all items from list""" self._model.clear() self.entry.set_text("") def prefill(self, itemdata, sort=False): """ See L{kiwi.interfaces.IEasyCombo.prefill} """ self._model.clear() self.entry.prefill(itemdata, sort) self.mode = self.entry.get_mode() def select_item_by_data(self, data): """ See L{kiwi.interfaces.IEasyCombo.select_item_by_data} """ treeiter = self.entry.get_iter_by_data(data) self.set_active_iter(treeiter) def select_item_by_label(self, text): """ See L{kiwi.interfaces.IEasyCombo.select_item_by_label} """ treeiter = self.entry.get_iter_by_label(text) self.set_active_iter(treeiter) def select_item_by_position(self, position): """ See L{kiwi.interfaces.IEasyCombo.select_item_by_position} """ row = self._model[position] self.set_active_iter(row.iter) def get_selected(self): """ See L{kiwi.interfaces.IEasyCombo.get_selected} """ treeiter = self.get_active_iter() if treeiter: return self.entry.get_selected_by_iter(treeiter) def get_selected_label(self): """ See L{kiwi.interfaces.IEasyCombo.get_selected_label} """ treeiter = self.get_active_iter() if treeiter: return self.entry.get_selected_label(treeiter) def get_selected_data(self): """ See L{kiwi.interfaces.IEasyCombo.get_selected_data} """ treeiter = self.get_active_iter() if treeiter: return self.entry.get_selected_data(treeiter) def select(self, obj): """ See L{kiwi.interfaces.IEasyCombo.select} """ try: treeiter = self.entry.get_iter_from_obj(obj) except KeyError: log.warn("%s does not contain a %r object" % ( self.__class__.__name__, obj)) return self.set_active_iter(treeiter) def append_item(self, label, data=None): """ See L{kiwi.interfaces.IEasyCombo.append_item} """ if not isinstance(label, basestring): raise TypeError("label must be string, found %s" % label) if self.mode == ComboMode.UNKNOWN: if data is not None: self.mode = ComboMode.DATA else: self.mode = ComboMode.STRING model = self._model if self.mode == ComboMode.STRING: if data is not None: raise TypeError("data can not be specified in string mode") model.append((label, None)) elif self.mode == ComboMode.DATA: if data is None: raise TypeError("data must be specified in string mode") model.append((label, data)) else: raise AssertionError def get_model_strings(self): """ See L{kiwi.interfaces.IEasyCombo.get_model_strings} """ return [row[ComboColumn.LABEL] for row in self._model] def get_model_items(self): """ See L{kiwi.interfaces.IEasyCombo.get_model_items} """ if self.mode != ComboMode.DATA: raise TypeError("get_model_items can only be used in data mode") model = self._model items = {} for row in model: items[row[ComboColumn.LABEL]] = row[ComboColumn.DATA] return items # IconEntry def set_pixbuf(self, pixbuf): self.entry.set_pixbuf(pixbuf) def update_background(self, color): self.entry.update_background(color) def get_background(self): return self.entry.get_background()
class _EasyComboBoxHelper(object): implements(IEasyCombo) def __init__(self, combobox): """Call this constructor after the Combo one""" if not isinstance(combobox, (gtk.ComboBox, ComboEntry)): raise TypeError( "combo needs to be a gtk.ComboBox or ComboEntry instance") self._combobox = combobox model = gtk.ListStore(str, object) self._combobox.set_model(model) self.mode = ComboMode.UNKNOWN def get_mode(self): return self.mode def set_mode(self, mode): if self.mode != ComboMode.UNKNOWN: raise AssertionError self.mode = mode def clear(self): """Removes all items from list""" model = self._combobox.get_model() model.clear() def prefill(self, itemdata, sort=False): if not isinstance(itemdata, (list, tuple)): raise TypeError("'data' parameter must be a list or tuple of item " "descriptions, found %s" % (type(itemdata), )) self.clear() if len(itemdata) == 0: return if self.mode == ComboMode.UNKNOWN: first = itemdata[0] if isinstance(first, basestring): self.set_mode(ComboMode.STRING) elif isinstance(first, (tuple, list)): self.set_mode(ComboMode.DATA) else: raise TypeError("Could not determine type, items must " "be strings or tuple/list") mode = self.mode if mode not in (ComboMode.STRING, ComboMode.DATA): raise TypeError("Incorrect format for itemdata; see " "docstring for more information") model = self._combobox.get_model() values = set() if mode == ComboMode.STRING: if sort: itemdata.sort() for item in itemdata: if item in values: raise KeyError("Tried to insert duplicate value " "%s into Combo!" % (item, )) values.add(item) model.append((item, None)) elif mode == ComboMode.DATA: if sort: itemdata.sort(lambda x, y: cmp(x[0], y[0])) for item in itemdata: text, data = item orig = text count = 1 while text in values: text = orig + ' (%d)' % count count += 1 values.add(text) model.append((text, data)) def append_item(self, label, data=None): """ Adds a single item to the Combo. Takes: - label: a string with the text to be added - data: the data to be associated with that item """ if not isinstance(label, basestring): raise TypeError("label must be string, found %s" % (label, )) if self.mode == ComboMode.UNKNOWN: if data is not None: self.set_mode(ComboMode.DATA) else: self.set_mode(ComboMode.STRING) model = self._combobox.get_model() if self.mode == ComboMode.STRING: if data is not None: raise TypeError("data can not be specified in string mode") model.append((label, None)) elif self.mode == ComboMode.DATA: if data is None: raise TypeError("data must be specified in string mode") model.append((label, data)) else: raise AssertionError def insert_item(self, position, label, data=None): """ Inserts a single item at a position to the Combo. :param position: position to insert the item at :param label: a string with the text to be added :param data: the data to be associated with that item """ if not isinstance(label, basestring): raise TypeError("label must be string, found %s" % (label, )) if self.mode == ComboMode.UNKNOWN: if data is not None: self.set_mode(ComboMode.DATA) else: self.set_mode(ComboMode.STRING) if position < 0: position = 0 model = self._combobox.get_model() if self.mode == ComboMode.STRING: if data is not None: raise TypeError("data can not be specified in string mode") model.insert(position, (label, None)) elif self.mode == ComboMode.DATA: if data is None: raise TypeError("data must be specified in string mode") model.insert(position, (label, data)) else: raise AssertionError def select(self, data): if self.mode == ComboMode.STRING: self.select_item_by_label(data) elif self.mode == ComboMode.DATA: self.select_item_by_data(data) else: # XXX: When setting the datatype to non string, automatically go to # data mode raise TypeError("unknown ComboBox mode. Did you call prefill?") def select_item_by_position(self, pos): self._combobox.set_active(pos) def select_item_by_label(self, label): model = self._combobox.get_model() for row in model: if row[ComboColumn.LABEL] == label: self._combobox.set_active_iter(row.iter) break else: raise KeyError("No item correspond to label %r in the combo %s" % (label, self._combobox.get_name())) def select_item_by_data(self, data): if self.mode != ComboMode.DATA: raise TypeError( "select_item_by_data can only be used in data mode") model = self._combobox.get_model() for row in model: if row[ComboColumn.DATA] == data: self._combobox.set_active_iter(row.iter) break else: raise KeyError("No item correspond to data %r in the combo %s" % (data, self._combobox.get_name())) def get_model_strings(self): return [row[ComboColumn.LABEL] for row in self._combobox.get_model()] def get_model_items(self): if self.mode != ComboMode.DATA: raise TypeError("get_model_items can only be used in data mode") model = self._combobox.get_model() items = {} for row in model: items[row[ComboColumn.LABEL]] = row[ComboColumn.DATA] return items def get_selected_label(self): iter = self._combobox.get_active_iter() if not iter: return model = self._combobox.get_model() return model[iter][ComboColumn.LABEL] def get_selected_data(self): if self.mode != ComboMode.DATA: raise TypeError("get_selected_data can only be used in data mode") iter = self._combobox.get_active_iter() if not iter: return model = self._combobox.get_model() return model[iter][ComboColumn.DATA] def get_selected(self): mode = self.mode if mode == ComboMode.STRING: return self.get_selected_label() elif mode == ComboMode.DATA: return self.get_selected_data() return None
class C(object): implements(I1)