diff options
Diffstat (limited to 'src/gs-repos-dialog.c')
-rw-r--r-- | src/gs-repos-dialog.c | 890 |
1 files changed, 890 insertions, 0 deletions
diff --git a/src/gs-repos-dialog.c b/src/gs-repos-dialog.c new file mode 100644 index 0000000..53bbf2f --- /dev/null +++ b/src/gs-repos-dialog.c @@ -0,0 +1,890 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2013-2017 Richard Hughes <richard@hughsie.com> + * Copyright (C) 2013 Matthias Clasen <mclasen@redhat.com> + * Copyright (C) 2014-2018 Kalev Lember <klember@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "config.h" + +#include "gs-repos-dialog.h" + +#include "gnome-software-private.h" +#include "gs-common.h" +#include "gs-os-release.h" +#include "gs-repo-row.h" +#include "gs-third-party-repo-row.h" +#include "gs-utils.h" +#include <glib/gi18n.h> + +struct _GsReposDialog +{ + GtkDialog parent_instance; + GSettings *settings; + GsApp *third_party_repo; + + GCancellable *cancellable; + GsPluginLoader *plugin_loader; + GtkWidget *frame_third_party; + GtkWidget *label_description; + GtkWidget *label_empty; + GtkWidget *label_header; + GtkWidget *listbox; + GtkWidget *listbox_third_party; + GtkWidget *row_third_party; + GtkWidget *spinner; + GtkWidget *stack; +}; + +G_DEFINE_TYPE (GsReposDialog, gs_repos_dialog, GTK_TYPE_DIALOG) + +typedef struct { + GsReposDialog *dialog; + GsApp *repo; + GsPluginAction action; +} InstallRemoveData; + +static void +install_remove_data_free (InstallRemoveData *data) +{ + g_clear_object (&data->dialog); + g_clear_object (&data->repo); + g_slice_free (InstallRemoveData, data); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(InstallRemoveData, install_remove_data_free); + +static void reload_sources (GsReposDialog *dialog); +static void reload_third_party_repo (GsReposDialog *dialog); + +static gchar * +get_repo_installed_text (GsApp *repo) +{ + GsAppList *related; + guint cnt_addon = 0; + guint cnt_apps = 0; + g_autofree gchar *addons_text = NULL; + g_autofree gchar *apps_text = NULL; + + related = gs_app_get_related (repo); + for (guint i = 0; i < gs_app_list_length (related); i++) { + GsApp *app_tmp = gs_app_list_index (related, i); + switch (gs_app_get_kind (app_tmp)) { + case AS_APP_KIND_WEB_APP: + case AS_APP_KIND_DESKTOP: + cnt_apps++; + break; + case AS_APP_KIND_FONT: + case AS_APP_KIND_CODEC: + case AS_APP_KIND_INPUT_METHOD: + case AS_APP_KIND_ADDON: + cnt_addon++; + break; + default: + break; + } + } + + if (cnt_apps == 0 && cnt_addon == 0) { + /* nothing! */ + return NULL; + } + if (cnt_addon == 0) { + /* TRANSLATORS: This string is used to construct the 'X applications + installed' sentence, describing a software repository. */ + return g_strdup_printf (ngettext ("%u application installed", + "%u applications installed", + cnt_apps), cnt_apps); + } + if (cnt_apps == 0) { + /* TRANSLATORS: This string is used to construct the 'X add-ons + installed' sentence, describing a software repository. */ + return g_strdup_printf (ngettext ("%u add-on installed", + "%u add-ons installed", + cnt_addon), cnt_addon); + } + + /* TRANSLATORS: This string is used to construct the 'X applications + and y add-ons installed' sentence, describing a software repository. + The correct form here depends on the number of applications. */ + apps_text = g_strdup_printf (ngettext ("%u application", + "%u applications", + cnt_apps), cnt_apps); + /* TRANSLATORS: This string is used to construct the 'X applications + and y add-ons installed' sentence, describing a software repository. + The correct form here depends on the number of add-ons. */ + addons_text = g_strdup_printf (ngettext ("%u add-on", + "%u add-ons", + cnt_addon), cnt_addon); + /* TRANSLATORS: This string is used to construct the 'X applications + and y add-ons installed' sentence, describing a software repository. + The correct form here depends on the total number of + applications and add-ons. */ + return g_strdup_printf (ngettext ("%s and %s installed", + "%s and %s installed", + cnt_apps + cnt_addon), + apps_text, addons_text); +} + +static gboolean +repo_supports_removal (GsApp *repo) +{ + const gchar *management_plugin = gs_app_get_management_plugin (repo); + + /* can't remove a repo, only enable/disable existing ones */ + if (g_strcmp0 (management_plugin, "fwupd") == 0 || + g_strcmp0 (management_plugin, "packagekit") == 0 || + g_strcmp0 (management_plugin, "rpm-ostree") == 0) + return FALSE; + + return TRUE; +} + +static void +repo_enabled_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source); + g_autoptr(InstallRemoveData) install_remove_data = (InstallRemoveData *) user_data; + g_autoptr(GError) error = NULL; + const gchar *action_str; + + action_str = gs_plugin_action_to_string (install_remove_data->action); + + if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) { + if (g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED)) { + g_debug ("repo %s cancelled", action_str); + return; + } + + g_warning ("failed to %s repo: %s", action_str, error->message); + return; + } + + g_debug ("finished %s repo %s", action_str, gs_app_get_id (install_remove_data->repo)); +} + +static void +_enable_repo (InstallRemoveData *install_data) +{ + GsReposDialog *dialog = install_data->dialog; + g_autoptr(GsPluginJob) plugin_job = NULL; + g_debug ("enabling repo %s", gs_app_get_id (install_data->repo)); + plugin_job = gs_plugin_job_newv (install_data->action, + "interactive", TRUE, + "app", install_data->repo, + NULL); + gs_plugin_loader_job_process_async (dialog->plugin_loader, plugin_job, + dialog->cancellable, + repo_enabled_cb, + install_data); +} + +static void +enable_repo_response_cb (GtkDialog *confirm_dialog, + gint response, + gpointer user_data) +{ + g_autoptr(InstallRemoveData) install_data = (InstallRemoveData *) user_data; + + /* unmap the dialog */ + gtk_widget_destroy (GTK_WIDGET (confirm_dialog)); + + /* not agreed */ + if (response != GTK_RESPONSE_OK) + return; + + _enable_repo (g_steal_pointer (&install_data)); +} + +static void +enable_repo (GsReposDialog *dialog, GsApp *repo) +{ + g_autoptr(InstallRemoveData) install_data = NULL; + + install_data = g_slice_new0 (InstallRemoveData); + install_data->action = GS_PLUGIN_ACTION_INSTALL; + install_data->repo = g_object_ref (repo); + install_data->dialog = g_object_ref (dialog); + + /* user needs to confirm acceptance of an agreement */ + if (gs_app_get_agreement (repo) != NULL) { + GtkWidget *confirm_dialog; + g_autofree gchar *message = NULL; + g_autoptr(GError) error = NULL; + + /* convert from AppStream markup */ + message = as_markup_convert_simple (gs_app_get_agreement (repo), &error); + if (message == NULL) { + /* failed, so just try and show the original markup */ + message = g_strdup (gs_app_get_agreement (repo)); + g_warning ("Failed to process AppStream markup: %s", + error->message); + } + + /* ask for confirmation */ + confirm_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + /* TRANSLATORS: window title */ + "%s", _("Enable Third-Party Software Repository?")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (confirm_dialog), + "%s", message); + + /* TRANSLATORS: button to accept the agreement */ + gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), _("Enable"), + GTK_RESPONSE_OK); + + /* handle this async */ + g_signal_connect (confirm_dialog, "response", + G_CALLBACK (enable_repo_response_cb), + g_steal_pointer (&install_data)); + + gtk_window_set_modal (GTK_WINDOW (confirm_dialog), TRUE); + gtk_window_present (GTK_WINDOW (confirm_dialog)); + return; + } + + /* no prompt required */ + _enable_repo (g_steal_pointer (&install_data)); +} + +static void +remove_repo_response_cb (GtkDialog *confirm_dialog, + gint response, + gpointer user_data) +{ + g_autoptr(InstallRemoveData) remove_data = (InstallRemoveData *) user_data; + GsReposDialog *dialog = remove_data->dialog; + g_autoptr(GsPluginJob) plugin_job = NULL; + + /* unmap the dialog */ + gtk_widget_destroy (GTK_WIDGET (confirm_dialog)); + + /* not agreed */ + if (response != GTK_RESPONSE_OK) + return; + + g_debug ("removing repo %s", gs_app_get_id (remove_data->repo)); + plugin_job = gs_plugin_job_newv (remove_data->action, + "interactive", TRUE, + "app", remove_data->repo, + NULL); + gs_plugin_loader_job_process_async (dialog->plugin_loader, plugin_job, + dialog->cancellable, + repo_enabled_cb, + g_steal_pointer (&remove_data)); +} + +static void +remove_confirm_repo (GsReposDialog *dialog, GsApp *repo) +{ + InstallRemoveData *remove_data; + GtkWidget *confirm_dialog; + g_autofree gchar *message = NULL; + g_autofree gchar *title = NULL; + GtkWidget *button; + GtkStyleContext *context; + + remove_data = g_slice_new0 (InstallRemoveData); + remove_data->action = GS_PLUGIN_ACTION_REMOVE; + remove_data->repo = g_object_ref (repo); + remove_data->dialog = g_object_ref (dialog); + + if (repo_supports_removal (repo)) { + /* TRANSLATORS: this is a prompt message, and '%s' is a + * repository name, e.g. 'GNOME Nightly' */ + title = g_strdup_printf (_("Remove “%s”?"), + gs_app_get_name (repo)); + } else { + /* TRANSLATORS: this is a prompt message, and '%s' is a + * repository name, e.g. 'GNOME Nightly' */ + title = g_strdup_printf (_("Disable “%s”?"), + gs_app_get_name (repo)); + } + /* TRANSLATORS: longer dialog text */ + message = g_strdup (_("Software that has been installed from this " + "repository will no longer receive updates, " + "including security fixes.")); + + /* ask for confirmation */ + confirm_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + "%s", title); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (confirm_dialog), + "%s", message); + + if (repo_supports_removal (repo)) { + /* TRANSLATORS: this is button text to remove the repo */ + button = gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), _("Remove"), GTK_RESPONSE_OK); + } else { + /* TRANSLATORS: this is button text to remove the repo */ + button = gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), _("Disable"), GTK_RESPONSE_OK); + } + + context = gtk_widget_get_style_context (button); + gtk_style_context_add_class (context, "destructive-action"); + + /* handle this async */ + g_signal_connect (confirm_dialog, "response", + G_CALLBACK (remove_repo_response_cb), remove_data); + + gtk_window_set_modal (GTK_WINDOW (confirm_dialog), TRUE); + gtk_window_present (GTK_WINDOW (confirm_dialog)); +} + +static void +repo_button_clicked_cb (GsRepoRow *row, + GsReposDialog *dialog) +{ + GsApp *repo; + + repo = gs_repo_row_get_repo (row); + + switch (gs_app_get_state (repo)) { + case AS_APP_STATE_AVAILABLE: + case AS_APP_STATE_AVAILABLE_LOCAL: + enable_repo (dialog, repo); + break; + case AS_APP_STATE_INSTALLED: + remove_confirm_repo (dialog, repo); + break; + default: + g_warning ("repo %s button clicked in unexpected state %s", + gs_app_get_id (repo), + as_app_state_to_string (gs_app_get_state (repo))); + break; + } +} + +static GtkListBox * +get_list_box_for_repo (GsReposDialog *dialog, GsApp *repo) +{ + if (dialog->third_party_repo != NULL) { + const gchar *source_repo; + const gchar *source_third_party_package; + + source_repo = gs_app_get_source_id_default (repo); + source_third_party_package = gs_app_get_source_id_default (dialog->third_party_repo); + + /* group repos from the same repo-release package together */ + if (g_strcmp0 (source_repo, source_third_party_package) == 0) + return GTK_LIST_BOX (dialog->listbox_third_party); + } + + return GTK_LIST_BOX (dialog->listbox); +} + +static void +add_repo (GsReposDialog *dialog, GsApp *repo) +{ + GtkWidget *row; + g_autofree gchar *text = NULL; + AsAppState state; + + state = gs_app_get_state (repo); + if (!(state == AS_APP_STATE_AVAILABLE || + state == AS_APP_STATE_AVAILABLE_LOCAL || + state == AS_APP_STATE_INSTALLED || + state == AS_APP_STATE_INSTALLING || + state == AS_APP_STATE_REMOVING)) { + g_warning ("repo %s in invalid state %s", + gs_app_get_id (repo), + as_app_state_to_string (state)); + return; + } + + row = gs_repo_row_new (); + gs_repo_row_set_name (GS_REPO_ROW (row), + gs_app_get_name (repo)); + text = get_repo_installed_text (repo); + gs_repo_row_set_comment (GS_REPO_ROW (row), text); + gs_repo_row_set_url (GS_REPO_ROW (row), + gs_app_get_url (repo, AS_URL_KIND_HOMEPAGE)); + gs_repo_row_show_status (GS_REPO_ROW (row)); + gs_repo_row_set_repo (GS_REPO_ROW (row), repo); + + g_signal_connect (row, "button-clicked", + G_CALLBACK (repo_button_clicked_cb), dialog); + + gtk_list_box_prepend (get_list_box_for_repo (dialog, repo), row); + gtk_widget_show (row); +} + +static void +third_party_repo_installed_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source); + g_autoptr(InstallRemoveData) install_data = (InstallRemoveData *) user_data; + g_autoptr(GError) error = NULL; + + if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) { + const gchar *action_str = gs_plugin_action_to_string (install_data->action); + + if (g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED)) { + g_debug ("third party repo %s cancelled", action_str); + return; + } + + g_warning ("failed to %s third party repo: %s", action_str, error->message); + return; + } + + reload_sources (install_data->dialog); +} + +static void +install_third_party_repo (GsReposDialog *dialog, gboolean install) +{ + InstallRemoveData *install_data; + g_autoptr(GsPluginJob) plugin_job = NULL; + + install_data = g_slice_new0 (InstallRemoveData); + install_data->dialog = g_object_ref (dialog); + install_data->action = install ? GS_PLUGIN_ACTION_INSTALL : GS_PLUGIN_ACTION_REMOVE; + + plugin_job = gs_plugin_job_newv (install_data->action, + "interactive", TRUE, + "app", dialog->third_party_repo, + NULL); + gs_plugin_loader_job_process_async (dialog->plugin_loader, + plugin_job, + dialog->cancellable, + third_party_repo_installed_cb, + install_data); +} + +static void +third_party_repo_button_clicked_cb (GsThirdPartyRepoRow *row, + gpointer user_data) +{ + GsReposDialog *dialog = (GsReposDialog *) user_data; + GsApp *app; + + app = gs_third_party_repo_row_get_app (row); + + switch (gs_app_get_state (app)) { + case AS_APP_STATE_UNAVAILABLE: + case AS_APP_STATE_AVAILABLE: + case AS_APP_STATE_AVAILABLE_LOCAL: + install_third_party_repo (dialog, TRUE); + break; + case AS_APP_STATE_UPDATABLE_LIVE: + case AS_APP_STATE_UPDATABLE: + case AS_APP_STATE_INSTALLED: + install_third_party_repo (dialog, FALSE); + break; + default: + g_warning ("third party repo %s button clicked in unexpected state %s", + gs_app_get_id (app), + as_app_state_to_string (gs_app_get_state (app))); + break; + } + + g_settings_set_boolean (dialog->settings, "show-nonfree-prompt", FALSE); +} + +static void +refresh_third_party_repo (GsReposDialog *dialog) +{ + if (dialog->third_party_repo == NULL) { + gtk_widget_hide (dialog->frame_third_party); + return; + } + + gtk_widget_show (dialog->frame_third_party); +} + +static void +remove_all_repo_rows_cb (GtkWidget *widget, gpointer user_data) +{ + GtkContainer *container = GTK_CONTAINER (user_data); + + if (GS_IS_REPO_ROW (widget)) + gtk_container_remove (container, widget); +} + +static void +container_remove_all_repo_rows (GtkContainer *container) +{ + gtk_container_foreach (container, remove_all_repo_rows_cb, container); +} + +static void +get_sources_cb (GsPluginLoader *plugin_loader, + GAsyncResult *res, + GsReposDialog *dialog) +{ + GsApp *app; + g_autoptr(GError) error = NULL; + g_autoptr(GsAppList) list = NULL; + + /* get the results */ + list = gs_plugin_loader_job_process_finish (plugin_loader, res, &error); + if (list == NULL) { + if (g_error_matches (error, + GS_PLUGIN_ERROR, + GS_PLUGIN_ERROR_CANCELLED)) { + g_debug ("get sources cancelled"); + return; + } else { + g_warning ("failed to get sources: %s", error->message); + } + gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "empty"); + gtk_style_context_add_class (gtk_widget_get_style_context (dialog->label_header), + "dim-label"); + return; + } + + /* remove previous */ + gs_container_remove_all (GTK_CONTAINER (dialog->listbox)); + container_remove_all_repo_rows (GTK_CONTAINER (dialog->listbox_third_party)); + + /* stop the spinner */ + gs_stop_spinner (GTK_SPINNER (dialog->spinner)); + + /* no results */ + if (gs_app_list_length (list) == 0) { + g_debug ("no sources to show"); + gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "empty"); + gtk_style_context_add_class (gtk_widget_get_style_context (dialog->label_header), "dim-label"); + return; + } + + gtk_style_context_remove_class (gtk_widget_get_style_context (dialog->label_header), + "dim-label"); + + /* add each */ + gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "sources"); + for (guint i = 0; i < gs_app_list_length (list); i++) { + app = gs_app_list_index (list, i); + add_repo (dialog, app); + } +} + +static void +resolve_third_party_repo_cb (GsPluginLoader *plugin_loader, + GAsyncResult *res, + GsReposDialog *dialog) +{ + GsApp *app; + g_autoptr(GError) error = NULL; + g_autoptr(GsAppList) list = NULL; + + /* get the results */ + list = gs_plugin_loader_job_process_finish (plugin_loader, res, &error); + if (list == NULL) { + if (g_error_matches (error, + GS_PLUGIN_ERROR, + GS_PLUGIN_ERROR_CANCELLED)) { + g_debug ("resolve third party repo cancelled"); + return; + } else { + g_warning ("failed to resolve third party repo: %s", error->message); + return; + } + } + + /* we should only get one result */ + if (gs_app_list_length (list) > 0) + app = gs_app_list_index (list, 0); + else + app = NULL; + + g_set_object (&dialog->third_party_repo, app); + gs_third_party_repo_row_set_app (GS_THIRD_PARTY_REPO_ROW (dialog->row_third_party), app); + + /* refresh widget */ + refresh_third_party_repo (dialog); +} + +static void +reload_sources (GsReposDialog *dialog) +{ + g_autoptr(GsPluginJob) plugin_job = NULL; + + /* get the list of non-core software repositories */ + plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_GET_SOURCES, + "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_RELATED | + GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_HOSTNAME, + NULL); + gs_plugin_loader_job_process_async (dialog->plugin_loader, plugin_job, + dialog->cancellable, + (GAsyncReadyCallback) get_sources_cb, + dialog); +} + +static gboolean +is_fedora (void) +{ + const gchar *id = NULL; + g_autoptr(GsOsRelease) os_release = NULL; + + os_release = gs_os_release_new (NULL); + if (os_release == NULL) + return FALSE; + + id = gs_os_release_get_id (os_release); + if (g_strcmp0 (id, "fedora") == 0) + return TRUE; + + return FALSE; +} + +static void +reload_third_party_repo (GsReposDialog *dialog) +{ + const gchar *third_party_repo_package = "fedora-workstation-repositories"; + g_autoptr(GsPluginJob) plugin_job = NULL; + + /* Fedora-specific functionality */ + if (!is_fedora ()) + return; + + plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_SEARCH_PROVIDES, + "search", third_party_repo_package, + "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_SETUP_ACTION | + GS_PLUGIN_REFINE_FLAGS_ALLOW_PACKAGES, + NULL); + gs_plugin_loader_job_process_async (dialog->plugin_loader, plugin_job, + dialog->cancellable, + (GAsyncReadyCallback) resolve_third_party_repo_cb, + dialog); +} + +static void +list_header_func (GtkListBoxRow *row, + GtkListBoxRow *before, + gpointer user_data) +{ + GtkWidget *header = NULL; + if (before != NULL) + header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_list_box_row_set_header (row, header); +} + +static gchar * +get_row_sort_key (GtkListBoxRow *row) +{ + GsApp *app; + guint sort_order; + g_autofree gchar *sort_key = NULL; + + /* sort third party repo rows first */ + if (GS_IS_THIRD_PARTY_REPO_ROW (row)) { + sort_order = 1; + app = gs_third_party_repo_row_get_app (GS_THIRD_PARTY_REPO_ROW (row)); + } else { + sort_order = 2; + app = gs_repo_row_get_repo (GS_REPO_ROW (row)); + } + + if (gs_app_get_name (app) != NULL) { + sort_key = gs_utils_sort_key (gs_app_get_name (app)); + return g_strdup_printf ("%u:%s", sort_order, sort_key); + } else { + return g_strdup_printf ("%u:", sort_order); + } +} + +static gint +list_sort_func (GtkListBoxRow *a, + GtkListBoxRow *b, + gpointer user_data) +{ + g_autofree gchar *key1 = get_row_sort_key (a); + g_autofree gchar *key2 = get_row_sort_key (b); + + /* compare the keys according to the algorithm above */ + return g_strcmp0 (key1, key2); +} + +static void +list_row_activated_cb (GtkListBox *list_box, + GtkListBoxRow *row, + GsReposDialog *dialog) +{ + GtkListBoxRow *other_row; + + if (!GS_IS_REPO_ROW (row)) + return; + + gs_repo_row_show_details (GS_REPO_ROW (row)); + + for (guint i = 0; (other_row = gtk_list_box_get_row_at_index (list_box, i)) != NULL; i++) { + if (!GS_IS_REPO_ROW (other_row)) + continue; + if (other_row == row) + continue; + + gs_repo_row_hide_details (GS_REPO_ROW (other_row)); + } +} + +static gchar * +get_os_name (void) +{ + gchar *name = NULL; + g_autoptr(GsOsRelease) os_release = NULL; + + os_release = gs_os_release_new (NULL); + if (os_release != NULL) + name = g_strdup (gs_os_release_get_name (os_release)); + if (name == NULL) { + /* TRANSLATORS: this is the fallback text we use if we can't + figure out the name of the operating system */ + name = g_strdup (_("the operating system")); + } + + return name; +} + +static void +reload_cb (GsPluginLoader *plugin_loader, GsReposDialog *dialog) +{ + reload_sources (dialog); + reload_third_party_repo (dialog); +} + +static void +set_plugin_loader (GsReposDialog *dialog, GsPluginLoader *plugin_loader) +{ + dialog->plugin_loader = g_object_ref (plugin_loader); + g_signal_connect (dialog->plugin_loader, "reload", + G_CALLBACK (reload_cb), dialog); +} + +static void +gs_repos_dialog_dispose (GObject *object) +{ + GsReposDialog *dialog = GS_REPOS_DIALOG (object); + + if (dialog->plugin_loader != NULL) { + g_signal_handlers_disconnect_by_func (dialog->plugin_loader, reload_cb, dialog); + g_clear_object (&dialog->plugin_loader); + } + + g_cancellable_cancel (dialog->cancellable); + g_clear_object (&dialog->cancellable); + g_clear_object (&dialog->settings); + g_clear_object (&dialog->third_party_repo); + + G_OBJECT_CLASS (gs_repos_dialog_parent_class)->dispose (object); +} + +static void +gs_repos_dialog_init (GsReposDialog *dialog) +{ + g_autofree gchar *label_description_text = NULL; + g_autofree gchar *label_empty_text = NULL; + g_autofree gchar *os_name = NULL; + g_autoptr(GString) str = g_string_new (NULL); + + gtk_widget_init_template (GTK_WIDGET (dialog)); + + dialog->cancellable = g_cancellable_new (); + dialog->settings = g_settings_new ("org.gnome.software"); + + os_name = get_os_name (); + + gtk_list_box_set_header_func (GTK_LIST_BOX (dialog->listbox), + list_header_func, + dialog, + NULL); + gtk_list_box_set_sort_func (GTK_LIST_BOX (dialog->listbox), + list_sort_func, + dialog, NULL); + g_signal_connect (dialog->listbox, "row-activated", + G_CALLBACK (list_row_activated_cb), dialog); + + /* TRANSLATORS: This is the description text displayed in the Software Repositories dialog. + %s gets replaced by the name of the actual distro, e.g. Fedora. */ + label_description_text = g_strdup_printf (_("These repositories supplement the default software provided by %s."), + os_name); + gtk_label_set_text (GTK_LABEL (dialog->label_description), label_description_text); + + /* set up third party repository row */ + gtk_list_box_set_header_func (GTK_LIST_BOX (dialog->listbox_third_party), + list_header_func, + dialog, + NULL); + gtk_list_box_set_sort_func (GTK_LIST_BOX (dialog->listbox_third_party), + list_sort_func, + dialog, NULL); + g_signal_connect (dialog->listbox_third_party, "row-activated", + G_CALLBACK (list_row_activated_cb), dialog); + g_signal_connect (dialog->row_third_party, "button-clicked", + G_CALLBACK (third_party_repo_button_clicked_cb), dialog); + gs_third_party_repo_row_set_name (GS_THIRD_PARTY_REPO_ROW (dialog->row_third_party), + /* TRANSLATORS: info bar title in the software repositories dialog */ + _("Third Party Repositories")); + g_string_append (str, + /* TRANSLATORS: this is the third party repositories info bar. */ + _("Access additional software from selected third party sources.")); + g_string_append (str, " "); + g_string_append (str, + /* TRANSLATORS: this is the third party repositories info bar. */ + _("Some of this software is proprietary and therefore has restrictions on use, sharing, and access to source code.")); + g_string_append_printf (str, " <a href=\"%s\">%s</a>", + "https://fedoraproject.org/wiki/Workstation/Third_Party_Software_Repositories", + /* TRANSLATORS: this is the clickable + * link on the third party repositories info bar */ + _("Find out more…")); + gs_third_party_repo_row_set_comment (GS_THIRD_PARTY_REPO_ROW (dialog->row_third_party), str->str); + refresh_third_party_repo (dialog); + + /* TRANSLATORS: This is the description text displayed in the Software Repositories dialog. + %s gets replaced by the name of the actual distro, e.g. Fedora. */ + label_empty_text = g_strdup_printf (_("These repositories supplement the default software provided by %s."), + os_name); + gtk_label_set_text (GTK_LABEL (dialog->label_empty), label_empty_text); +} + +static void +gs_repos_dialog_class_init (GsReposDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gs_repos_dialog_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-repos-dialog.ui"); + + gtk_widget_class_bind_template_child (widget_class, GsReposDialog, frame_third_party); + gtk_widget_class_bind_template_child (widget_class, GsReposDialog, label_description); + gtk_widget_class_bind_template_child (widget_class, GsReposDialog, label_empty); + gtk_widget_class_bind_template_child (widget_class, GsReposDialog, label_header); + gtk_widget_class_bind_template_child (widget_class, GsReposDialog, listbox); + gtk_widget_class_bind_template_child (widget_class, GsReposDialog, listbox_third_party); + gtk_widget_class_bind_template_child (widget_class, GsReposDialog, row_third_party); + gtk_widget_class_bind_template_child (widget_class, GsReposDialog, spinner); + gtk_widget_class_bind_template_child (widget_class, GsReposDialog, stack); +} + +GtkWidget * +gs_repos_dialog_new (GtkWindow *parent, GsPluginLoader *plugin_loader) +{ + GsReposDialog *dialog; + + dialog = g_object_new (GS_TYPE_REPOS_DIALOG, + "use-header-bar", TRUE, + "transient-for", parent, + "modal", TRUE, + NULL); + set_plugin_loader (dialog, plugin_loader); + gtk_stack_set_visible_child_name (GTK_STACK (dialog->stack), "waiting"); + gs_start_spinner (GTK_SPINNER (dialog->spinner)); + reload_sources (dialog); + reload_third_party_repo (dialog); + + return GTK_WIDGET (dialog); +} |