[gnome-software/wip/hughsie/non-free: 6/6] Add functionality to enable non-free sources



commit f135e7be891235e7d17c98f7b6af2f0103d77064
Author: Richard Hughes <richard hughsie com>
Date:   Mon Aug 29 19:46:12 2016 +0100

    Add functionality to enable non-free sources

 data/org.gnome.software.gschema.xml |   12 +++
 src/gs-shell-overview.c             |  167 +++++++++++++++++++++++++++++++
 src/gs-shell-overview.ui            |   73 ++++++++++++++
 src/gs-sources-dialog.c             |  187 +++++++++++++++++++++++++++++++++++
 src/gs-sources-dialog.ui            |   27 ++++-
 5 files changed, 461 insertions(+), 5 deletions(-)
---
diff --git a/data/org.gnome.software.gschema.xml b/data/org.gnome.software.gschema.xml
index 0677bc9..1d5ed1b 100644
--- a/data/org.gnome.software.gschema.xml
+++ b/data/org.gnome.software.gschema.xml
@@ -90,9 +90,21 @@
       <default>true</default>
       <summary>Show some UI elements informing the user that an app is non-free</summary>
     </key>
+    <key name="show-nonfree-prompt" type="b">
+      <default>true</default>
+      <summary>Show the prompt to install nonfree software sources</summary>
+    </key>
+    <key name="show-nonfree-software" type="b">
+      <default>false</default>
+      <summary>Show non-free software in search results</summary>
+    </key>
     <key name="nonfree-software-uri" type="s">
       <default>'https://en.wikipedia.org/wiki/Proprietary_software'</default>
       <summary>The URI that explains nonfree and proprietary software</summary>
     </key>
+    <key name="nonfree-sources" type="as">
+      <default>['google-chrome']</default>
+      <summary>A list of non-free sources that can be optionally enabled</summary>
+    </key>
   </schema>
 </schemalist>
diff --git a/src/gs-shell-overview.c b/src/gs-shell-overview.c
index 57b4920..cf37f56 100644
--- a/src/gs-shell-overview.c
+++ b/src/gs-shell-overview.c
@@ -53,7 +53,10 @@ typedef struct
        gchar                   *category_of_day;
        GtkWidget               *search_button;
        GHashTable              *category_hash;         /* id : GsCategory */
+       GSettings               *settings;
 
+       GtkWidget               *infobar_proprietary;
+       GtkWidget               *label_proprietary;
        GtkWidget               *bin_featured;
        GtkWidget               *box_overview;
        GtkWidget               *box_popular;
@@ -604,6 +607,141 @@ gs_shell_overview_categories_expander_cb (GtkButton *button, GsShellOverview *se
        gtk_revealer_set_reveal_child (GTK_REVEALER (priv->categories_more), TRUE);
 }
 
+static gchar *
+gs_utils_build_source_unique_id (const gchar *id)
+{
+       if (as_utils_unique_id_valid (id))
+               return g_strdup (id);
+       return as_utils_unique_id_build (AS_APP_SCOPE_UNKNOWN,
+                                        AS_BUNDLE_KIND_UNKNOWN,
+                                        NULL,
+                                        AS_APP_KIND_SOURCE,
+                                        id,
+                                        NULL);
+}
+
+static void
+g_shell_overview_get_sources_cb (GsPluginLoader *plugin_loader,
+                                GAsyncResult *res,
+                                GsShellOverview *self)
+{
+       guint i;
+       g_auto(GStrv) nonfree_ids = NULL;
+       g_autoptr(GError) error = NULL;
+       g_autoptr(GsAppList) list = NULL;
+       GsShellOverviewPrivate *priv = gs_shell_overview_get_instance_private (self);
+
+       /* get the results */
+       list = gs_plugin_loader_get_sources_finish (plugin_loader, res, &error);
+       if (list == NULL) {
+               if (g_error_matches (error,
+                                    G_IO_ERROR,
+                                    G_IO_ERROR_CANCELLED)) {
+                       g_debug ("get sources cancelled");
+               } else {
+                       g_warning ("failed to get sources: %s", error->message);
+               }
+               return;
+       }
+
+       /* enable each */
+       nonfree_ids = g_settings_get_strv (priv->settings, "nonfree-sources");
+       for (i = 0; nonfree_ids[i] != NULL; i++) {
+               GsApp *app;
+               g_autofree gchar *unique_id = NULL;
+
+               /* match the ID from GSettings to an actual GsApp */
+               unique_id = gs_utils_build_source_unique_id (nonfree_ids[i]);
+               app = gs_app_list_lookup (list, unique_id);
+               if (app == NULL) {
+                       g_warning ("no source for %s", unique_id);
+                       continue;
+               }
+
+               /* depending on the new policy, add or remove the source */
+               if (g_settings_get_boolean (priv->settings, "show-nonfree-software")) {
+                       if (gs_app_get_state (app) == AS_APP_STATE_AVAILABLE) {
+                               g_error ("not ready yet");
+                               gs_page_install_app (GS_PAGE (self), app,
+                                                    priv->cancellable);
+                       }
+               } else {
+                       if (gs_app_get_state (app) == AS_APP_STATE_INSTALLED) {
+                               gs_page_remove_app (GS_PAGE (self), app,
+                                                   priv->cancellable);
+                       }
+               }
+       }
+}
+
+static void
+g_shell_overview_rescan_proprietary_sources (GsShellOverview *self)
+{
+       GsShellOverviewPrivate *priv = gs_shell_overview_get_instance_private (self);
+       gs_plugin_loader_get_sources_async (priv->plugin_loader,
+                                           GS_PLUGIN_REFINE_FLAGS_REQUIRE_SETUP_ACTION,
+                                           priv->cancellable,
+                                           (GAsyncReadyCallback) g_shell_overview_get_sources_cb,
+                                           self);
+}
+
+static void
+g_shell_overview_proprietary_response_cb (GtkInfoBar *info_bar,
+                                         gint response_id,
+                                         GsShellOverview *self)
+{
+       GsShellOverviewPrivate *priv = gs_shell_overview_get_instance_private (self);
+       g_settings_set_boolean (priv->settings, "show-nonfree-prompt", FALSE);
+       if (response_id == GTK_RESPONSE_CLOSE) {
+               gtk_widget_hide (priv->infobar_proprietary);
+               return;
+       }
+       if (response_id != GTK_RESPONSE_YES)
+               return;
+       g_settings_set_boolean (priv->settings, "show-nonfree-software", TRUE);
+
+       /* actually call into the plugin loader and do the action */
+       g_shell_overview_rescan_proprietary_sources (self);
+}
+
+static void
+gs_shell_overview_refresh_proprietary (GsShellOverview *self)
+{
+       GsShellOverviewPrivate *priv = gs_shell_overview_get_instance_private (self);
+       g_auto(GStrv) nonfree_ids = NULL;
+
+       /* only show if never prompted and have nonfree repos */
+       nonfree_ids = g_settings_get_strv (priv->settings, "nonfree-sources");
+       if (g_settings_get_boolean (priv->settings, "show-nonfree-prompt") &&
+           !g_settings_get_boolean (priv->settings, "show-nonfree-software") &&
+           g_strv_length (nonfree_ids) > 0) {
+               g_autoptr(GString) str = g_string_new (NULL);
+               g_autofree gchar *uri = NULL;
+
+               /* get from GSettings, as some distros want to override this */
+               uri = g_settings_get_string (priv->settings, "nonfree-software-uri");
+
+               /* TRANSLATORS: this is the proprietary info bar */
+               g_string_append (str, _("Provides access to additional software, "
+                                       "including web browsers and games."));
+               g_string_append (str, " ");
+               /* TRANSLATORS: this is the proprietary info bar */
+               g_string_append (str, _("Proprietary software has restrictions "
+                                       "on use and access to source code."));
+               if (uri != NULL && uri[0] != '\0') {
+                       g_string_append (str, "\n");
+                       g_string_append_printf (str, "<a href=\"%s\">%s</a>",
+                                               /* TRANSLATORS: this is the clickable
+                                                * link on the proprietary info bar */
+                                               uri, _("Find out more…"));
+               }
+               gtk_label_set_markup (GTK_LABEL (priv->label_proprietary), str->str);
+               gtk_widget_set_visible (priv->infobar_proprietary, TRUE);
+       } else {
+               gtk_widget_set_visible (priv->infobar_proprietary, FALSE);
+       }
+}
+
 void
 gs_shell_overview_setup (GsShellOverview *self,
                         GsShell *shell,
@@ -625,6 +763,14 @@ gs_shell_overview_setup (GsShellOverview *self,
        priv->category_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                     g_free, (GDestroyNotify) g_object_unref);
 
+       /* create info bar if not already dismissed in initial-setup */
+       gs_shell_overview_refresh_proprietary (self);
+       gtk_info_bar_add_button (GTK_INFO_BAR (priv->infobar_proprietary),
+                                /* TRANSLATORS: button to turn on proprietary software sources */
+                                _("Enable"), GTK_RESPONSE_YES);
+       g_signal_connect (priv->infobar_proprietary, "response",
+                         G_CALLBACK (g_shell_overview_proprietary_response_cb), self);
+
        /* avoid a ref cycle */
        priv->shell = shell;
 
@@ -657,9 +803,27 @@ gs_shell_overview_setup (GsShellOverview *self,
 }
 
 static void
+settings_changed_cb (GSettings *settings,
+                    const gchar *key,
+                    GsShellOverview *self)
+{
+       if (g_strcmp0 (key, "show-nonfree-software") == 0 ||
+           g_strcmp0 (key, "show-nonfree-prompt") == 0 ||
+           g_strcmp0 (key, "nonfree-software-uri") == 0 ||
+           g_strcmp0 (key, "nonfree-sources") == 0) {
+               gs_shell_overview_refresh_proprietary (self);
+       }
+}
+
+static void
 gs_shell_overview_init (GsShellOverview *self)
 {
+       GsShellOverviewPrivate *priv = gs_shell_overview_get_instance_private (self);
        gtk_widget_init_template (GTK_WIDGET (self));
+       priv->settings = g_settings_new ("org.gnome.software");
+       g_signal_connect (priv->settings, "changed",
+                         G_CALLBACK (settings_changed_cb),
+                         self);
 }
 
 static void
@@ -671,6 +835,7 @@ gs_shell_overview_dispose (GObject *object)
        g_clear_object (&priv->builder);
        g_clear_object (&priv->plugin_loader);
        g_clear_object (&priv->cancellable);
+       g_clear_object (&priv->settings);
        g_clear_pointer (&priv->category_of_day, g_free);
        g_clear_pointer (&priv->category_hash, g_hash_table_unref);
 
@@ -710,6 +875,8 @@ gs_shell_overview_class_init (GsShellOverviewClass *klass)
 
        gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Software/gs-shell-overview.ui");
 
+       gtk_widget_class_bind_template_child_private (widget_class, GsShellOverview, infobar_proprietary);
+       gtk_widget_class_bind_template_child_private (widget_class, GsShellOverview, label_proprietary);
        gtk_widget_class_bind_template_child_private (widget_class, GsShellOverview, bin_featured);
        gtk_widget_class_bind_template_child_private (widget_class, GsShellOverview, box_overview);
        gtk_widget_class_bind_template_child_private (widget_class, GsShellOverview, box_popular);
diff --git a/src/gs-shell-overview.ui b/src/gs-shell-overview.ui
index 18d2f1c..ab9e1ff 100644
--- a/src/gs-shell-overview.ui
+++ b/src/gs-shell-overview.ui
@@ -15,6 +15,79 @@
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="orientation">vertical</property>
+
+            <child>
+              <object class="GtkInfoBar" id="infobar_proprietary">
+                <property name="visible">True</property>
+                <property name="app_paintable">True</property>
+                <property name="can_focus">False</property>
+                <property name="show_close_button">True</property>
+                <child internal-child="action_area">
+                  <object class="GtkButtonBox">
+                    <property name="can_focus">False</property>
+                    <property name="spacing">6</property>
+                    <property name="layout_style">end</property>
+                    <child/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child internal-child="content_area">
+                  <object class="GtkBox">
+                    <property name="can_focus">False</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
+                    <property name="border_width">12</property>
+                    <child>
+                      <object class="GtkLabel" id="label_proprietary_title">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes">Enable Proprietary Software 
Sources?</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_proprietary">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label">Provides access to additional software.</property>
+                        <property name="wrap">True</property>
+                        <property name="wrap_mode">word-char</property>
+                        <property name="xalign">0</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+
             <child>
               <object class="GtkScrolledWindow" id="scrolledwindow_overview">
                 <property name="visible">True</property>
diff --git a/src/gs-sources-dialog.c b/src/gs-sources-dialog.c
index e9c2496..94d0cd1 100644
--- a/src/gs-sources-dialog.c
+++ b/src/gs-sources-dialog.c
@@ -25,6 +25,7 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
+#include "gs-app-list.h"
 #include "gs-sources-dialog.h"
 #include "gs-os-release.h"
 #include "gs-sources-dialog-row.h"
@@ -33,17 +34,22 @@
 struct _GsSourcesDialog
 {
        GtkDialog        parent_instance;
+       GSettings       *settings;
+       GsAppList       *source_list;
 
        GCancellable    *cancellable;
        GsPluginLoader  *plugin_loader;
        GtkWidget       *button_back;
        GtkWidget       *button_remove;
+       GtkWidget       *frame_proprietary;
        GtkWidget       *grid_noresults;
        GtkWidget       *label2;
        GtkWidget       *label_empty;
        GtkWidget       *label_header;
        GtkWidget       *listbox;
        GtkWidget       *listbox_apps;
+       GtkWidget       *listbox_proprietary;
+       GtkWidget       *row_proprietary;
        GtkWidget       *scrolledwindow_apps;
        GtkWidget       *spinner;
        GtkWidget       *stack;
@@ -51,6 +57,8 @@ struct _GsSourcesDialog
 
 G_DEFINE_TYPE (GsSourcesDialog, gs_sources_dialog, GTK_TYPE_DIALOG)
 
+static void reload_sources (GsSourcesDialog *dialog);
+
 static gchar *
 get_source_installed_text (GPtrArray *sources)
 {
@@ -155,6 +163,157 @@ add_source (GtkListBox *listbox, GsApp *app)
        gtk_widget_show (row);
 }
 
+static gchar *
+gs_utils_build_source_unique_id (const gchar *id)
+{
+       if (as_utils_unique_id_valid (id))
+               return g_strdup (id);
+       return as_utils_unique_id_build (AS_APP_SCOPE_UNKNOWN,
+                                        AS_BUNDLE_KIND_UNKNOWN,
+                                        NULL,
+                                        AS_APP_KIND_SOURCE,
+                                        id,
+                                        NULL);
+}
+
+static void
+source_modified_cb (GObject *source,
+                   GAsyncResult *res,
+                   gpointer user_data)
+{
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+       GsSourcesDialog *dialog = GS_SOURCES_DIALOG (user_data);
+       g_autoptr(GError) error = NULL;
+
+       if (!gs_plugin_loader_app_action_finish (plugin_loader, res, &error)) {
+               g_warning ("failed to remove: %s", error->message);
+       } else {
+               reload_sources (dialog);
+       }
+}
+
+static void
+gs_sources_dialog_rescan_proprietary_sources (GsSourcesDialog *dialog)
+{
+       guint i;
+       g_auto(GStrv) nonfree_ids = NULL;
+
+       nonfree_ids = g_settings_get_strv (dialog->settings, "nonfree-sources");
+       for (i = 0; nonfree_ids[i] != NULL; i++) {
+               GsApp *app;
+               g_autofree gchar *unique_id = NULL;
+               unique_id = gs_utils_build_source_unique_id (nonfree_ids[i]);
+               app = gs_app_list_lookup (dialog->source_list, unique_id);
+               if (app == NULL) {
+                       g_warning ("no source for %s", unique_id);
+                       continue;
+               }
+
+               /* depending on the new policy, add or remove the source */
+               if (g_settings_get_boolean (dialog->settings, "show-nonfree-software")) {
+                       if (gs_app_get_state (app) == AS_APP_STATE_AVAILABLE) {
+                               gs_plugin_loader_app_action_async (dialog->plugin_loader,
+                                                                  app,
+                                                                  GS_PLUGIN_LOADER_ACTION_INSTALL,
+                                                                  dialog->cancellable,
+                                                                  source_modified_cb,
+                                                                  dialog);
+                       }
+               } else {
+                       g_error ("Don't leave me this way!");
+                       if (gs_app_get_state (app) == AS_APP_STATE_INSTALLED) {
+                               gs_plugin_loader_app_action_async (dialog->plugin_loader,
+                                                                  app,
+                                                                  GS_PLUGIN_LOADER_ACTION_REMOVE,
+                                                                  dialog->cancellable,
+                                                                  source_modified_cb,
+                                                                  dialog);
+                       }
+               }
+       }
+}
+
+static void
+gs_sources_dialog_switch_active_cb (GsSourcesDialogRow *row,
+                                   GParamSpec *pspec,
+                                   GsSourcesDialog *dialog)
+{
+       gboolean active = gs_sources_dialog_row_get_switch_active (row);
+       g_settings_set_boolean (dialog->settings, "show-nonfree-software", active);
+       g_settings_set_boolean (dialog->settings, "show-nonfree-prompt", FALSE);
+}
+
+static void
+gs_sources_dialog_refresh_proprietary_apps (GsSourcesDialog *dialog)
+{
+       GtkWidget *row;
+       gboolean switch_active;
+       guint i;
+       g_autofree gchar *text = NULL;
+       g_autofree gchar *uri = NULL;
+       g_auto(GStrv) nonfree_ids = NULL;
+       g_autoptr(GPtrArray) sources = g_ptr_array_new ();
+       g_autoptr(GString) str = g_string_new (NULL);
+
+       /* get from GSettings, as some distros want to override this */
+       nonfree_ids = g_settings_get_strv (dialog->settings, "nonfree-sources");
+       if (g_strv_length (nonfree_ids) == 0) {
+               gtk_widget_hide (dialog->frame_proprietary);
+               return;
+       }
+
+       /* TRANSLATORS: nonfree software */
+       g_string_append (str, _("Typically has restrictions on use and "
+                               "access to source code."));
+       g_string_append (str, " ");
+
+       /* optional URL */
+       uri = g_settings_get_string (dialog->settings, "nonfree-software-uri");
+       if (uri != NULL) {
+               g_string_append_printf (str, "<a href=\"%s\">%s</a>", uri,
+                                       /* TRANSLATORS: this is the clickable
+                                        * link on the proprietary info bar */
+                                       _("Find out more…"));
+       }
+
+       /* add row */
+       if (dialog->row_proprietary == NULL) {
+               dialog->row_proprietary = gs_sources_dialog_row_new ();
+               g_signal_connect (dialog->row_proprietary, "notify::switch-active",
+                                 G_CALLBACK (gs_sources_dialog_switch_active_cb),
+                                 dialog);
+               gs_sources_dialog_row_set_name (GS_SOURCES_DIALOG_ROW (dialog->row_proprietary),
+                                               /* TRANSLATORS: list header */
+                                               _("Proprietary Software Sources"));
+               gs_sources_dialog_row_set_switch_enabled (GS_SOURCES_DIALOG_ROW (dialog->row_proprietary), 
TRUE);
+               gtk_list_box_prepend (GTK_LIST_BOX (dialog->listbox_proprietary), dialog->row_proprietary);
+               gtk_widget_show (dialog->row_proprietary);
+       }
+       gs_sources_dialog_row_set_comment (GS_SOURCES_DIALOG_ROW (dialog->row_proprietary), str->str);
+
+       /* get all the proprietary sources */
+       for (i = 0; nonfree_ids[i] != NULL; i++) {
+               GsApp *app;
+               g_autofree gchar *unique_id = NULL;
+               unique_id = gs_utils_build_source_unique_id (nonfree_ids[i]);
+               app = gs_app_list_lookup (dialog->source_list, unique_id);
+               if (app == NULL) {
+                       g_warning ("no source for %s", unique_id);
+                       continue;
+               }
+               g_ptr_array_add (sources, app);
+       }
+       text = get_source_installed_text (sources);
+       gs_sources_dialog_row_set_description (GS_SOURCES_DIALOG_ROW (dialog->row_proprietary),
+                                              text);
+
+       /* if the user opted in then show the switch as active */
+       switch_active = g_settings_get_boolean (dialog->settings, "show-nonfree-software");
+       gs_sources_dialog_row_set_switch_active (GS_SOURCES_DIALOG_ROW (dialog->row_proprietary),
+                                                switch_active);
+       gtk_widget_show (dialog->frame_proprietary);
+}
+
 static void
 get_sources_cb (GsPluginLoader *plugin_loader,
                GAsyncResult *res,
@@ -200,7 +359,11 @@ get_sources_cb (GsPluginLoader *plugin_loader,
                if (gs_app_get_state (app) != AS_APP_STATE_INSTALLED)
                        continue;
                add_source (GTK_LIST_BOX (dialog->listbox), app);
+               gs_app_list_add (dialog->source_list, app);
        }
+
+       /* refresh widget */
+       gs_sources_dialog_refresh_proprietary_apps (dialog);
 }
 
 static void
@@ -441,6 +604,19 @@ set_plugin_loader (GsSourcesDialog *dialog, GsPluginLoader *plugin_loader)
 }
 
 static void
+settings_changed_cb (GSettings *settings,
+                    const gchar *key,
+                    GsSourcesDialog *dialog)
+{
+       if (g_strcmp0 (key, "show-nonfree-software") == 0 ||
+           g_strcmp0 (key, "nonfree-software-uri") == 0 ||
+           g_strcmp0 (key, "nonfree-sources") == 0) {
+               gs_sources_dialog_refresh_proprietary_apps (dialog);
+               gs_sources_dialog_rescan_proprietary_sources (dialog);
+       }
+}
+
+static void
 gs_sources_dialog_dispose (GObject *object)
 {
        GsSourcesDialog *dialog = GS_SOURCES_DIALOG (object);
@@ -454,6 +630,8 @@ gs_sources_dialog_dispose (GObject *object)
                g_cancellable_cancel (dialog->cancellable);
                g_clear_object (&dialog->cancellable);
        }
+       g_clear_object (&dialog->settings);
+       g_clear_object (&dialog->source_list);
 
        G_OBJECT_CLASS (gs_sources_dialog_parent_class)->dispose (object);
 }
@@ -466,7 +644,12 @@ gs_sources_dialog_init (GsSourcesDialog *dialog)
 
        gtk_widget_init_template (GTK_WIDGET (dialog));
 
+       dialog->source_list = gs_app_list_new ();
        dialog->cancellable = g_cancellable_new ();
+       dialog->settings = g_settings_new ("org.gnome.software");
+       g_signal_connect (dialog->settings, "changed",
+                         G_CALLBACK (settings_changed_cb),
+                         dialog);
 
        gtk_list_box_set_header_func (GTK_LIST_BOX (dialog->listbox),
                                      list_header_func,
@@ -486,6 +669,8 @@ gs_sources_dialog_init (GsSourcesDialog *dialog)
                                    list_sort_func,
                                    dialog, NULL);
 
+       gs_sources_dialog_refresh_proprietary_apps (dialog);
+
        os_name = get_os_name ();
        /* TRANSLATORS: This is the text displayed in the Software Sources
           dialog when no OS-provided software sources are enabled. %s gets
@@ -518,12 +703,14 @@ gs_sources_dialog_class_init (GsSourcesDialogClass *klass)
 
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, button_back);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, button_remove);
+       gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, frame_proprietary);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, grid_noresults);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, label2);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, label_empty);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, label_header);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, listbox);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, listbox_apps);
+       gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, listbox_proprietary);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, scrolledwindow_apps);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, spinner);
        gtk_widget_class_bind_template_child (widget_class, GsSourcesDialog, stack);
diff --git a/src/gs-sources-dialog.ui b/src/gs-sources-dialog.ui
index e9a7977..92fc490 100644
--- a/src/gs-sources-dialog.ui
+++ b/src/gs-sources-dialog.ui
@@ -43,7 +43,7 @@
           <object class="GtkLabel" id="label_header">
             <property name="can_focus">False</property>
             <property name="visible">True</property>
-            <property name="label" translatable="yes">Software Sources</property>
+            <property name="label" translatable="yes">Additional Software Sources</property>
             <property name="selectable">False</property>
             <style>
               <class name="title"/>
@@ -130,14 +130,34 @@
                 <property name="margin_bottom">32</property>
                 <property name="orientation">vertical</property>
                 <property name="spacing">4</property>
+
+                <child>
+                  <object class="GtkFrame" id="frame_proprietary">
+                    <property name="visible">True</property>
+                    <property name="shadow_type">in</property>
+                    <property name="halign">fill</property>
+                    <property name="valign">start</property>
+                    <property name="margin_bottom">16</property>
+                    <child>
+                      <object class="GtkListBox" id="listbox_proprietary">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="selection_mode">none</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
                 <child>
                   <object class="GtkLabel" id="label1">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="xalign">0</property>
-                    <property name="label" translatable="yes">Software sources give you access to additional 
software.</property>
+                    <property name="label" translatable="yes">Additional Sources</property>
                     <property name="wrap">True</property>
                     <property name="max_width_chars">30</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
                     <style>
                       <class name="dim-label"/>
                     </style>
@@ -145,7 +165,6 @@
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
-                    <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
@@ -163,7 +182,6 @@
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
-                    <property name="position">1</property>
                   </packing>
                 </child>
                 <child>
@@ -193,7 +211,6 @@
                   <packing>
                     <property name="expand">True</property>
                     <property name="fill">True</property>
-                    <property name="position">2</property>
                   </packing>
                 </child>
               </object>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]