[gnome-software/1005-add-option-for-packaging-format-preference: 7/7] gs-details-page: Add option for packaging format preference
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/1005-add-option-for-packaging-format-preference: 7/7] gs-details-page: Add option for packaging format preference
- Date: Tue, 27 Jul 2021 15:55:19 +0000 (UTC)
commit 294a10419ce02e71553bc459aad471fa2cffe7cf
Author: Milan Crha <mcrha redhat com>
Date: Tue Jul 27 12:33:31 2021 +0200
gs-details-page: Add option for packaging format preference
The details page preselects the origin based on the preference
of the packaging format, which can be set by a user or by a packager.
Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1005
data/org.gnome.software.gschema.xml | 4 ++
src/gs-details-page.c | 135 +++++++++++++++++++++++++++++++++++-
2 files changed, 137 insertions(+), 2 deletions(-)
---
diff --git a/data/org.gnome.software.gschema.xml b/data/org.gnome.software.gschema.xml
index 36a219c12..90aed03c6 100644
--- a/data/org.gnome.software.gschema.xml
+++ b/data/org.gnome.software.gschema.xml
@@ -139,6 +139,10 @@
<default>false</default>
<summary>Install the AppStream files to a system-wide location for all users</summary>
</key>
+ <key name="packaging-format-preference" type="as">
+ <default>['']</default>
+ <summary>Priority order of packaging formats to prefer, with more important formats listed first. An
empty array means the default order. Omitted formats are assumed to be listed last.</summary>
+ </key>
<child name="auth" schema="org.gnome.software.auth"/>
</schema>
<schema id="org.gnome.software.auth" gettext-domain="gnome-software">
diff --git a/src/gs-details-page.c b/src/gs-details-page.c
index b40ed8b83..7f8f2b9d8 100644
--- a/src/gs-details-page.c
+++ b/src/gs-details-page.c
@@ -79,6 +79,9 @@ struct _GsDetailsPage
GtkSizeGroup *size_group_origin_popover;
GsOdrsProvider *odrs_provider; /* (nullable) (owned), NULL if reviews are disabled */
GAppInfoMonitor *app_info_monitor; /* (owned) */
+ GHashTable *packaging_format_preference; /* gchar * ~> gint */
+ gboolean origin_by_packaging_format; /* when TRUE, change the 'app' to the most
preferred
+ packaging format when the alternatives are
found */
GtkWidget *application_details_icon;
GtkWidget *application_details_summary;
@@ -648,6 +651,36 @@ app_origin_equal (GsApp *a,
return TRUE;
}
+static gint
+sort_by_packaging_format_preference (GsApp *app1,
+ GsApp *app2,
+ gpointer user_data)
+{
+ GHashTable *preference = user_data;
+ const gchar *packaging_format_raw1 = gs_app_get_packaging_format_raw (app1);
+ const gchar *packaging_format_raw2 = gs_app_get_packaging_format_raw (app2);
+ gint index1, index2;
+
+ if (g_strcmp0 (packaging_format_raw1, packaging_format_raw2) == 0)
+ return 0;
+
+ if (packaging_format_raw1 == NULL || packaging_format_raw2 == NULL)
+ return packaging_format_raw1 == NULL ? -1 : 1;
+
+ index1 = GPOINTER_TO_INT (g_hash_table_lookup (preference, packaging_format_raw1));
+ index2 = GPOINTER_TO_INT (g_hash_table_lookup (preference, packaging_format_raw2));
+
+ if (index1 == index2)
+ return 0;
+
+ /* Index 0 means unspecified packaging format in the preference array,
+ thus move these at the end. */
+ if (index1 == 0 || index2 == 0)
+ return index1 == 0 ? 1 : -1;
+
+ return index1 - index2;
+}
+
static void
gs_details_page_get_alternates_cb (GObject *source_object,
GAsyncResult *res,
@@ -659,7 +692,12 @@ gs_details_page_get_alternates_cb (GObject *source_object,
g_autoptr(GsAppList) list = NULL;
g_autofree gchar *origin_ui = NULL;
gboolean instance_changed = FALSE;
+ gboolean origin_by_packaging_format = self->origin_by_packaging_format;
+ GtkWidget *select_row = NULL;
+ GtkWidget *origin_row_by_packaging_format = NULL;
+ gint origin_row_by_packaging_format_index = 0;
+ self->origin_by_packaging_format = FALSE;
gs_container_remove_all (GTK_CONTAINER (self->origin_popover_list_box));
/* Did we switch away from the page in the meantime? */
@@ -710,8 +748,11 @@ gs_details_page_get_alternates_cb (GObject *source_object,
/* add the local file to the list so that we can carry it over when
* switching between alternates */
- if (self->app_local_file != NULL)
+ if (self->app_local_file != NULL) {
gs_app_list_add (list, self->app_local_file);
+ /* Do not allow change of the app by the packaging format when it's a local file */
+ origin_by_packaging_format = FALSE;
+ }
/* no alternates to show */
if (gs_app_list_length (list) < 2) {
@@ -719,6 +760,14 @@ gs_details_page_get_alternates_cb (GObject *source_object,
return;
}
+ /* Do not allow change of the app by the packaging format when it's installed */
+ origin_by_packaging_format = origin_by_packaging_format &&
+ self->app != NULL && gs_app_get_state (self->app) != GS_APP_STATE_INSTALLED;
+
+ /* Sort the alternates by the user's packaging preferences */
+ if (g_hash_table_size (self->packaging_format_preference) > 0)
+ gs_app_list_sort (list, sort_by_packaging_format_preference,
self->packaging_format_preference);
+
for (guint i = 0; i < gs_app_list_length (list); i++) {
GsApp *app = gs_app_list_index (list, i);
GtkWidget *row = gs_origin_popover_row_new (app);
@@ -735,13 +784,36 @@ gs_details_page_get_alternates_cb (GObject *source_object,
self->app = g_object_ref (app);
instance_changed = TRUE;
}
- gs_origin_popover_row_set_selected (GS_ORIGIN_POPOVER_ROW (row), TRUE);
+ select_row = row;
}
gs_origin_popover_row_set_size_group (GS_ORIGIN_POPOVER_ROW (row),
self->size_group_origin_popover);
gtk_container_add (GTK_CONTAINER (self->origin_popover_list_box), row);
+
+ if (origin_by_packaging_format) {
+ const gchar *packaging_format = gs_app_get_packaging_format_raw (app);
+ gint index = GPOINTER_TO_INT (g_hash_table_lookup (self->packaging_format_preference,
packaging_format));
+ if (index > 0 && (index < origin_row_by_packaging_format_index ||
origin_row_by_packaging_format_index == 0)) {
+ origin_row_by_packaging_format_index = index;
+ origin_row_by_packaging_format = row;
+ }
+ }
+ }
+
+ if (origin_row_by_packaging_format) {
+ GsOriginPopoverRow *row = GS_ORIGIN_POPOVER_ROW (origin_row_by_packaging_format);
+ GsApp *app = gs_origin_popover_row_get_app (row);
+ select_row = origin_row_by_packaging_format;
+ if (app != self->app) {
+ g_clear_object (&self->app);
+ self->app = g_object_ref (app);
+ instance_changed = TRUE;
+ }
}
+ if (select_row)
+ gs_origin_popover_row_set_selected (GS_ORIGIN_POPOVER_ROW (select_row), TRUE);
+
origin_ui = gs_app_get_origin_ui (self->app);
if (origin_ui != NULL)
gtk_label_set_text (GTK_LABEL (self->origin_button_label), origin_ui);
@@ -1679,6 +1751,7 @@ gs_details_page_set_local_file (GsDetailsPage *self, GFile *file)
gs_details_page_set_state (self, GS_DETAILS_PAGE_STATE_LOADING);
g_clear_object (&self->app_local_file);
g_clear_object (&self->app);
+ self->origin_by_packaging_format = FALSE;
plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_FILE_TO_APP,
"file", file,
"refine-flags", GS_DETAILS_PAGE_REFINE_FLAGS,
@@ -1696,6 +1769,7 @@ gs_details_page_set_url (GsDetailsPage *self, const gchar *url)
gs_details_page_set_state (self, GS_DETAILS_PAGE_STATE_LOADING);
g_clear_object (&self->app_local_file);
g_clear_object (&self->app);
+ self->origin_by_packaging_format = FALSE;
plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_URL_TO_APP,
"search", url,
"refine-flags", GS_DETAILS_PAGE_REFINE_FLAGS |
@@ -1770,9 +1844,34 @@ origin_popover_row_activated_cb (GtkListBox *list_box,
}
}
+static void
+gs_details_page_read_packaging_format_preference (GsDetailsPage *self)
+{
+ g_auto(GStrv) preference = NULL;
+
+ if (self->packaging_format_preference == NULL)
+ return;
+
+ g_hash_table_remove_all (self->packaging_format_preference);
+
+ preference = g_settings_get_strv (self->settings, "packaging-format-preference");
+ if (preference == NULL || preference[0] == NULL)
+ return;
+
+ for (gsize ii = 0; preference[ii] != NULL; ii++) {
+ /* Using 'ii + 1' to easily distinguish between "not found" and "the first" index */
+ g_hash_table_insert (self->packaging_format_preference, g_strdup (preference[ii]),
GINT_TO_POINTER (ii + 1));
+ }
+}
+
static void
settings_changed_cb (GsDetailsPage *self, const gchar *key, gpointer data)
{
+ if (g_strcmp0 (key, "packaging-format-preference") == 0) {
+ gs_details_page_read_packaging_format_preference (self);
+ return;
+ }
+
if (self->app == NULL)
return;
if (g_strcmp0 (key, "show-nonfree-ui") == 0) {
@@ -1806,6 +1905,7 @@ gs_details_page_set_app (GsDetailsPage *self, GsApp *app)
/* save GsApp */
_set_app (self, app);
+ self->origin_by_packaging_format = TRUE;
gs_details_page_load_stage1 (self);
}
@@ -2148,6 +2248,30 @@ gs_details_page_setup (GsPage *page,
return TRUE;
}
+static guint
+gs_details_page_strcase_hash (gconstpointer key)
+{
+ const gchar *ptr;
+ guint hsh = 0, gg;
+
+ for (ptr = (const gchar *) key; *ptr != '\0'; ptr++) {
+ hsh = (hsh << 4) + g_ascii_toupper (*ptr);
+ if ((gg = hsh & 0xf0000000)) {
+ hsh = hsh ^ (gg >> 24);
+ hsh = hsh ^ gg;
+ }
+ }
+
+ return hsh;
+}
+
+static gboolean
+gs_details_page_strcase_equal (gconstpointer key1,
+ gconstpointer key2)
+{
+ return g_ascii_strcasecmp ((const gchar *) key1, (const gchar *) key2) == 0;
+}
+
static void
gs_details_page_get_property (GObject *object,
guint prop_id,
@@ -2217,6 +2341,10 @@ gs_details_page_dispose (GObject *object)
g_signal_handlers_disconnect_by_func (self->app, gs_details_page_progress_changed_cb, self);
g_clear_object (&self->app);
}
+ if (self->packaging_format_preference) {
+ g_hash_table_unref (self->packaging_format_preference);
+ self->packaging_format_preference = NULL;
+ }
g_clear_object (&self->app_local_file);
g_clear_object (&self->plugin_loader);
g_clear_object (&self->cancellable);
@@ -2348,6 +2476,7 @@ gs_details_page_init (GsDetailsPage *self)
/* setup networking */
self->session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, gs_user_agent (),
NULL);
+ self->packaging_format_preference = g_hash_table_new_full (gs_details_page_strcase_hash,
gs_details_page_strcase_equal, g_free, NULL);
self->settings = g_settings_new ("org.gnome.software");
g_signal_connect_swapped (self->settings, "changed",
G_CALLBACK (settings_changed_cb),
@@ -2368,6 +2497,8 @@ gs_details_page_init (GsDetailsPage *self)
G_CALLBACK (version_history_list_row_activated_cb), self);
gs_page_set_header_end_widget (GS_PAGE (self), self->origin_box);
+
+ gs_details_page_read_packaging_format_preference (self);
}
GsDetailsPage *
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]