def __init__(self, selected_callback, cancel_callback):
        self._selected_callback = selected_callback
        self._cancel_callback = cancel_callback
        self.total_contracts = 0
        self.contract_selection_xml = gtk.glade.XML(CONTRACT_SELECTION_GLADE)
        self.contract_selection_win = self.contract_selection_xml.get_widget(
            "contract_selection_window")
        self.subscribe_button = self.contract_selection_xml.get_widget(
            'subscribe_button')
        self.edit_quantity_label = self.contract_selection_xml.get_widget(
            'edit_quantity_label')

        self.contract_selection_treeview = \
                self.contract_selection_xml.get_widget(
                        "contract_selection_treeview")
        self.contract_selection_treeview.get_selection().connect(
            "changed", self._on_contract_selection)

        self.subscription_name_label = self.contract_selection_xml.get_widget(
            "subscription_name_label")

        self.total_contracts_label = self.contract_selection_xml.get_widget(
            "total_contracts_label")

        self.contract_selection_xml.signal_autoconnect({
            "on_cancel_button_clicked":
            self._cancel_button_clicked,
            "on_subscribe_button_clicked":
            self._subscribe_button_clicked,
        })

        self.model = MappedListStore(self.get_type_map())
        self.contract_selection_treeview.set_model(self.model)
    def __init__(self, selected_callback, cancel_callback):
        self._selected_callback = selected_callback
        self._cancel_callback = cancel_callback
        self.total_contracts = 0
        self.contract_selection_xml = gtk.glade.XML(CONTRACT_SELECTION_GLADE)
        self.contract_selection_win = self.contract_selection_xml.get_widget(
            "contract_selection_window")
        self.subscribe_button = self.contract_selection_xml.get_widget('subscribe_button')
        self.edit_quantity_label = self.contract_selection_xml.get_widget('edit_quantity_label')

        self.contract_selection_treeview = \
                self.contract_selection_xml.get_widget(
                        "contract_selection_treeview")
        self.contract_selection_treeview.get_selection().connect("changed",
            self._on_contract_selection)

        self.subscription_name_label = self.contract_selection_xml.get_widget(
            "subscription_name_label")

        self.total_contracts_label = self.contract_selection_xml.get_widget(
            "total_contracts_label")

        self.contract_selection_xml.signal_autoconnect({
            "on_cancel_button_clicked": self._cancel_button_clicked,
            "on_subscribe_button_clicked": self._subscribe_button_clicked,
        })

        self.model = MappedListStore(self.get_type_map())
        self.contract_selection_treeview.set_model(self.model)
Ejemplo n.º 3
0
    def __init__(self, selected_callback, cancel_callback):
        super(ContractSelectionWindow, self).__init__()
        self._selected_callback = selected_callback
        self._cancel_callback = cancel_callback
        self.total_contracts = 0

        self.contract_selection_treeview.get_selection().connect("changed",
            self._on_contract_selection)

        self.subscription_name_label.set_line_wrap(True)

        callbacks = {"on_cancel_button_clicked": self._cancel_button_clicked,
                     "size-allocate": lambda label, size: label.set_size_request(size.width - 1, -1),
                     "on_subscribe_button_clicked": self._subscribe_button_clicked}
        self.connect_signals(callbacks)

        self.model = MappedListStore(self.get_type_map())
        self.contract_selection_treeview.set_model(self.model)
class ContractSelectionWindow(object):

    def __init__(self, selected_callback, cancel_callback):
        self._selected_callback = selected_callback
        self._cancel_callback = cancel_callback
        self.total_contracts = 0
        self.contract_selection_xml = gtk.glade.XML(CONTRACT_SELECTION_GLADE)
        self.contract_selection_win = self.contract_selection_xml.get_widget(
            "contract_selection_window")
        self.subscribe_button = self.contract_selection_xml.get_widget('subscribe_button')
        self.edit_quantity_label = self.contract_selection_xml.get_widget('edit_quantity_label')

        self.contract_selection_treeview = \
                self.contract_selection_xml.get_widget(
                        "contract_selection_treeview")
        self.contract_selection_treeview.get_selection().connect("changed",
            self._on_contract_selection)

        self.subscription_name_label = self.contract_selection_xml.get_widget(
            "subscription_name_label")

        self.total_contracts_label = self.contract_selection_xml.get_widget(
            "total_contracts_label")

        self.contract_selection_xml.signal_autoconnect({
            "on_cancel_button_clicked": self._cancel_button_clicked,
            "on_subscribe_button_clicked": self._subscribe_button_clicked,
        })

        self.model = MappedListStore(self.get_type_map())
        self.contract_selection_treeview.set_model(self.model)

    def get_type_map(self):
        return {
                'contract_number': str,
                'consumed_fraction': str,
                'start_date': gobject.TYPE_PYOBJECT,
                'end_date': gobject.TYPE_PYOBJECT,
                'default_quantity': int,
                'product_name': str,
                'pool': gobject.TYPE_PYOBJECT,
                'is_virt_only': bool,
                'multi_entitlement': bool,
                'quantity_available': int,
                'quantity_increment': int,
                }

    def show(self):
        self.populate_treeview()
        self.contract_selection_win.show_all()

    def destroy(self):
        self.contract_selection_win.destroy()

    def populate_treeview(self):
        renderer = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_("Contract"), renderer,
                text=self.model['contract_number'])
        column.set_expand(True)
        column.set_sort_column_id(self.model['contract_number'])
        self.model.set_sort_func(self.model['contract_number'], self._sort_text, None)
        self.contract_selection_treeview.append_column(column)

        column = widgets.MachineTypeColumn(self.model['is_virt_only'])
        column.set_sort_column_id(self.model['is_virt_only'])
        self.model.set_sort_func(self.model['is_virt_only'], self._sort_machine_type, column)
        self.contract_selection_treeview.append_column(column)

        renderer = gtk.CellRendererText()
        renderer.set_property("xalign", 0.5)
        column = gtk.TreeViewColumn(_("Used / Total"), renderer,
                text=self.model['consumed_fraction'])
        self.contract_selection_treeview.append_column(column)

        renderer = widgets.CellRendererDate()
        column = gtk.TreeViewColumn(_("Start Date"), renderer, date=self.model['start_date'])
        column.set_sort_column_id(self.model['start_date'])
        self.model.set_sort_func(self.model['start_date'], self._sort_date, None)
        self.contract_selection_treeview.append_column(column)

        renderer = widgets.CellRendererDate()
        column = gtk.TreeViewColumn(_("End Date"), renderer, date=self.model['end_date'])
        column.set_sort_column_id(self.model['end_date'])
        self.model.set_sort_func(self.model['end_date'], self._sort_date, None)
        self.contract_selection_treeview.append_column(column)

        column = widgets.QuantitySelectionColumn(_("Quantity"), self.model,
                self.model['default_quantity'],
                self.model['multi_entitlement'],
                self.model['quantity_available'],
                self.model['quantity_increment'])
        self.contract_selection_treeview.append_column(column)

        self.edit_quantity_label.set_label(column.get_column_legend_text())

    def add_pool(self, pool, default_quantity_value):
        self.total_contracts += 1
        self.total_contracts_label.set_text(str(self.total_contracts))
        self.subscription_name_label.set_text(pool['productName'])

        # Use unlimited for -1 quanities
        quantity = pool['quantity']
        if quantity < 0:
            quantity = _('Unlimited')
            quantity_available = -1
        else:
            quantity_available = int(pool['quantity']) - int(pool['consumed'])

        # cap the default selected quantity at the max available
        # for that pool. See #855257. Watch out for quantity_available
        # being -1 (unlimited).
        if default_quantity_value > quantity_available and quantity_available >= 0:
            default_quantity_value = quantity_available

        quantity_increment = 1
        if 'calculatedAttributes' in pool:
            calculated_attrs = pool['calculatedAttributes']

            if 'quantity_increment' in calculated_attrs:
                quantity_increment = int(calculated_attrs['quantity_increment'])

        self.model.add_map({
            'contract_number': pool['contractNumber'],
            'consumed_fraction': "%s / %s" % (pool['consumed'], quantity),
            'start_date': isodate.parse_date(pool['startDate']),
            'end_date': isodate.parse_date(pool['endDate']),
            'default_quantity': default_quantity_value,
            'product_name': pool['productName'],
            'pool': pool,
            'is_virt_only': PoolWrapper(pool).is_virt_only(),
            'multi_entitlement': allows_multi_entitlement(pool),
            'quantity_available': quantity_available,
            'quantity_increment': quantity_increment,
            })

    def set_parent_window(self, window):
        self.contract_selection_win.set_transient_for(window)

    def _cancel_button_clicked(self, button):
        self._cancel_callback()

    def _subscribe_button_clicked(self, button):
        selection = self.contract_selection_treeview.get_selection()
        model, iter = selection.get_selected()
        if iter is not None:
            pool, quantity = model.get(iter, model['pool'], model['default_quantity'])
            self._selected_callback(pool, quantity)

    def _on_contract_selection(self, widget):
        model, tree_iter = widget.get_selected()

        enabled = True
        if not tree_iter:
            enabled = False

        self.subscribe_button.set_sensitive(enabled)

    def _sort_text(self, model, row1, row2, data):
        sort_column, sort_type = model.get_sort_column_id()
        str1 = model.get_value(row1, sort_column)
        str2 = model.get_value(row2, sort_column)
        return cmp(str1, str2)

    def _sort_machine_type(self, model, row1, row2, col):
        # Machine type is actually a boolean denoting whether the type is
        # virtual or not.  We do not want to sort on the boolean value.
        # Instead we want to sort on the text value that is going to get
        # displayed for the boolean value.
        sort_column, sort_type = model.get_sort_column_id()
        results = []
        for row in [row1, row2]:
            _bool = model.get_value(row, sort_column)
            if _bool is None:
                text = col._get_none_text()
            elif bool(_bool):
                text = col._get_true_text()
            else:
                text = col._get_false_text()
            results.append(text)
        return cmp(results[0], results[1])

    def _sort_date(self, model, row1, row2, data):
        """
        Used for sorting dates that could be None.
        """
        sort_column, sort_type = model.get_sort_column_id()
        date1 = model.get_value(row1, sort_column) \
            or datetime.date(datetime.MINYEAR, 1, 1)
        date2 = model.get_value(row2, sort_column) \
            or datetime.date(datetime.MINYEAR, 1, 1)
        epoch1 = time.mktime(date1.timetuple())
        epoch2 = time.mktime(date2.timetuple())
        return cmp(epoch1, epoch2)
Ejemplo n.º 5
0
    def __init__(self, backend, parent):
        super(RepositoriesDialog, self).__init__('repositories.glade')

        # Set up dynamic elements
        self.overrides_treeview = gtk.TreeView()
        # Add at-spi because we no longer create this widget from glade
        self.overrides_treeview.get_accessible().set_name("Repository View")
        self.no_repos_label, self.no_repos_label_viewport = widgets.get_scrollable_label()
        self.widget_switcher = widgets.WidgetSwitcher(self.scrolledwindow,
                self.no_repos_label_viewport, self.overrides_treeview)

        # We require the backend here so that we can always use its version
        # of Overrides which will guarantee that the CP UEPConnection is up
        # to date.
        # FIXME: We really shouldn't have to worry about our connection info
        #        changing out from under us.
        self.backend = backend
        self.identity = require(IDENTITY)
        self.ent_dir = require(ENT_DIR)

        self.glade.signal_autoconnect({
                "on_dialog_delete_event": self._on_close,
                "on_close_button_clicked": self._on_close,
                "on_reset_button_clicked": self._on_reset_repo,
        })

        self.overrides_store = MappedListStore({
            "repo_id": str,
            "enabled": bool,
            "modified": bool,
            "modified-icon": gtk.gdk.Pixbuf,
            "name": str,
            "baseurl": str,
            "gpgcheck": bool,
            "gpgcheck_modified": bool,
            "repo_data": object,
            "override_data": object
        })

        self.other_overrides = OverridesTable(self.other_overrides_view)

        # Change the background color of the no_repos_label_container to the same color
        # as the label's base color. The event container allows us to change the color.
        label_base_color = self.no_repos_label.style.base[gtk.STATE_NORMAL]
        self.no_repos_label_viewport.modify_bg(gtk.STATE_NORMAL, label_base_color)

        self.overrides_treeview.set_model(self.overrides_store)

        self.modified_icon = self.overrides_treeview.render_icon(gtk.STOCK_APPLY,
                                                                 gtk.ICON_SIZE_MENU)

        sortable_cols = []
        enabled_col = CheckBoxColumn(_("Enabled"), self.overrides_store, 'enabled',
            self._on_enable_repo_toggle)
        self.overrides_treeview.append_column(enabled_col)

        gpgcheck_col = CheckBoxColumn(_("Gpgcheck"), self.overrides_store, 'gpgcheck',
            self._on_gpgcheck_toggle_changed)
        self.overrides_treeview.append_column(gpgcheck_col)

        repo_id_col = TextTreeViewColumn(self.overrides_store, _("Repository ID"), 'repo_id',
                                         expand=True)
        self.overrides_treeview.append_column(repo_id_col)
        sortable_cols.append((repo_id_col, 'text', 'repo_id'))

        modified_col = gtk.TreeViewColumn(_("Modified"), gtk.CellRendererPixbuf(),
                                          pixbuf=self.overrides_store['modified-icon'])
        self.overrides_treeview.append_column(modified_col)
        sortable_cols.append((modified_col, 'text', 'modified'))

        self.set_sorts(self.overrides_store, sortable_cols)

        self.overrides_treeview.get_selection().connect('changed', self._on_selection)
        self.overrides_treeview.set_rules_hint(True)

        self.main_window.set_transient_for(parent)
Ejemplo n.º 6
0
class RepositoriesDialog(widgets.GladeWidget, HasSortableWidget):
    """
    GTK dialog for managing repositories and their overrides.
    """
    widget_names = ['main_window', 'reset_button', 'close_button',
                    'name_text', 'baseurl_text', 'scrolledwindow',
                    'other_overrides_view']

    ENTS_PROVIDE_NO_REPOS = _("Attached subscriptions do not provide any repositories.")
    NO_ATTACHED_SUBS = _("No repositories are available without an attached subscription.")

    def __init__(self, backend, parent):
        super(RepositoriesDialog, self).__init__('repositories.glade')

        # Set up dynamic elements
        self.overrides_treeview = gtk.TreeView()
        # Add at-spi because we no longer create this widget from glade
        self.overrides_treeview.get_accessible().set_name("Repository View")
        self.no_repos_label, self.no_repos_label_viewport = widgets.get_scrollable_label()
        self.widget_switcher = widgets.WidgetSwitcher(self.scrolledwindow,
                self.no_repos_label_viewport, self.overrides_treeview)

        # We require the backend here so that we can always use its version
        # of Overrides which will guarantee that the CP UEPConnection is up
        # to date.
        # FIXME: We really shouldn't have to worry about our connection info
        #        changing out from under us.
        self.backend = backend
        self.identity = require(IDENTITY)
        self.ent_dir = require(ENT_DIR)

        self.glade.signal_autoconnect({
                "on_dialog_delete_event": self._on_close,
                "on_close_button_clicked": self._on_close,
                "on_reset_button_clicked": self._on_reset_repo,
        })

        self.overrides_store = MappedListStore({
            "repo_id": str,
            "enabled": bool,
            "modified": bool,
            "modified-icon": gtk.gdk.Pixbuf,
            "name": str,
            "baseurl": str,
            "gpgcheck": bool,
            "gpgcheck_modified": bool,
            "repo_data": object,
            "override_data": object
        })

        self.other_overrides = OverridesTable(self.other_overrides_view)

        # Change the background color of the no_repos_label_container to the same color
        # as the label's base color. The event container allows us to change the color.
        label_base_color = self.no_repos_label.style.base[gtk.STATE_NORMAL]
        self.no_repos_label_viewport.modify_bg(gtk.STATE_NORMAL, label_base_color)

        self.overrides_treeview.set_model(self.overrides_store)

        self.modified_icon = self.overrides_treeview.render_icon(gtk.STOCK_APPLY,
                                                                 gtk.ICON_SIZE_MENU)

        sortable_cols = []
        enabled_col = CheckBoxColumn(_("Enabled"), self.overrides_store, 'enabled',
            self._on_enable_repo_toggle)
        self.overrides_treeview.append_column(enabled_col)

        gpgcheck_col = CheckBoxColumn(_("Gpgcheck"), self.overrides_store, 'gpgcheck',
            self._on_gpgcheck_toggle_changed)
        self.overrides_treeview.append_column(gpgcheck_col)

        repo_id_col = TextTreeViewColumn(self.overrides_store, _("Repository ID"), 'repo_id',
                                         expand=True)
        self.overrides_treeview.append_column(repo_id_col)
        sortable_cols.append((repo_id_col, 'text', 'repo_id'))

        modified_col = gtk.TreeViewColumn(_("Modified"), gtk.CellRendererPixbuf(),
                                          pixbuf=self.overrides_store['modified-icon'])
        self.overrides_treeview.append_column(modified_col)
        sortable_cols.append((modified_col, 'text', 'modified'))

        self.set_sorts(self.overrides_store, sortable_cols)

        self.overrides_treeview.get_selection().connect('changed', self._on_selection)
        self.overrides_treeview.set_rules_hint(True)

        self.main_window.set_transient_for(parent)

    def hide(self):
        self.main_window.hide()

    def show(self):
        self._load_data()
        self.main_window.present()

    def _load_data(self):
        # pull the latest overrides from the cache which will be the ones from the server.
        current_overrides = self.backend.overrides.get_overrides(self.identity.uuid) or []
        self._refresh(current_overrides)
        # By default sort by repo_id
        self.overrides_store.set_sort_column_id(0, gtk.SORT_ASCENDING)

    def _refresh(self, current_overrides, repo_id_to_select=None):
        overrides_per_repo = {}

        for override in current_overrides:
            repo_id = override.repo_id
            overrides_per_repo.setdefault(repo_id, {})
            overrides_per_repo[repo_id][override.name] = override.value

        self.overrides_store.clear()
        self.other_overrides.clear()

        current_repos = self.backend.overrides.repo_lib.get_repos(apply_overrides=False)
        if (current_repos):
            self.widget_switcher.set_active(1)
        else:
            ent_count = len(self.ent_dir.list_valid())
            no_repos_message = self.ENTS_PROVIDE_NO_REPOS
            if ent_count == 0:
                no_repos_message = self.NO_ATTACHED_SUBS

            self.no_repos_label.set_markup("<b>%s</b>" % no_repos_message)
            self.widget_switcher.set_active(0)

        # Fetch the repositories from repolib without any overrides applied.
        # We do this so that we can tell if anything has been modified by
        # overrides.
        for repo in current_repos:
            overrides = overrides_per_repo.get(repo.id, None)
            modified = not overrides is None
            enabled = self._get_boolean(self._get_model_value(repo, overrides, 'enabled')[0])
            gpgcheck, gpgcheck_modified = self._get_model_value(repo, overrides, 'gpgcheck')
            gpgcheck = self._get_boolean(gpgcheck)
            self.overrides_store.add_map({
                'enabled': bool(int(enabled)),
                'repo_id': repo.id,
                'modified': modified,
                'modified-icon': self._get_modified_icon(modified),
                'name': repo['name'],
                'baseurl': repo['baseurl'],
                'gpgcheck': gpgcheck,
                'gpgcheck_modified': gpgcheck_modified,
                'repo_data': repo,
                'override_data': overrides
            })

        first_row_iter = self.overrides_store.get_iter_first()
        if not first_row_iter:
            self.reset_button.set_sensitive(False)
        elif repo_id_to_select:
            self._select_by_repo_id(repo_id_to_select)
        else:
            self.overrides_treeview.get_selection().select_iter(first_row_iter)

    def _get_modified_icon(self, modified):
        icon = None
        if modified:
            icon = self.modified_icon
        return icon

    def _get_selected_repo_id(self):
        selected = None
        override_selection = SelectionWrapper(self.overrides_treeview.get_selection(),
                                              self.overrides_store)
        if override_selection.is_valid():
            selected = override_selection['repo_id']
        return selected

    def _select_by_repo_id(self, repo_id):
        repo_data = (repo_id, self.overrides_store['repo_id'])
        self.overrides_store.foreach(self._select_repo_row, repo_data)

    def _select_repo_row(self, model, path, tree_iter, repo_data_tuple):
        """
        Passed to model's foreach method to select the row if the repo_id
        matches. Returning True tells foreach to stop processing rows.
        """
        repo_id, check_idx = repo_data_tuple
        row_repo_id = model.get_value(tree_iter, check_idx)
        if repo_id == row_repo_id:
            self.overrides_treeview.get_selection().select_iter(tree_iter)
            return True
        return False

    def _get_boolean(self, override_value):
        # An override value might come in as an int or a boolean string.
        # Try our best to convert it, and default to 0.
        try:
            val = int(override_value)
        except ValueError:
            val = 0
            if override_value is not None and override_value.upper() == "TRUE":
                val = 1
        return bool(val)

    def _get_model_value(self, repo, overrides, property_name):
        if not overrides or not property_name in overrides:
            return (repo[property_name], False)
        return (overrides[property_name], True)

    def _on_reset_repo(self, button):
        selection = SelectionWrapper(self.overrides_treeview.get_selection(),
                                     self.overrides_store)

        if not selection.is_valid():
            return

        confirm = YesNoDialog(_("Are you sure you want to remove all overrides for <b>%s</b>?") % selection['repo_id'],
                                 self._get_dialog_widget(), _("Confirm Remove All Overrides"))
        confirm.connect("response", self._on_reset_repo_response)

    def _on_reset_repo_response(self, dialog, response):
        if not response:
            return

        selection = SelectionWrapper(self.overrides_treeview.get_selection(),
                                     self.overrides_store)

        if not selection.is_valid():
            return

        repo_id = selection['repo_id']

        try:
            self._delete_all_overrides(repo_id)
        except Exception, e:
            handle_gui_exception(e, _("Unable to reset repository overrides."),
                                 self._get_dialog_widget())
Ejemplo n.º 7
0
    def __init__(self, backend, parent):
        super(RepositoriesDialog, self).__init__()

        # Label wrapping on resize does not work very well on GTK2+
        # so we resize on the fly.
        if gtk_compat.GTK_COMPAT_VERSION == "2":
            # Set the title to wrap and connect to size-allocate to
            # properly resize the label so that it takes up the most
            # space it can.
            self.description_text.set_line_wrap(True)
            self.description_text.connect(
                'size-allocate',
                lambda label, size: label.set_size_request(size.width - 1, -1))

            self.name_text.set_line_wrap(True)
            self.name_text.connect(
                'size-allocate',
                lambda label, size: label.set_size_request(size.width - 1, -1))

            self.baseurl_text.set_line_wrap(True)
            self.baseurl_text.connect(
                'size-allocate',
                lambda label, size: label.set_size_request(size.width - 1, -1))

        # Set up dynamic elements
        self.overrides_treeview = ga_Gtk.TreeView()
        # Add at-spi because we no longer create this widget from glade
        self.overrides_treeview.get_accessible().set_name("Repository View")
        self.no_repos_label, self.no_repos_label_viewport = widgets.get_scrollable_label(
        )
        self.widget_switcher = widgets.WidgetSwitcher(
            self.scrolledwindow, self.no_repos_label_viewport,
            self.overrides_treeview)

        # We require the backend here so that we can always use its version
        # of Overrides which will guarantee that the CP UEPConnection is up
        # to date.
        # FIXME: We really shouldn't have to worry about our connection info
        #        changing out from under us.
        self.backend = backend
        self.async_update = AsyncRepoOverridesUpdate(self.backend.overrides)
        self.identity = require(IDENTITY)
        self.ent_dir = require(ENT_DIR)

        self.connect_signals({
            "on_dialog_delete_event": self._on_close,
            "on_close_button_clicked": self._on_close,
            "on_apply_button_clicked": self._on_apply_request,
            "on_reset_button_clicked": self._on_reset_repo
        })

        self.overrides_store = MappedListStore({
            "repo_id": str,
            "enabled": bool,
            "modified": bool,
            "modified-icon": ga_GdkPixbuf.Pixbuf,
            "name": str,
            "baseurl": str,
            "gpgcheck": bool,
            "gpgcheck_modified": bool,
            "repo_data": object,
            "override_data": object
        })

        self.other_overrides = OverridesTable(self.other_overrides_view)

        # FIXME: think this needs get_style_context() and possible a
        # Gtk.StyleProvider for gtk3
        # Change the background color of the no_repos_label_container to the same color
        # as the label's base color. The event container allows us to change the color.
        #label_base_color = self.no_repos_label.get_style_context().base[Gtk.StateType.NORMAL]
        #self.no_repos_label_viewport.modify_bg(Gtk.StateType.NORMAL, label_base_color)

        self.overrides_treeview.set_model(self.overrides_store)

        self.modified_icon = self.overrides_treeview.render_icon(
            ga_Gtk.STOCK_APPLY, ga_Gtk.IconSize.MENU)

        sortable_cols = []
        enabled_col = CheckBoxColumn(_("Enabled"), self.overrides_store,
                                     'enabled', self._on_enable_repo_toggle)
        self.overrides_treeview.append_column(enabled_col)

        gpgcheck_col = CheckBoxColumn(_("Gpgcheck"), self.overrides_store,
                                      'gpgcheck',
                                      self._on_gpgcheck_toggle_changed)
        self.overrides_treeview.append_column(gpgcheck_col)

        repo_id_col = TextTreeViewColumn(self.overrides_store,
                                         _("Repository ID"),
                                         'repo_id',
                                         expand=True)
        self.overrides_treeview.append_column(repo_id_col)
        sortable_cols.append((repo_id_col, 'text', 'repo_id'))

        modified_col = ga_Gtk.TreeViewColumn(
            _("Modified"),
            ga_Gtk.CellRendererPixbuf(),
            pixbuf=self.overrides_store['modified-icon'])
        self.overrides_treeview.append_column(modified_col)
        sortable_cols.append((modified_col, 'text', 'modified'))

        self.set_sorts(self.overrides_store, sortable_cols)

        self.overrides_treeview.get_selection().connect(
            'changed', self._on_selection)
        self.overrides_treeview.set_rules_hint(True)

        # Progress bar
        self.pb = None
        self.timer = 0

        self.parent = parent
        self.main_window.set_transient_for(parent)
Ejemplo n.º 8
0
class RepositoriesDialog(widgets.SubmanBaseWidget, HasSortableWidget):
    """
    GTK dialog for managing repositories and their overrides.
    """
    widget_names = [
        'main_window', 'reset_button', 'close_button', 'apply_button',
        'name_text', 'baseurl_text', 'scrolledwindow', 'other_overrides_view',
        'details_vbox', 'main_content_container', 'description_text'
    ]
    gui_file = "repositories"

    ENTS_PROVIDE_NO_REPOS = _(
        "Attached subscriptions do not provide any repositories.")
    NO_ATTACHED_SUBS = _(
        "No repositories are available without an attached subscription.")
    REPOS_DISABLED_BY_CFG = _("Repositories disabled by configuration.")

    def __init__(self, backend, parent):
        super(RepositoriesDialog, self).__init__()

        # Label wrapping on resize does not work very well on GTK2+
        # so we resize on the fly.
        if gtk_compat.GTK_COMPAT_VERSION == "2":
            # Set the title to wrap and connect to size-allocate to
            # properly resize the label so that it takes up the most
            # space it can.
            self.description_text.set_line_wrap(True)
            self.description_text.connect(
                'size-allocate',
                lambda label, size: label.set_size_request(size.width - 1, -1))

            self.name_text.set_line_wrap(True)
            self.name_text.connect(
                'size-allocate',
                lambda label, size: label.set_size_request(size.width - 1, -1))

            self.baseurl_text.set_line_wrap(True)
            self.baseurl_text.connect(
                'size-allocate',
                lambda label, size: label.set_size_request(size.width - 1, -1))

        # Set up dynamic elements
        self.overrides_treeview = ga_Gtk.TreeView()
        # Add at-spi because we no longer create this widget from glade
        self.overrides_treeview.get_accessible().set_name("Repository View")
        self.no_repos_label, self.no_repos_label_viewport = widgets.get_scrollable_label(
        )
        self.widget_switcher = widgets.WidgetSwitcher(
            self.scrolledwindow, self.no_repos_label_viewport,
            self.overrides_treeview)

        # We require the backend here so that we can always use its version
        # of Overrides which will guarantee that the CP UEPConnection is up
        # to date.
        # FIXME: We really shouldn't have to worry about our connection info
        #        changing out from under us.
        self.backend = backend
        self.async_update = AsyncRepoOverridesUpdate(self.backend.overrides)
        self.identity = require(IDENTITY)
        self.ent_dir = require(ENT_DIR)

        self.connect_signals({
            "on_dialog_delete_event": self._on_close,
            "on_close_button_clicked": self._on_close,
            "on_apply_button_clicked": self._on_apply_request,
            "on_reset_button_clicked": self._on_reset_repo
        })

        self.overrides_store = MappedListStore({
            "repo_id": str,
            "enabled": bool,
            "modified": bool,
            "modified-icon": ga_GdkPixbuf.Pixbuf,
            "name": str,
            "baseurl": str,
            "gpgcheck": bool,
            "gpgcheck_modified": bool,
            "repo_data": object,
            "override_data": object
        })

        self.other_overrides = OverridesTable(self.other_overrides_view)

        # FIXME: think this needs get_style_context() and possible a
        # Gtk.StyleProvider for gtk3
        # Change the background color of the no_repos_label_container to the same color
        # as the label's base color. The event container allows us to change the color.
        #label_base_color = self.no_repos_label.get_style_context().base[Gtk.StateType.NORMAL]
        #self.no_repos_label_viewport.modify_bg(Gtk.StateType.NORMAL, label_base_color)

        self.overrides_treeview.set_model(self.overrides_store)

        self.modified_icon = self.overrides_treeview.render_icon(
            ga_Gtk.STOCK_APPLY, ga_Gtk.IconSize.MENU)

        sortable_cols = []
        enabled_col = CheckBoxColumn(_("Enabled"), self.overrides_store,
                                     'enabled', self._on_enable_repo_toggle)
        self.overrides_treeview.append_column(enabled_col)

        gpgcheck_col = CheckBoxColumn(_("Gpgcheck"), self.overrides_store,
                                      'gpgcheck',
                                      self._on_gpgcheck_toggle_changed)
        self.overrides_treeview.append_column(gpgcheck_col)

        repo_id_col = TextTreeViewColumn(self.overrides_store,
                                         _("Repository ID"),
                                         'repo_id',
                                         expand=True)
        self.overrides_treeview.append_column(repo_id_col)
        sortable_cols.append((repo_id_col, 'text', 'repo_id'))

        modified_col = ga_Gtk.TreeViewColumn(
            _("Modified"),
            ga_Gtk.CellRendererPixbuf(),
            pixbuf=self.overrides_store['modified-icon'])
        self.overrides_treeview.append_column(modified_col)
        sortable_cols.append((modified_col, 'text', 'modified'))

        self.set_sorts(self.overrides_store, sortable_cols)

        self.overrides_treeview.get_selection().connect(
            'changed', self._on_selection)
        self.overrides_treeview.set_rules_hint(True)

        # Progress bar
        self.pb = None
        self.timer = 0

        self.parent = parent
        self.main_window.set_transient_for(parent)

    def hide(self):
        self.main_window.hide()

    def show(self):
        self._load_data()

    def _load_data(self):
        self._show_progress_bar(_("Loading Repository Data"),
                                _("Retrieving repository data from server."),
                                self.parent)
        self.async_update.load_data(self._on_async_load_data_success,
                                    self._on_async_load_data_failure)

    def _on_async_load_data_success(self, current_overrides, current_repos):
        self.overrides_store.set_sort_column_id(0, ga_Gtk.SortType.ASCENDING)
        self._refresh(current_overrides, current_repos)
        self._clear_progress_bar()
        self.main_window.present()
        # By default sort by repo_id

    def _on_async_load_data_failure(self, e):
        self._clear_progress_bar()
        handle_gui_exception(e, _("Unable to load repository data."),
                             self._get_dialog_widget())

    def _refresh(self,
                 current_overrides,
                 current_repos,
                 repo_id_to_select=None):
        # Current overrides from server
        overrides_per_repo = {}

        for override in current_overrides:
            repo_id = override.repo_id
            overrides_per_repo.setdefault(repo_id, {})
            overrides_per_repo[repo_id][override.name] = override.value

        self.apply_button.set_sensitive(False)

        self.overrides_store.clear()
        self.other_overrides.clear()

        self.main_content_container.show_all()

        # Switch the dialog view depending on content availability
        if (current_repos):
            self.widget_switcher.set_active(1)
            self.details_vbox.show()
        else:
            ent_count = len(self.ent_dir.list_valid())
            no_repos_message = self.ENTS_PROVIDE_NO_REPOS
            if ent_count == 0:
                no_repos_message = self.NO_ATTACHED_SUBS
            # Checks config for manage_repos. Output updated according to bz 1139174.
            if cfg.has_option('rhsm', 'manage_repos') and \
                    not int(cfg.get('rhsm', 'manage_repos')):
                no_repos_message = self.REPOS_DISABLED_BY_CFG

            self.no_repos_label.set_markup("<b>%s</b>" % no_repos_message)

            self.details_vbox.hide()
            self.widget_switcher.set_active(0)

        # Update the table model from our gathered override/repo data
        for repo in current_repos:
            overrides = overrides_per_repo.get(repo.id, None)
            self.overrides_store.add_map(
                self._build_table_row_data(repo, overrides))

        first_row_iter = self.overrides_store.get_iter_first()
        if not first_row_iter:
            self.reset_button.set_sensitive(False)
        elif repo_id_to_select:
            self._select_by_repo_id(repo_id_to_select)
        else:
            self.overrides_treeview.get_selection().select_iter(first_row_iter)

    def _build_table_row_data(self, repo_data, repo_overrides):
        modified = not repo_overrides is None
        enabled = self._get_boolean(
            self._get_model_value(repo_data, repo_overrides, 'enabled')[0])
        gpgcheck, gpgcheck_modified = self._get_model_value(
            repo_data, repo_overrides, 'gpgcheck')
        gpgcheck = self._get_boolean(gpgcheck)

        return {
            'enabled': bool(int(enabled)),
            'repo_id': repo_data.id,
            'modified': modified,
            'modified-icon': self._get_modified_icon(modified),
            'name': repo_data['name'],
            'baseurl': repo_data['baseurl'],
            'gpgcheck': gpgcheck,
            'gpgcheck_modified': gpgcheck_modified,
            'repo_data': repo_data,
            'override_data': repo_overrides
        }

    def _show_progress_bar(self, title, label, progress_parent=None):
        self.pb = progress.Progress(title, label, True)
        self.timer = ga_GObject.timeout_add(100, self.pb.pulse)
        self.pb.set_transient_for(progress_parent or self._get_dialog_widget())

    def _clear_progress_bar(self):
        if self.pb:
            self.pb.hide()
            ga_GObject.source_remove(self.timer)
            self.timer = 0
            self.pb = None

    def _get_modified_icon(self, modified):
        icon = None
        if modified:
            icon = self.modified_icon
        return icon

    def _get_selected_repo_id(self):
        selected = None
        override_selection = SelectionWrapper(
            self.overrides_treeview.get_selection(), self.overrides_store)
        if override_selection.is_valid():
            selected = override_selection['repo_id']
        return selected

    def _select_by_repo_id(self, repo_id):
        repo_data = (repo_id, self.overrides_store['repo_id'])
        self.overrides_store.foreach(self._select_repo_row, repo_data)

    def _select_repo_row(self, model, path, tree_iter, repo_data_tuple):
        """
        Passed to model's foreach method to select the row if the repo_id
        matches. Returning True tells foreach to stop processing rows.
        """
        repo_id, check_idx = repo_data_tuple
        row_repo_id = model.get_value(tree_iter, check_idx)
        if repo_id == row_repo_id:
            self.overrides_treeview.get_selection().select_iter(tree_iter)
            return True
        return False

    def _get_boolean(self, override_value):
        # An override value might come in as an int or a boolean string.
        # Try our best to convert it, and default to 0.
        try:
            val = int(override_value)
        except ValueError:
            val = 0
            if override_value is not None and override_value.upper() == "TRUE":
                val = 1
        return bool(val)

    def _get_model_value(self, repo, overrides, property_name):
        if not overrides or not property_name in overrides:
            return (repo[property_name], False)
        return (overrides[property_name], True)

    def _on_reset_repo(self, button):
        selection = SelectionWrapper(self.overrides_treeview.get_selection(),
                                     self.overrides_store)

        if not selection.is_valid():
            return

        confirm = YesNoDialog(
            _("Are you sure you want to remove all overrides for <b>%s</b>?") %
            selection['repo_id'], self._get_dialog_widget(),
            _("Confirm Remove All Overrides"))
        confirm.connect("response", self._on_reset_repo_response)

    def _on_reset_repo_response(self, dialog, response):
        if not response:
            return

        selection = SelectionWrapper(self.overrides_treeview.get_selection(),
                                     self.overrides_store)

        if not selection.is_valid():
            return

        repo_id = selection['repo_id']

        self._show_progress_bar(
            _("Removing Repository Overrides"),
            _("Removing all overrides for repository <b>%s</b>") % repo_id)
        self.async_update.remove_all_overrides(
            [repo_id], self._on_async_delete_all_overrides_success,
            self._on_async_delete_all_overrides_failure)

    def _on_async_delete_all_overrides_success(self, current_overrides,
                                               current_repos):
        selection = SelectionWrapper(self.overrides_treeview.get_selection(),
                                     self.overrides_store)

        repo_id = selection['repo_id']
        repo_data = None
        for repo in current_repos:
            if repo_id == repo.id:
                repo_data = repo
                break

        if repo_data:
            override_data = None
            for override in current_overrides:
                if repo_id == override.repo_id:
                    override_data = override
                    break

            row_data = self._build_table_row_data(repo_data, override_data)
            self.overrides_store.update_map(selection.tree_iter, row_data)

        # Update the UI based on the current selection as no selection change is
        # triggered, but data may enable/disable different widgets based on data
        # change.
        self._on_selection(self.overrides_treeview.get_selection())

        self._clear_progress_bar()

    def _on_async_delete_all_overrides_failure(self, e):
        self._clear_progress_bar()
        handle_gui_exception(e, _("Unable to reset repository overrides."),
                             self._get_dialog_widget())

    def _on_selection(self, tree_selection):
        selection = SelectionWrapper(tree_selection, self.overrides_store)

        self.other_overrides.clear()
        reset_enabled = False
        if selection.is_valid():
            overrides = selection['override_data']
            reset_enabled = overrides is not None and len(overrides) > 0

            self.name_text.set_text(selection['name'])
            self.baseurl_text.set_text(selection['baseurl'])

            for key, value in list((selection['override_data'] or {}).items()):
                if key not in ['gpgcheck', 'enabled']:
                    self.other_overrides.add_override(key, value)

        self.reset_button.set_sensitive(reset_enabled)

    def _get_changed_overrides(self):
        override_mapping = {'to_add': [], 'to_remove': []}

        # Process each row in the model and build up a mapping of overrides.
        self.overrides_store.foreach(self._get_overrides_for_row,
                                     override_mapping)
        return override_mapping

    def _on_apply_request(self, button, event=None):
        override_mapping = self._get_changed_overrides()
        self._apply_override_changes(override_mapping, self._on_update_success)

    def _apply_override_changes(self, override_mapping, success_handler):
        self._show_progress_bar(
            _("Updating Repository Overrides"),
            _("Applying override changes to repositories."))
        self.async_update.update_overrides(override_mapping['to_add'],
                                           override_mapping['to_remove'],
                                           success_handler,
                                           self._on_update_failure)

    def _on_update_success(self, current_overrides, current_repos):
        self._refresh(current_overrides, current_repos,
                      self._get_selected_repo_id())
        self._clear_progress_bar()

    def _on_update_failure(self, e):
        handle_gui_exception(e, _("Unable to update overrides."),
                             self._get_dialog_widget())
        self._clear_progress_bar()

    def _on_close(self, button, event=None):
        override_mapping = self._get_changed_overrides()
        if (len(override_mapping["to_add"]) == 0
                and len(override_mapping["to_remove"]) == 0):
            self.close_dialog()
            return True

        # There are changes pending, check if the user would like to save changes.
        confirm = YesNoDialog(_("Repositories have changes. Save changes?"),
                              self._get_dialog_widget(), _("Save Changes"))
        confirm.connect("response", self._on_apply_changes_on_close_response,
                        override_mapping)

    def _on_apply_changes_on_close_response(self, dialog, response,
                                            override_mapping):
        if not response:
            self.close_dialog()
            return
        self._apply_override_changes(override_mapping,
                                     self._on_async_close_request)

    def _on_async_close_request(self, current_overrides, current_repos):
        self.close_dialog()

    def close_dialog(self):
        self._clear_progress_bar()
        self.hide()

    def _get_overrides_for_row(self, model, path, iter, override_mapping):
        self._update_override_mapping("gpgcheck", model, path, iter,
                                      override_mapping)
        self._update_override_mapping("enabled", model, path, iter,
                                      override_mapping)

    def _update_override_mapping(self, attribute, model, path, iter,
                                 override_mapping):
        '''
        Process a single model row and determine if an override should be added/removed.
        '''
        repo = model.get_value(iter, model['repo_data'])
        remote_overrides = model.get_value(iter, model['override_data'])

        repo_attribute_value = self._get_boolean(repo[attribute])
        model_attribute_value = self._get_boolean(
            model.get_value(iter, model[attribute]))
        has_remote_override = remote_overrides and attribute in remote_overrides

        if not has_remote_override and model_attribute_value != repo_attribute_value:
            override_mapping["to_add"].append(
                Override(repo.id, attribute, str(int(model_attribute_value))))
        elif has_remote_override and model_attribute_value == repo_attribute_value:
            override_mapping["to_remove"].append(Override(repo.id, attribute))

    def _on_toggle_changed(self, override_model_iter, enabled, key):
        '''
        Update the gui based on check box change.
        '''
        repo = self.overrides_store.get_value(
            override_model_iter, self.overrides_store['repo_data'])
        overrides = self.overrides_store.get_value(
            override_model_iter, self.overrides_store['override_data'])

        current_gpg_check = self._get_boolean(
            self.overrides_store.get_value(override_model_iter,
                                           self.overrides_store['gpgcheck']))
        repo_gpg_check = self._get_boolean(repo['gpgcheck'])

        current_enabled = self._get_boolean(
            self.overrides_store.get_value(override_model_iter,
                                           self.overrides_store['enabled']))
        repo_enabled = self._get_boolean(repo['enabled'])

        has_extra = self._has_extra_overrides(overrides)

        mark_modified = has_extra or repo_gpg_check != current_gpg_check or repo_enabled != current_enabled
        enable_reset = overrides is not None and len(overrides) > 0

        self.reset_button.set_sensitive(enable_reset)
        self.overrides_store.set_value(override_model_iter,
                                       self.overrides_store['modified'],
                                       mark_modified)
        self.overrides_store.set_value(override_model_iter,
                                       self.overrides_store['modified-icon'],
                                       self._get_modified_icon(mark_modified))

        changed = self._get_changed_overrides()
        activate_apply_button = len(changed["to_add"]) != 0 or len(
            changed["to_remove"]) != 0
        self.apply_button.set_sensitive(activate_apply_button)

    def _has_extra_overrides(self, override_data):
        for key, value in list((override_data or {}).items()):
            if key not in ['gpgcheck', 'enabled']:
                return True
        return False

    def _on_enable_repo_toggle(self, override_model_iter, enabled):
        return self._on_toggle_changed(override_model_iter, enabled, 'enabled')

    def _on_gpgcheck_toggle_changed(self, override_model_iter, enabled):
        return self._on_toggle_changed(override_model_iter, enabled,
                                       'gpgcheck')

    def _get_dialog_widget(self):
        return self.main_window
class ContractSelectionWindow(object):
    def __init__(self, selected_callback, cancel_callback):
        self._selected_callback = selected_callback
        self._cancel_callback = cancel_callback
        self.total_contracts = 0
        self.contract_selection_xml = gtk.glade.XML(CONTRACT_SELECTION_GLADE)
        self.contract_selection_win = self.contract_selection_xml.get_widget(
            "contract_selection_window")
        self.subscribe_button = self.contract_selection_xml.get_widget(
            'subscribe_button')
        self.edit_quantity_label = self.contract_selection_xml.get_widget(
            'edit_quantity_label')

        self.contract_selection_treeview = \
                self.contract_selection_xml.get_widget(
                        "contract_selection_treeview")
        self.contract_selection_treeview.get_selection().connect(
            "changed", self._on_contract_selection)

        self.subscription_name_label = self.contract_selection_xml.get_widget(
            "subscription_name_label")

        self.total_contracts_label = self.contract_selection_xml.get_widget(
            "total_contracts_label")

        self.contract_selection_xml.signal_autoconnect({
            "on_cancel_button_clicked":
            self._cancel_button_clicked,
            "on_subscribe_button_clicked":
            self._subscribe_button_clicked,
        })

        self.model = MappedListStore(self.get_type_map())
        self.contract_selection_treeview.set_model(self.model)

    def get_type_map(self):
        return {
            'contract_number': str,
            'consumed_fraction': str,
            'start_date': gobject.TYPE_PYOBJECT,
            'end_date': gobject.TYPE_PYOBJECT,
            'default_quantity': int,
            'product_name': str,
            'pool': gobject.TYPE_PYOBJECT,
            'is_virt_only': bool,
            'multi_entitlement': bool,
            'quantity_available': int,
            'quantity_increment': int,
        }

    def show(self):
        self.populate_treeview()
        self.contract_selection_win.show_all()

    def destroy(self):
        self.contract_selection_win.destroy()

    def populate_treeview(self):
        renderer = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_("Contract"),
                                    renderer,
                                    text=self.model['contract_number'])
        column.set_expand(True)
        column.set_sort_column_id(self.model['contract_number'])
        self.model.set_sort_func(self.model['contract_number'],
                                 self._sort_text, None)
        self.contract_selection_treeview.append_column(column)

        column = widgets.MachineTypeColumn(self.model['is_virt_only'])
        column.set_sort_column_id(self.model['is_virt_only'])
        self.model.set_sort_func(self.model['is_virt_only'],
                                 self._sort_machine_type, column)
        self.contract_selection_treeview.append_column(column)

        renderer = gtk.CellRendererText()
        renderer.set_property("xalign", 0.5)
        column = gtk.TreeViewColumn(_("Used / Total"),
                                    renderer,
                                    text=self.model['consumed_fraction'])
        self.contract_selection_treeview.append_column(column)

        renderer = widgets.CellRendererDate()
        column = gtk.TreeViewColumn(_("Start Date"),
                                    renderer,
                                    date=self.model['start_date'])
        column.set_sort_column_id(self.model['start_date'])
        self.model.set_sort_func(self.model['start_date'], self._sort_date,
                                 None)
        self.contract_selection_treeview.append_column(column)

        renderer = widgets.CellRendererDate()
        column = gtk.TreeViewColumn(_("End Date"),
                                    renderer,
                                    date=self.model['end_date'])
        column.set_sort_column_id(self.model['end_date'])
        self.model.set_sort_func(self.model['end_date'], self._sort_date, None)
        self.contract_selection_treeview.append_column(column)

        column = widgets.QuantitySelectionColumn(
            _("Quantity"), self.model, self.model['default_quantity'],
            self.model['multi_entitlement'], self.model['quantity_available'],
            self.model['quantity_increment'])
        self.contract_selection_treeview.append_column(column)

        self.edit_quantity_label.set_label(column.get_column_legend_text())

    def add_pool(self, pool, default_quantity_value):
        self.total_contracts += 1
        self.total_contracts_label.set_text(str(self.total_contracts))
        self.subscription_name_label.set_text(pool['productName'])

        # Use unlimited for -1 quanities
        quantity = pool['quantity']
        if quantity < 0:
            quantity = _('Unlimited')
            quantity_available = -1
        else:
            quantity_available = int(pool['quantity']) - int(pool['consumed'])

        # cap the default selected quantity at the max available
        # for that pool. See #855257. Watch out for quantity_available
        # being -1 (unlimited).
        if default_quantity_value > quantity_available and quantity_available >= 0:
            default_quantity_value = quantity_available

        quantity_increment = 1
        if 'calculatedAttributes' in pool:
            calculated_attrs = pool['calculatedAttributes']

            if 'quantity_increment' in calculated_attrs:
                quantity_increment = int(
                    calculated_attrs['quantity_increment'])

        self.model.add_map({
            'contract_number':
            pool['contractNumber'],
            'consumed_fraction':
            "%s / %s" % (pool['consumed'], quantity),
            'start_date':
            isodate.parse_date(pool['startDate']),
            'end_date':
            isodate.parse_date(pool['endDate']),
            'default_quantity':
            default_quantity_value,
            'product_name':
            pool['productName'],
            'pool':
            pool,
            'is_virt_only':
            PoolWrapper(pool).is_virt_only(),
            'multi_entitlement':
            allows_multi_entitlement(pool),
            'quantity_available':
            quantity_available,
            'quantity_increment':
            quantity_increment,
        })

    def set_parent_window(self, window):
        self.contract_selection_win.set_transient_for(window)

    def _cancel_button_clicked(self, button):
        self._cancel_callback()

    def _subscribe_button_clicked(self, button):
        selection = self.contract_selection_treeview.get_selection()
        model, iter = selection.get_selected()
        if iter is not None:
            pool, quantity = model.get(iter, model['pool'],
                                       model['default_quantity'])
            self._selected_callback(pool, quantity)

    def _on_contract_selection(self, widget):
        model, tree_iter = widget.get_selected()

        enabled = True
        if not tree_iter:
            enabled = False

        self.subscribe_button.set_sensitive(enabled)

    def _sort_text(self, model, row1, row2, data):
        sort_column, sort_type = model.get_sort_column_id()
        str1 = model.get_value(row1, sort_column)
        str2 = model.get_value(row2, sort_column)
        return cmp(str1, str2)

    def _sort_machine_type(self, model, row1, row2, col):
        # Machine type is actually a boolean denoting whether the type is
        # virtual or not.  We do not want to sort on the boolean value.
        # Instead we want to sort on the text value that is going to get
        # displayed for the boolean value.
        sort_column, sort_type = model.get_sort_column_id()
        results = []
        for row in [row1, row2]:
            _bool = model.get_value(row, sort_column)
            if _bool is None:
                text = col._get_none_text()
            elif bool(_bool):
                text = col._get_true_text()
            else:
                text = col._get_false_text()
            results.append(text)
        return cmp(results[0], results[1])

    def _sort_date(self, model, row1, row2, data):
        """
        Used for sorting dates that could be None.
        """
        sort_column, sort_type = model.get_sort_column_id()
        date1 = model.get_value(row1, sort_column) \
            or datetime.date(datetime.MINYEAR, 1, 1)
        date2 = model.get_value(row2, sort_column) \
            or datetime.date(datetime.MINYEAR, 1, 1)
        epoch1 = time.mktime(date1.timetuple())
        epoch2 = time.mktime(date2.timetuple())
        return cmp(epoch1, epoch2)
Ejemplo n.º 10
0
    def __init__(self, backend, parent):
        super(RepositoriesDialog, self).__init__()

        # Label wrapping on resize does not work very well on GTK2+
        # so we resize on the fly.
        if gtk_compat.GTK_COMPAT_VERSION == "2":
            # Set the title to wrap and connect to size-allocate to
            # properly resize the label so that it takes up the most
            # space it can.
            self.description_text.set_line_wrap(True)
            self.description_text.connect('size-allocate', lambda label, size: label.set_size_request(size.width - 1, -1))

            self.name_text.set_line_wrap(True)
            self.name_text.connect('size-allocate', lambda label, size: label.set_size_request(size.width - 1, -1))

            self.baseurl_text.set_line_wrap(True)
            self.baseurl_text.connect('size-allocate', lambda label, size: label.set_size_request(size.width - 1, -1))

        # Set up dynamic elements
        self.overrides_treeview = ga_Gtk.TreeView()
        # Add at-spi because we no longer create this widget from glade
        self.overrides_treeview.get_accessible().set_name("Repository View")
        self.no_repos_label, self.no_repos_label_viewport = widgets.get_scrollable_label()
        self.widget_switcher = widgets.WidgetSwitcher(self.scrolledwindow,
                self.no_repos_label_viewport, self.overrides_treeview)

        # We require the backend here so that we can always use its version
        # of Overrides which will guarantee that the CP UEPConnection is up
        # to date.
        # FIXME: We really shouldn't have to worry about our connection info
        #        changing out from under us.
        self.backend = backend
        self.async_update = AsyncRepoOverridesUpdate(self.backend.overrides)
        self.identity = require(IDENTITY)
        self.ent_dir = require(ENT_DIR)

        self.connect_signals({"on_dialog_delete_event": self._on_close,
                              "on_close_button_clicked": self._on_close,
                              "on_apply_button_clicked": self._on_apply_request,
                              "on_reset_button_clicked": self._on_reset_repo})

        self.overrides_store = MappedListStore({
            "repo_id": str,
            "enabled": bool,
            "modified": bool,
            "modified-icon": ga_GdkPixbuf.Pixbuf,
            "name": str,
            "baseurl": str,
            "gpgcheck": bool,
            "gpgcheck_modified": bool,
            "repo_data": object,
            "override_data": object
        })

        self.other_overrides = OverridesTable(self.other_overrides_view)

        # FIXME: think this needs get_style_context() and possible a
        # Gtk.StyleProvider for gtk3
        # Change the background color of the no_repos_label_container to the same color
        # as the label's base color. The event container allows us to change the color.
        #label_base_color = self.no_repos_label.get_style_context().base[Gtk.StateType.NORMAL]
        #self.no_repos_label_viewport.modify_bg(Gtk.StateType.NORMAL, label_base_color)

        self.overrides_treeview.set_model(self.overrides_store)

        self.modified_icon = self.overrides_treeview.render_icon(ga_Gtk.STOCK_APPLY,
                                                                 ga_Gtk.IconSize.MENU)

        sortable_cols = []
        enabled_col = CheckBoxColumn(_("Enabled"), self.overrides_store, 'enabled',
            self._on_enable_repo_toggle)
        self.overrides_treeview.append_column(enabled_col)

        gpgcheck_col = CheckBoxColumn(_("Gpgcheck"), self.overrides_store, 'gpgcheck',
            self._on_gpgcheck_toggle_changed)
        self.overrides_treeview.append_column(gpgcheck_col)

        repo_id_col = TextTreeViewColumn(self.overrides_store,
                                         _("Repository ID"),
                                         'repo_id',
                                         expand=True)
        self.overrides_treeview.append_column(repo_id_col)
        sortable_cols.append((repo_id_col, 'text', 'repo_id'))

        modified_col = ga_Gtk.TreeViewColumn(_("Modified"), ga_Gtk.CellRendererPixbuf(),
                                          pixbuf=self.overrides_store['modified-icon'])
        self.overrides_treeview.append_column(modified_col)
        sortable_cols.append((modified_col, 'text', 'modified'))

        self.set_sorts(self.overrides_store, sortable_cols)

        self.overrides_treeview.get_selection().connect('changed', self._on_selection)
        self.overrides_treeview.set_rules_hint(True)

        # Progress bar
        self.pb = None
        self.timer = 0

        self.parent = parent
        self.main_window.set_transient_for(parent)
Ejemplo n.º 11
0
class RepositoriesDialog(widgets.SubmanBaseWidget, HasSortableWidget):
    """
    GTK dialog for managing repositories and their overrides.
    """
    widget_names = ['main_window', 'reset_button', 'close_button', 'apply_button',
                    'name_text', 'baseurl_text', 'scrolledwindow',
                    'other_overrides_view', 'details_vbox', 'main_content_container',
                    'description_text']
    gui_file = "repositories"

    ENTS_PROVIDE_NO_REPOS = _("Attached subscriptions do not provide any repositories.")
    NO_ATTACHED_SUBS = _("No repositories are available without an attached subscription.")
    REPOS_DISABLED_BY_CFG = _("Repositories disabled by configuration.")

    def __init__(self, backend, parent):
        super(RepositoriesDialog, self).__init__()

        # Label wrapping on resize does not work very well on GTK2+
        # so we resize on the fly.
        if gtk_compat.GTK_COMPAT_VERSION == "2":
            # Set the title to wrap and connect to size-allocate to
            # properly resize the label so that it takes up the most
            # space it can.
            self.description_text.set_line_wrap(True)
            self.description_text.connect('size-allocate', lambda label, size: label.set_size_request(size.width - 1, -1))

            self.name_text.set_line_wrap(True)
            self.name_text.connect('size-allocate', lambda label, size: label.set_size_request(size.width - 1, -1))

            self.baseurl_text.set_line_wrap(True)
            self.baseurl_text.connect('size-allocate', lambda label, size: label.set_size_request(size.width - 1, -1))

        # Set up dynamic elements
        self.overrides_treeview = ga_Gtk.TreeView()
        # Add at-spi because we no longer create this widget from glade
        self.overrides_treeview.get_accessible().set_name("Repository View")
        self.no_repos_label, self.no_repos_label_viewport = widgets.get_scrollable_label()
        self.widget_switcher = widgets.WidgetSwitcher(self.scrolledwindow,
                self.no_repos_label_viewport, self.overrides_treeview)

        # We require the backend here so that we can always use its version
        # of Overrides which will guarantee that the CP UEPConnection is up
        # to date.
        # FIXME: We really shouldn't have to worry about our connection info
        #        changing out from under us.
        self.backend = backend
        self.async_update = AsyncRepoOverridesUpdate(self.backend.overrides)
        self.identity = require(IDENTITY)
        self.ent_dir = require(ENT_DIR)

        self.connect_signals({"on_dialog_delete_event": self._on_close,
                              "on_close_button_clicked": self._on_close,
                              "on_apply_button_clicked": self._on_apply_request,
                              "on_reset_button_clicked": self._on_reset_repo})

        self.overrides_store = MappedListStore({
            "repo_id": str,
            "enabled": bool,
            "modified": bool,
            "modified-icon": ga_GdkPixbuf.Pixbuf,
            "name": str,
            "baseurl": str,
            "gpgcheck": bool,
            "gpgcheck_modified": bool,
            "repo_data": object,
            "override_data": object
        })

        self.other_overrides = OverridesTable(self.other_overrides_view)

        # FIXME: think this needs get_style_context() and possible a
        # Gtk.StyleProvider for gtk3
        # Change the background color of the no_repos_label_container to the same color
        # as the label's base color. The event container allows us to change the color.
        #label_base_color = self.no_repos_label.get_style_context().base[Gtk.StateType.NORMAL]
        #self.no_repos_label_viewport.modify_bg(Gtk.StateType.NORMAL, label_base_color)

        self.overrides_treeview.set_model(self.overrides_store)

        self.modified_icon = self.overrides_treeview.render_icon(ga_Gtk.STOCK_APPLY,
                                                                 ga_Gtk.IconSize.MENU)

        sortable_cols = []
        enabled_col = CheckBoxColumn(_("Enabled"), self.overrides_store, 'enabled',
            self._on_enable_repo_toggle)
        self.overrides_treeview.append_column(enabled_col)

        gpgcheck_col = CheckBoxColumn(_("Gpgcheck"), self.overrides_store, 'gpgcheck',
            self._on_gpgcheck_toggle_changed)
        self.overrides_treeview.append_column(gpgcheck_col)

        repo_id_col = TextTreeViewColumn(self.overrides_store,
                                         _("Repository ID"),
                                         'repo_id',
                                         expand=True)
        self.overrides_treeview.append_column(repo_id_col)
        sortable_cols.append((repo_id_col, 'text', 'repo_id'))

        modified_col = ga_Gtk.TreeViewColumn(_("Modified"), ga_Gtk.CellRendererPixbuf(),
                                          pixbuf=self.overrides_store['modified-icon'])
        self.overrides_treeview.append_column(modified_col)
        sortable_cols.append((modified_col, 'text', 'modified'))

        self.set_sorts(self.overrides_store, sortable_cols)

        self.overrides_treeview.get_selection().connect('changed', self._on_selection)
        self.overrides_treeview.set_rules_hint(True)

        # Progress bar
        self.pb = None
        self.timer = 0

        self.parent = parent
        self.main_window.set_transient_for(parent)

    def hide(self):
        self.main_window.hide()

    def show(self):
        self._load_data()

    def _load_data(self):
        self._show_progress_bar(_("Loading Repository Data"), _("Retrieving repository data from server."), self.parent)
        self.async_update.load_data(self._on_async_load_data_success, self._on_async_load_data_failure)

    def _on_async_load_data_success(self, current_overrides, current_repos):
        self.overrides_store.set_sort_column_id(0, ga_Gtk.SortType.ASCENDING)
        self._refresh(current_overrides, current_repos)
        self._clear_progress_bar()
        self.main_window.present()
        # By default sort by repo_id

    def _on_async_load_data_failure(self, e):
        self._clear_progress_bar()
        handle_gui_exception(e, _("Unable to load repository data."),
                     self._get_dialog_widget())

    def _refresh(self, current_overrides, current_repos, repo_id_to_select=None):
        # Current overrides from server
        overrides_per_repo = {}

        for override in current_overrides:
            repo_id = override.repo_id
            overrides_per_repo.setdefault(repo_id, {})
            overrides_per_repo[repo_id][override.name] = override.value

        self.apply_button.set_sensitive(False)

        self.overrides_store.clear()
        self.other_overrides.clear()

        self.main_content_container.show_all()

        # Switch the dialog view depending on content availability
        if (current_repos):
            self.widget_switcher.set_active(1)
            self.details_vbox.show()
        else:
            ent_count = len(self.ent_dir.list_valid())
            no_repos_message = self.ENTS_PROVIDE_NO_REPOS
            if ent_count == 0:
                no_repos_message = self.NO_ATTACHED_SUBS
            # Checks config for manage_repos. Output updated according to bz 1139174.
            if cfg.has_option('rhsm', 'manage_repos') and \
                    not int(cfg.get('rhsm', 'manage_repos')):
                no_repos_message = self.REPOS_DISABLED_BY_CFG

            self.no_repos_label.set_markup("<b>%s</b>" % no_repos_message)

            self.details_vbox.hide()
            self.widget_switcher.set_active(0)

        # Update the table model from our gathered override/repo data
        for repo in current_repos:
            overrides = overrides_per_repo.get(repo.id, None)
            self.overrides_store.add_map(self._build_table_row_data(repo, overrides))

        first_row_iter = self.overrides_store.get_iter_first()
        if not first_row_iter:
            self.reset_button.set_sensitive(False)
        elif repo_id_to_select:
            self._select_by_repo_id(repo_id_to_select)
        else:
            self.overrides_treeview.get_selection().select_iter(first_row_iter)

    def _build_table_row_data(self, repo_data, repo_overrides):
        modified = not repo_overrides is None
        enabled = self._get_boolean(self._get_model_value(repo_data, repo_overrides, 'enabled')[0])
        gpgcheck, gpgcheck_modified = self._get_model_value(repo_data, repo_overrides, 'gpgcheck')
        gpgcheck = self._get_boolean(gpgcheck)

        return {
            'enabled': bool(int(enabled)),
            'repo_id': repo_data.id,
            'modified': modified,
            'modified-icon': self._get_modified_icon(modified),
            'name': repo_data['name'],
            'baseurl': repo_data['baseurl'],
            'gpgcheck': gpgcheck,
            'gpgcheck_modified': gpgcheck_modified,
            'repo_data': repo_data,
            'override_data': repo_overrides
        }

    def _show_progress_bar(self, title, label, progress_parent=None):
        self.pb = progress.Progress(title, label, True)
        self.timer = ga_GObject.timeout_add(100, self.pb.pulse)
        self.pb.set_transient_for(progress_parent or self._get_dialog_widget())

    def _clear_progress_bar(self):
        if self.pb:
            self.pb.hide()
            ga_GObject.source_remove(self.timer)
            self.timer = 0
            self.pb = None

    def _get_modified_icon(self, modified):
        icon = None
        if modified:
            icon = self.modified_icon
        return icon

    def _get_selected_repo_id(self):
        selected = None
        override_selection = SelectionWrapper(self.overrides_treeview.get_selection(), self.overrides_store)
        if override_selection.is_valid():
            selected = override_selection['repo_id']
        return selected

    def _select_by_repo_id(self, repo_id):
        repo_data = (repo_id, self.overrides_store['repo_id'])
        self.overrides_store.foreach(self._select_repo_row, repo_data)

    def _select_repo_row(self, model, path, tree_iter, repo_data_tuple):
        """
        Passed to model's foreach method to select the row if the repo_id
        matches. Returning True tells foreach to stop processing rows.
        """
        repo_id, check_idx = repo_data_tuple
        row_repo_id = model.get_value(tree_iter, check_idx)
        if repo_id == row_repo_id:
            self.overrides_treeview.get_selection().select_iter(tree_iter)
            return True
        return False

    def _get_boolean(self, override_value):
        # An override value might come in as an int or a boolean string.
        # Try our best to convert it, and default to 0.
        try:
            val = int(override_value)
        except ValueError:
            val = 0
            if override_value is not None and override_value.upper() == "TRUE":
                val = 1
        return bool(val)

    def _get_model_value(self, repo, overrides, property_name):
        if not overrides or not property_name in overrides:
            return (repo[property_name], False)
        return (overrides[property_name], True)

    def _on_reset_repo(self, button):
        selection = SelectionWrapper(self.overrides_treeview.get_selection(),
                                     self.overrides_store)

        if not selection.is_valid():
            return

        confirm = YesNoDialog(_("Are you sure you want to remove all overrides for <b>%s</b>?") % selection['repo_id'],
                                 self._get_dialog_widget(), _("Confirm Remove All Overrides"))
        confirm.connect("response", self._on_reset_repo_response)

    def _on_reset_repo_response(self, dialog, response):
        if not response:
            return

        selection = SelectionWrapper(self.overrides_treeview.get_selection(),
                                     self.overrides_store)

        if not selection.is_valid():
            return

        repo_id = selection['repo_id']

        self._show_progress_bar(_("Removing Repository Overrides"), _("Removing all overrides for repository <b>%s</b>") % repo_id)
        self.async_update.remove_all_overrides([repo_id], self._on_async_delete_all_overrides_success,
                                          self._on_async_delete_all_overrides_failure)

    def _on_async_delete_all_overrides_success(self, current_overrides, current_repos):
        selection = SelectionWrapper(self.overrides_treeview.get_selection(),
                                     self.overrides_store)

        repo_id = selection['repo_id']
        repo_data = None
        for repo in current_repos:
            if repo_id == repo.id:
                repo_data = repo
                break

        if repo_data:
            override_data = None
            for override in current_overrides:
                if repo_id == override.repo_id:
                    override_data = override
                    break

            row_data = self._build_table_row_data(repo_data, override_data)
            self.overrides_store.update_map(selection.tree_iter, row_data)

        # Update the UI based on the current selection as no selection change is
        # triggered, but data may enable/disable different widgets based on data
        # change.
        self._on_selection(self.overrides_treeview.get_selection())

        self._clear_progress_bar()

    def _on_async_delete_all_overrides_failure(self, e):
        self._clear_progress_bar()
        handle_gui_exception(e, _("Unable to reset repository overrides."),
                     self._get_dialog_widget())

    def _on_selection(self, tree_selection):
        selection = SelectionWrapper(tree_selection, self.overrides_store)

        self.other_overrides.clear()
        reset_enabled = False
        if selection.is_valid():
            overrides = selection['override_data']
            reset_enabled = overrides is not None and len(overrides) > 0

            self.name_text.set_text(selection['name'])
            self.baseurl_text.set_text(selection['baseurl'])

            for key, value in (selection['override_data'] or {}).items():
                if key not in ['gpgcheck', 'enabled']:
                    self.other_overrides.add_override(key, value)

        self.reset_button.set_sensitive(reset_enabled)

    def _get_changed_overrides(self):
        override_mapping = {
            'to_add': [],
            'to_remove': []
        }

        # Process each row in the model and build up a mapping of overrides.
        self.overrides_store.foreach(self._get_overrides_for_row, override_mapping)
        return override_mapping

    def _on_apply_request(self, button, event=None):
        override_mapping = self._get_changed_overrides()
        self._apply_override_changes(override_mapping, self._on_update_success)

    def _apply_override_changes(self, override_mapping, success_handler):
        self._show_progress_bar(_("Updating Repository Overrides"), _("Applying override changes to repositories."))
        self.async_update.update_overrides(override_mapping['to_add'], override_mapping['to_remove'],
                                      success_handler, self._on_update_failure)

    def _on_update_success(self, current_overrides, current_repos):
        self._refresh(current_overrides, current_repos, self._get_selected_repo_id())
        self._clear_progress_bar()

    def _on_update_failure(self, e):
        handle_gui_exception(e, _("Unable to update overrides."), self._get_dialog_widget())
        self._clear_progress_bar()

    def _on_close(self, button, event=None):
        override_mapping = self._get_changed_overrides()
        if (len(override_mapping["to_add"]) == 0 and len(override_mapping["to_remove"]) == 0):
            self.close_dialog()
            return True

        # There are changes pending, check if the user would like to save changes.
        confirm = YesNoDialog(_("Repositories have changes. Save changes?"),
                                 self._get_dialog_widget(), _("Save Changes"))
        confirm.connect("response", self._on_apply_changes_on_close_response, override_mapping)

    def _on_apply_changes_on_close_response(self, dialog, response, override_mapping):
        if not response:
            self.close_dialog()
            return
        self._apply_override_changes(override_mapping, self._on_async_close_request)

    def _on_async_close_request(self, current_overrides, current_repos):
        self.close_dialog()

    def close_dialog(self):
        self._clear_progress_bar()
        self.hide()

    def _get_overrides_for_row(self, model, path, iter, override_mapping):
        self._update_override_mapping("gpgcheck", model, path, iter, override_mapping)
        self._update_override_mapping("enabled", model, path, iter, override_mapping)

    def _update_override_mapping(self, attribute, model, path, iter, override_mapping):
        '''
        Process a single model row and determine if an override should be added/removed.
        '''
        repo = model.get_value(iter, model['repo_data'])
        remote_overrides = model.get_value(iter, model['override_data'])

        repo_attribute_value = self._get_boolean(repo[attribute])
        model_attribute_value = self._get_boolean(model.get_value(iter, model[attribute]))
        has_remote_override = remote_overrides and attribute in remote_overrides

        if not has_remote_override and model_attribute_value != repo_attribute_value:
            override_mapping["to_add"].append(Override(repo.id, attribute, str(int(model_attribute_value))))
        elif has_remote_override and model_attribute_value == repo_attribute_value:
            override_mapping["to_remove"].append(Override(repo.id, attribute))

    def _on_toggle_changed(self, override_model_iter, enabled, key):
        '''
        Update the gui based on check box change.
        '''
        repo = self.overrides_store.get_value(override_model_iter,
                                              self.overrides_store['repo_data'])
        overrides = self.overrides_store.get_value(override_model_iter,
                                              self.overrides_store['override_data'])

        current_gpg_check = self._get_boolean(self.overrides_store.get_value(override_model_iter,
                                                                             self.overrides_store['gpgcheck']))
        repo_gpg_check = self._get_boolean(repo['gpgcheck'])

        current_enabled = self._get_boolean(self.overrides_store.get_value(override_model_iter,
                                                                           self.overrides_store['enabled']))
        repo_enabled = self._get_boolean(repo['enabled'])

        has_extra = self._has_extra_overrides(overrides)

        mark_modified = has_extra or repo_gpg_check != current_gpg_check or repo_enabled != current_enabled
        enable_reset = overrides is not None and len(overrides) > 0

        self.reset_button.set_sensitive(enable_reset)
        self.overrides_store.set_value(override_model_iter, self.overrides_store['modified'], mark_modified)
        self.overrides_store.set_value(override_model_iter, self.overrides_store['modified-icon'],
                                       self._get_modified_icon(mark_modified))

        changed = self._get_changed_overrides()
        activate_apply_button = len(changed["to_add"]) != 0 or len(changed["to_remove"]) != 0
        self.apply_button.set_sensitive(activate_apply_button)

    def _has_extra_overrides(self, override_data):
        for key, value in (override_data or {}).items():
            if key not in ['gpgcheck', 'enabled']:
                return True
        return False

    def _on_enable_repo_toggle(self, override_model_iter, enabled):
        return self._on_toggle_changed(override_model_iter, enabled, 'enabled')

    def _on_gpgcheck_toggle_changed(self, override_model_iter, enabled):
        return self._on_toggle_changed(override_model_iter, enabled, 'gpgcheck')

    def _get_dialog_widget(self):
        return self.main_window
Ejemplo n.º 12
0
    def __init__(self, backend, parent):
        super(RepositoriesDialog, self).__init__('repositories.glade')

        # Set up dynamic elements
        self.overrides_treeview = gtk.TreeView()
        self.no_repos_label, self.no_repos_label_viewport = widgets.get_scrollable_label()
        self.widget_switcher = widgets.WidgetSwitcher(self.scrolledwindow,
                self.no_repos_label_viewport, self.overrides_treeview)

        # We require the backend here so that we can always use its version
        # of Overrides which will guarantee that the CP UEPConnection is up
        # to date.
        # FIXME: We really shouldn't have to worry about our connection info
        #        changing out from under us.
        self.backend = backend
        self.identity = require(IDENTITY)
        self.ent_dir = require(ENT_DIR)

        self.glade.signal_autoconnect({
                "on_dialog_delete_event": self._on_close,
                "on_close_button_clicked": self._on_close,
                "on_reset_button_clicked": self._on_reset_repo,
                "on_gpgcheck_edit_button_clicked": self._on_gpgcheck_edit_button_clicked,
                "on_gpgcheck_remove_button_clicked": self._on_gpgcheck_remove_button_clicked,
                "on_gpgcheck_combo_box_changed": self._on_gpgcheck_combo_box_changed,
        })

        self.overrides_store = MappedListStore({
            "repo_id": str,
            "enabled": bool,
            "modified": bool,
            "modified-icon": gtk.gdk.Pixbuf,
            "name": str,
            "baseurl": str,
            "gpgcheck": bool,
            "gpgcheck_modified": bool,
            "repo_data": object,
            "override_data": object
        })

        # Change the background color of the no_repos_label_container to the same color
        # as the label's base color. The event container allows us to change the color.
        label_base_color = self.no_repos_label.style.base[gtk.STATE_NORMAL]
        self.no_repos_label_viewport.modify_bg(gtk.STATE_NORMAL, label_base_color)

        # Gnome will hide all button icons by default (gnome setting),
        # so force the icons to show in this case as there is no button
        # text, just the icon.
        gpgcheck_edit_image = gtk.Image()
        gpgcheck_edit_image.set_from_stock(gtk.STOCK_EDIT, gtk.ICON_SIZE_BUTTON)
        self.gpgcheck_edit_button.set_image(gpgcheck_edit_image)
        self.gpgcheck_edit_button.get_image().show()

        gpgcheck_reset_image = gtk.Image()
        gpgcheck_reset_image.set_from_stock(gtk.STOCK_DELETE, gtk.ICON_SIZE_BUTTON)
        self.gpgcheck_remove_button.set_image(gpgcheck_reset_image)
        self.gpgcheck_remove_button.get_image().show()

        self.overrides_treeview.set_model(self.overrides_store)

        self.modified_icon = self.overrides_treeview.render_icon(gtk.STOCK_APPLY,
                                                                 gtk.ICON_SIZE_MENU)

        sortable_cols = []
        enabled_col = CheckBoxColumn(_("Enabled"), self.overrides_store, 'enabled',
            self._on_enable_repo_toggle)
        self.overrides_treeview.append_column(enabled_col)

        repo_id_col = TextTreeViewColumn(self.overrides_store, _("Repository ID"), 'repo_id',
                                         expand=True)
        self.overrides_treeview.append_column(repo_id_col)
        sortable_cols.append((repo_id_col, 'text', 'repo_id'))

        modified_col = gtk.TreeViewColumn(_("Modified"), gtk.CellRendererPixbuf(),
                                          pixbuf=self.overrides_store['modified-icon'])
        self.overrides_treeview.append_column(modified_col)
        sortable_cols.append((modified_col, 'text', 'modified'))

        self.set_sorts(self.overrides_store, sortable_cols)

        self.overrides_treeview.get_selection().connect('changed', self._on_selection)
        self.overrides_treeview.set_rules_hint(True)

        self.gpgcheck_cb_model = gtk.ListStore(str, bool)
        self.gpgcheck_cb_model.append((_("Enabled"), True))
        self.gpgcheck_cb_model.append((_("Disabled"), False))

        gpgcheck_cell_renderer = gtk.CellRendererText()
        self.gpgcheck_combo_box.pack_start(gpgcheck_cell_renderer, True)
        self.gpgcheck_combo_box.add_attribute(gpgcheck_cell_renderer, "text", 0)
        self.gpgcheck_combo_box.set_model(self.gpgcheck_cb_model)
        self.gpgcheck_combo_box.set_active(0)

        self.main_window.set_transient_for(parent)