Example #1
0
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)
Example #2
0
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()
Example #3
0
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)
Example #4
0
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
Example #5
0
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()
Example #6
0
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
Example #7
0
 class C(object):
     implements(I1)