[gnome-software] Split up gs-utils into two source files for sanity



commit 7c59ca10fda0fed12d0bb8fbd4e087424c449dcf
Author: Richard Hughes <richard hughsie com>
Date:   Tue May 10 08:43:23 2016 +0100

    Split up gs-utils into two source files for sanity
    
    gs-common.h is designed to be used by the frontend, gs-utils.h is designed to
    be used by plugins.

 po/POTFILES.in            |    2 +-
 src/Makefile.am           |    4 +
 src/gs-app-row.c          |    2 +-
 src/gs-app-tile.c         |    2 +-
 src/gs-app.c              |    1 +
 src/gs-common.c           |  750 +++++++++++++++++++++++++++++++++++++++++++++
 src/gs-common.h           |   71 +++++
 src/gs-feature-tile.c     |    2 +-
 src/gs-history-dialog.c   |    2 +-
 src/gs-page.c             |    2 +-
 src/gs-plugin-loader.c    |    2 +-
 src/gs-popular-tile.c     |    2 +-
 src/gs-screenshot-image.c |    3 +-
 src/gs-shell-category.c   |    2 +-
 src/gs-shell-details.c    |    4 +-
 src/gs-shell-extras.c     |    2 +-
 src/gs-shell-installed.c  |    2 +-
 src/gs-shell-moderate.c   |    2 +-
 src/gs-shell-overview.c   |    2 +-
 src/gs-shell-search.c     |    2 +-
 src/gs-shell-updates.c    |    2 +-
 src/gs-shell.c            |    2 +-
 src/gs-sources-dialog.c   |    2 +-
 src/gs-update-dialog.c    |    2 +-
 src/gs-upgrade-banner.c   |    2 +-
 src/gs-utils.c            |  719 -------------------------------------------
 src/gs-utils.h            |   47 +---
 27 files changed, 855 insertions(+), 782 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0f37728..8bd9c8b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -54,7 +54,7 @@ src/gs-update-list.c
 src/gs-update-monitor.c
 src/gs-upgrade-banner.c
 [type: gettext/glade]src/gs-upgrade-banner.ui
-src/gs-utils.c
+src/gs-common.c
 [type: gettext/glade]src/gs-menus.ui
 src/org.gnome.Software.desktop.in
 src/plugins/menu-spec-common.c
diff --git a/src/Makefile.am b/src/Makefile.am
index d7766d6..f2441ad 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -91,6 +91,7 @@ gnome_software_cmd_SOURCES =                          \
        gs-app-list.c                                   \
        gs-review.c                                     \
        gs-cmd.c                                        \
+       gs-common.c                                     \
        gs-debug.c                                      \
        gs-utils.c                                      \
        gs-os-release.c                                 \
@@ -122,6 +123,8 @@ gnome_software_SOURCES =                            \
        gs-app-list.h                                   \
        gs-category.c                                   \
        gs-category.h                                   \
+       gs-common.c                                     \
+       gs-common.h                                     \
        gs-debug.c                                      \
        gs-debug.h                                      \
        gs-app-addon-row.c                              \
@@ -300,6 +303,7 @@ gs_self_test_SOURCES =                                              \
        gs-app.c                                                \
        gs-app-list.c                                           \
        gs-category.c                                           \
+       gs-common.c                                             \
        gs-os-release.c                                         \
        gs-plugin-loader-sync.c                                 \
        gs-plugin-loader.c                                      \
diff --git a/src/gs-app-row.c b/src/gs-app-row.c
index 49a1fa4..28784e9 100644
--- a/src/gs-app-row.c
+++ b/src/gs-app-row.c
@@ -28,7 +28,7 @@
 #include "gs-app-row.h"
 #include "gs-star-widget.h"
 #include "gs-progress-button.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 #include "gs-folders.h"
 
 typedef struct
diff --git a/src/gs-app-tile.c b/src/gs-app-tile.c
index 5aefdfc..091e643 100644
--- a/src/gs-app-tile.c
+++ b/src/gs-app-tile.c
@@ -26,7 +26,7 @@
 
 #include "gs-app-tile.h"
 #include "gs-star-widget.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 struct _GsAppTile
 {
diff --git a/src/gs-app.c b/src/gs-app.c
index 99e7abf..2710c86 100644
--- a/src/gs-app.c
+++ b/src/gs-app.c
@@ -48,6 +48,7 @@
 #include <glib/gi18n.h>
 
 #include "gs-app.h"
+#include "gs-plugin.h"
 #include "gs-utils.h"
 
 struct _GsApp
diff --git a/src/gs-common.c b/src/gs-common.c
new file mode 100644
index 0000000..8253fba
--- /dev/null
+++ b/src/gs-common.c
@@ -0,0 +1,750 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013-2015 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gio/gdesktopappinfo.h>
+#include <errno.h>
+#include <fnmatch.h>
+
+#include "gs-app.h"
+#include "gs-common.h"
+#include "gs-plugin.h"
+
+#define SPINNER_DELAY 500
+
+static gboolean
+fade_in (gpointer data)
+{
+       GtkWidget *spinner = data;
+       gdouble opacity;
+
+       opacity = gtk_widget_get_opacity (spinner);
+       opacity = opacity + 0.1;
+       gtk_widget_set_opacity (spinner, opacity);
+
+       if (opacity >= 1.0) {
+               g_object_steal_data (G_OBJECT (spinner), "fade-timeout");
+               return G_SOURCE_REMOVE;
+       }
+       return G_SOURCE_CONTINUE;
+}
+
+static void
+remove_source (gpointer data)
+{
+       g_source_remove (GPOINTER_TO_UINT (data));
+}
+
+static gboolean
+start_spinning (gpointer data)
+{
+       GtkWidget *spinner = data;
+       guint id;
+
+       gtk_widget_set_opacity (spinner, 0);
+       gtk_spinner_start (GTK_SPINNER (spinner));
+       id = g_timeout_add (100, fade_in, spinner);
+       g_object_set_data_full (G_OBJECT (spinner), "fade-timeout",
+                               GUINT_TO_POINTER (id), remove_source);
+
+       /* don't try to remove this source in the future */
+       g_object_steal_data (G_OBJECT (spinner), "start-timeout");
+       return G_SOURCE_REMOVE;
+}
+
+void
+gs_stop_spinner (GtkSpinner *spinner)
+{
+       g_object_set_data (G_OBJECT (spinner), "start-timeout", NULL);
+       gtk_spinner_stop (spinner);
+}
+
+void
+gs_start_spinner (GtkSpinner *spinner)
+{
+       gboolean active;
+       guint id;
+
+       /* Don't do anything if it's already spinning */
+       g_object_get (spinner, "active", &active, NULL);
+       if (active || g_object_get_data (G_OBJECT (spinner), "start-timeout") != NULL)
+               return;
+
+       gtk_widget_set_opacity (GTK_WIDGET (spinner), 0);
+       id = g_timeout_add (SPINNER_DELAY, start_spinning, spinner);
+       g_object_set_data_full (G_OBJECT (spinner), "start-timeout",
+                               GUINT_TO_POINTER (id), remove_source);
+}
+
+static void
+remove_all_cb (GtkWidget *widget, gpointer user_data)
+{
+       GtkContainer *container = GTK_CONTAINER (user_data);
+       gtk_container_remove (container, widget);
+}
+
+void
+gs_container_remove_all (GtkContainer *container)
+{
+       gtk_container_foreach (container, remove_all_cb, container);
+}
+
+static void
+grab_focus (GtkWidget *widget)
+{
+       g_signal_handlers_disconnect_by_func (widget, grab_focus, NULL);
+       gtk_widget_grab_focus (widget);
+}
+
+void
+gs_grab_focus_when_mapped (GtkWidget *widget)
+{
+       if (gtk_widget_get_mapped (widget))
+               gtk_widget_grab_focus (widget);
+       else
+               g_signal_connect_after (widget, "map",
+                                       G_CALLBACK (grab_focus), NULL);
+}
+
+void
+gs_app_notify_installed (GsApp *app)
+{
+       g_autofree gchar *summary = NULL;
+       g_autoptr(GNotification) n = NULL;
+
+       /* TRANSLATORS: this is the summary of a notification that an application
+        * has been successfully installed */
+       summary = g_strdup_printf (_("%s is now installed"), gs_app_get_name (app));
+       n = g_notification_new (summary);
+       if (gs_app_get_kind (app) == AS_APP_KIND_DESKTOP) {
+               /* TRANSLATORS: this is button that opens the newly installed application */
+               g_notification_add_button_with_target (n, _("Launch"),
+                                                      "app.launch", "s",
+                                                      gs_app_get_id (app));
+       }
+       g_notification_set_default_action_and_target  (n, "app.details", "(ss)",
+                                                      gs_app_get_id (app), "");
+       g_application_send_notification (g_application_get_default (), "installed", n);
+}
+
+/**
+ * gs_app_notify_failed_modal:
+ **/
+void
+gs_app_notify_failed_modal (GsApp *app,
+                           GtkWindow *parent_window,
+                           GsPluginLoaderAction action,
+                           const GError *error)
+{
+       GtkWidget *dialog;
+       const gchar *title;
+       gboolean show_detailed_error;
+       g_autoptr(GString) msg = NULL;
+
+       /* TRANSLATORS: install or removed failed */
+       title = _("Sorry, this did not work");
+
+       /* say what we tried to do */
+       msg = g_string_new ("");
+       switch (action) {
+       case GS_PLUGIN_LOADER_ACTION_INSTALL:
+               /* TRANSLATORS: this is when the install fails */
+               g_string_append_printf (msg, _("Installation of %s failed."),
+                                       gs_app_get_name (app));
+               break;
+       case GS_PLUGIN_LOADER_ACTION_REMOVE:
+               /* TRANSLATORS: this is when the remove fails */
+               g_string_append_printf (msg, _("Removal of %s failed."),
+                                       gs_app_get_name (app));
+               break;
+       case GS_PLUGIN_LOADER_ACTION_UPGRADE_DOWNLOAD:
+               /* TRANSLATORS: this is when the upgrade download fails */
+               g_string_append_printf (msg, _("Upgrade to %s failed."),
+                                       gs_app_get_name (app));
+               break;
+       default:
+               g_assert_not_reached ();
+               break;
+       }
+       g_string_append (msg, " ");
+
+       /* give details about the error */
+       switch (error->code) {
+       case GS_PLUGIN_ERROR_NO_NETWORK:
+               /* TRANSLATORS: the package manager needed to download
+                * something with no network available */
+               g_string_append (msg, _("Internet access was required but wasn’t available."));
+               g_string_append (msg, " ");
+               /* TRANSLATORS: plug in the network cable... */
+               g_string_append (msg, _("Please make sure that you have internet access and try again."));
+               show_detailed_error = FALSE;
+               break;
+       case GS_PLUGIN_ERROR_NO_SPACE:
+               /* TRANSLATORS: we ran out of disk space */
+               g_string_append (msg, _("There wasn’t enough disk space."));
+               g_string_append (msg, " ");
+               /* TRANSLATORS: delete some stuff! */
+               g_string_append (msg, _("Please free up some space and try again."));
+               show_detailed_error = FALSE;
+               break;
+       default:
+               /* TRANSLATORS: we didn't handle the error type */
+               g_string_append (msg, _("If the problem persists, contact your software provider."));
+               show_detailed_error = TRUE;
+       }
+
+       dialog = gtk_message_dialog_new (parent_window,
+                                        GTK_DIALOG_MODAL |
+                                        GTK_DIALOG_USE_HEADER_BAR |
+                                        GTK_DIALOG_DESTROY_WITH_PARENT,
+                                        GTK_MESSAGE_ERROR,
+                                        GTK_BUTTONS_CLOSE,
+                                        "%s", title);
+       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                                 "%s", msg->str);
+
+       /* detailed error in an expander */
+       if (show_detailed_error) {
+               GtkWidget *vbox;
+               GtkWidget *expander;
+               GtkWidget *scrolled_window;
+               GtkWidget *label;
+
+               vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+               /* TRANSLATORS: this is an expander title */
+               expander = gtk_expander_new (_("Show Details"));
+               gtk_widget_set_margin_start (expander, 36);
+               gtk_widget_set_margin_end (expander, 36);
+               scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+               gtk_container_add (GTK_CONTAINER (expander), scrolled_window);
+               label = gtk_label_new (error->message);
+               gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+               gtk_container_add (GTK_CONTAINER (scrolled_window), label);
+               gtk_box_pack_end (GTK_BOX (vbox), expander, FALSE, TRUE, 4);
+               gtk_widget_show_all (expander);
+
+       }
+
+       g_signal_connect (dialog, "response",
+                         G_CALLBACK (gtk_widget_destroy), NULL);
+       gtk_window_present (GTK_WINDOW (dialog));
+}
+
+typedef enum {
+       GS_APP_LICENSE_FREE             = 0,
+       GS_APP_LICENSE_NONFREE          = 1,
+       GS_APP_LICENSE_PATENT_CONCERN   = 2
+} GsAppLicenseHint;
+
+/**
+ * gs_app_notify_unavailable:
+ **/
+GtkResponseType
+gs_app_notify_unavailable (GsApp *app, GtkWindow *parent)
+{
+       GsAppLicenseHint hint = GS_APP_LICENSE_FREE;
+       GtkResponseType response;
+       GtkWidget *dialog;
+       const gchar *license;
+       gboolean already_enabled = FALSE;       /* FIXME */
+       guint i;
+       struct {
+               const gchar     *str;
+               GsAppLicenseHint hint;
+       } keywords[] = {
+               { "NonFree",            GS_APP_LICENSE_NONFREE },
+               { "PatentConcern",      GS_APP_LICENSE_PATENT_CONCERN },
+               { "Proprietary",        GS_APP_LICENSE_NONFREE },
+               { NULL, 0 }
+       };
+       g_autofree gchar *origin_url = NULL;
+       g_autoptr(GSettings) settings = NULL;
+       g_autoptr(GString) body = NULL;
+       g_autoptr(GString) title = NULL;
+
+       /* this is very crude */
+       license = gs_app_get_license (app);
+       if (license != NULL) {
+               for (i = 0; keywords[i].str != NULL; i++) {
+                       if (g_strstr_len (license, -1, keywords[i].str) != NULL)
+                               hint |= keywords[i].hint;
+               }
+       } else {
+               /* use the worst-case assumption */
+               hint = GS_APP_LICENSE_NONFREE | GS_APP_LICENSE_PATENT_CONCERN;
+       }
+
+       /* check if the user has already dismissed */
+       settings = g_settings_new ("org.gnome.software");
+       if (!g_settings_get_boolean (settings, "prompt-for-nonfree"))
+               return GTK_RESPONSE_OK;
+
+       title = g_string_new ("");
+       if (already_enabled) {
+               g_string_append_printf (title, "<b>%s</b>",
+                                       /* TRANSLATORS: window title */
+                                       _("Install Third-Party Software?"));
+       } else {
+               g_string_append_printf (title, "<b>%s</b>",
+                                       /* TRANSLATORS: window title */
+                                       _("Enable Third-Party Software Source?"));
+       }
+       dialog = gtk_message_dialog_new (parent,
+                                        GTK_DIALOG_MODAL,
+                                        GTK_MESSAGE_QUESTION,
+                                        GTK_BUTTONS_CANCEL,
+                                        NULL);
+       gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), title->str);
+
+       /* FIXME: get the URL somehow... */
+       origin_url = g_strdup_printf ("<a href=\"\">%s</a>", gs_app_get_origin (app));
+       body = g_string_new ("");
+       if (hint & GS_APP_LICENSE_NONFREE) {
+               g_string_append_printf (body,
+                                       /* TRANSLATORS: the replacements are as follows:
+                                        * 1. Application name, e.g. "Firefox"
+                                        * 2. Software source name, e.g. fedora-optional
+                                        */
+                                       _("%s is not <a 
href=\"https://en.wikipedia.org/wiki/Free_and_open-source_software\";>"
+                                         "free and open source software</a>, "
+                                         "and is provided by “%s”."),
+                                       gs_app_get_name (app),
+                                       origin_url);
+       } else {
+               g_string_append_printf (body,
+                                       /* TRANSLATORS: the replacements are as follows:
+                                        * 1. Application name, e.g. "Firefox"
+                                        * 2. Software source name, e.g. fedora-optional */
+                                       _("%s is provided by “%s”."),
+                                       gs_app_get_name (app),
+                                       origin_url);
+       }
+
+       /* tell the use what needs to be done */
+       if (!already_enabled) {
+               g_string_append (body, " ");
+               g_string_append (body,
+                               /* TRANSLATORS: a software source is a repo */
+                               _("This software source must be "
+                                 "enabled to continue installation."));
+       }
+
+       /* be aware of patent clauses */
+       if (hint & GS_APP_LICENSE_PATENT_CONCERN) {
+               g_string_append (body, "\n\n");
+               if (gs_app_get_kind (app) != AS_APP_KIND_CODEC) {
+                       g_string_append_printf (body,
+                                               /* TRANSLATORS: Laws are geographical, urgh... */
+                                               _("It may be illegal to install "
+                                                 "or use %s in some countries."),
+                                               gs_app_get_name (app));
+               } else {
+                       g_string_append (body,
+                                       /* TRANSLATORS: Laws are geographical, urgh... */
+                                       _("It may be illegal to install or use "
+                                         "this codec in some countries."));
+               }
+       }
+
+       gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog), "%s", body->str);
+       /* TRANSLATORS: this is button text to not ask about non-free content again */
+       if (0) gtk_dialog_add_button (GTK_DIALOG (dialog), _("Don't Warn Again"), GTK_RESPONSE_YES);
+       if (already_enabled) {
+               gtk_dialog_add_button (GTK_DIALOG (dialog),
+                                      /* TRANSLATORS: button text */
+                                      _("Install"),
+                                      GTK_RESPONSE_OK);
+       } else {
+               gtk_dialog_add_button (GTK_DIALOG (dialog),
+                                      /* TRANSLATORS: button text */
+                                      _("Enable and Install"),
+                                      GTK_RESPONSE_OK);
+       }
+       response = gtk_dialog_run (GTK_DIALOG (dialog));
+       if (response == GTK_RESPONSE_YES) {
+               response = GTK_RESPONSE_OK;
+               g_settings_set_boolean (settings, "prompt-for-nonfree", FALSE);
+       }
+       gtk_widget_destroy (dialog);
+       return response;
+}
+
+void
+gs_app_show_url (GsApp *app, AsUrlKind kind)
+{
+       const gchar *url;
+       g_autoptr(GError) error = NULL;
+
+       url = gs_app_get_url (app, kind);
+       if (!gtk_show_uri (NULL, url, GDK_CURRENT_TIME, &error))
+               g_warning ("spawn of '%s' failed", url);
+}
+
+/**
+ * gs_image_set_from_pixbuf_with_scale:
+ **/
+void
+gs_image_set_from_pixbuf_with_scale (GtkImage *image, const GdkPixbuf *pixbuf, gint scale)
+{
+       cairo_surface_t *surface;
+       surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
+       gtk_image_set_from_surface (image, surface);
+       cairo_surface_destroy (surface);
+}
+
+/**
+ * gs_image_set_from_pixbuf:
+ **/
+void
+gs_image_set_from_pixbuf (GtkImage *image, const GdkPixbuf *pixbuf)
+{
+       gint scale;
+       scale = gdk_pixbuf_get_width (pixbuf) / 64;
+       gs_image_set_from_pixbuf_with_scale (image, pixbuf, scale);
+}
+
+#if AS_CHECK_VERSION(0,5,12)
+/**
+ * gs_utils_get_content_rating:
+ *
+ * Note: These are strings marked for translation for comment.
+ * This functionality is not currently used.
+ **/
+const gchar *
+gs_utils_get_content_rating (void)
+{
+       struct {
+               const gchar             *id;
+               AsContentRatingValue     value;
+               const gchar             *desc;
+       } content_rating_oars[] =  {
+       { "violence-cartoon",   AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating violence-cartoon", "None") },
+       { "violence-cartoon",   AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Cartoon characters in unsafe situations") },
+       { "violence-cartoon",   AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Cartoon characters in aggressive conflict") },
+       { "violence-cartoon",   AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Graphic violence involving cartoon characters") },
+       { "violence-fantasy",   AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating violence-fantasy", "None") },
+       { "violence-fantasy",   AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Characters in unsafe situations easily distinguishable from reality") },
+       { "violence-fantasy",   AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Characters in aggressive conflict easily distinguishable from reality") },
+       { "violence-fantasy",   AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Graphic violence easily distinguishable from reality") },
+       { "violence-realistic", AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating violence-realistic", "None") },
+       { "violence-realistic", AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Mild realistic characters in unsafe situations") },
+       { "violence-realistic", AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Depictions of realistic characters in aggressive conflict") },
+       { "violence-realistic", AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Graphic violence involving realistic characters") },
+       { "violence-bloodshed", AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating violence-bloodshed", "None") },
+       { "violence-bloodshed", AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Unrealistic bloodshed") },
+       { "violence-bloodshed", AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Realistic bloodshed") },
+       { "violence-bloodshed", AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Depictions of bloodshed and the mutilation of body parts") },
+       { "violence-sexual",    AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating violence-sexual", "None") },
+       { "violence-sexual",    AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Rape or other violent sexual behavior") },
+       { "drugs-alcohol",      AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating drugs-alcohol", "None") },
+       { "drugs-alcohol",      AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("References to alcoholic beverages") },
+       { "drugs-alcohol",      AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Use of alcoholic beverages") },
+       { "drugs-narcotics",    AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating drugs-narcotics", "None") },
+       { "drugs-narcotics",    AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("References to illicit drugs") },
+       { "drugs-narcotics",    AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Use of illicit drugs") },
+       { "drugs-tobacco",      AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("References to tobacco products") },
+       { "drugs-tobacco",      AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Use of tobacco products") },
+       { "sex-nudity",         AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating sex-nudity", "None") },
+       { "sex-nudity",         AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Brief artistic nudity") },
+       { "sex-nudity",         AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Prolonged nudity") },
+       { "sex-themes",         AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating sex-themes", "None") },
+       { "sex-themes",         AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Provocative references or depictions") },
+       { "sex-themes",         AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Sexual references or depictions") },
+       { "sex-themes",         AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Graphic sexual behavior") },
+       { "language-profanity", AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating language-profanity", "None") },
+       { "language-profanity", AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Mild or infrequent use of profanity") },
+       { "language-profanity", AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Moderate use of profanity") },
+       { "language-profanity", AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Strong or frequent use of profanity") },
+       { "language-humor",     AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating language-humor", "None") },
+       { "language-humor",     AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Slapstick humor") },
+       { "language-humor",     AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Vulgar or bathroom humor") },
+       { "language-humor",     AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Mature or sexual humor") },
+       { "language-discrimination", AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating language-discrimination", "None") },
+       { "language-discrimination", AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Negativity towards a specific group of people") },
+       { "language-discrimination", AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Discrimation designed to cause emotional harm") },
+       { "language-discrimination", AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Explicit discrimination based on gender, sexuality, race or religion") },
+       { "money-advertising", AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating money-advertising", "None") },
+       { "money-advertising", AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Product placement") },
+       { "money-advertising", AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Explicit references to specific brands or trademarked products") },
+       { "money-advertising", AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Players are encouraged to purchase specific real-world items") },
+       { "money-gambling",     AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating money-gambling", "None") },
+       { "money-gambling",     AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Gambling on random events using tokens or credits") },
+       { "money-gambling",     AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Gambling using \"play\" money") },
+       { "money-gambling",     AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Gambling using real money") },
+       { "money-purchasing",   AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating money-purchasing", "None") },
+       { "money-purchasing",   AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Ability to spend real money in-game") },
+       { "social-chat",        AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating social-chat", "None") },
+       { "social-chat",        AS_CONTENT_RATING_VALUE_MILD,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Player-to-player game interactions without chat functionality") },
+       { "social-chat",        AS_CONTENT_RATING_VALUE_MODERATE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Player-to-player preset interactions without chat functionality") },
+       { "social-chat",        AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Uncontrolled chat functionality between players") },
+       { "social-audio",       AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating social-audio", "None") },
+       { "social-audio",       AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Uncontrolled audio or video chat functionality between players") },
+       { "social-contacts",    AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating social-contacts", "None") },
+       { "social-contacts",    AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Sharing social network usernames or email addresses") },
+       { "social-info",        AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating social-info", "None") },
+       { "social-info",        AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Sharing user information with 3rd parties") },
+       { "social-location",    AS_CONTENT_RATING_VALUE_NONE,
+       /* TRANSLATORS: content rating description */
+       C_("content rating social-location", "None") },
+       { "social-location",    AS_CONTENT_RATING_VALUE_INTENSE,
+       /* TRANSLATORS: content rating description: comments welcome */
+       _("Sharing physical location to other users") },
+       { NULL, 0, NULL } };
+       return content_rating_oars[0].desc;
+}
+#endif
+
+/**
+ * gs_utils_is_current_desktop:
+ */
+gboolean
+gs_utils_is_current_desktop (const gchar *name)
+{
+       const gchar *tmp;
+       g_auto(GStrv) names = NULL;
+       tmp = g_getenv ("XDG_CURRENT_DESKTOP");
+       if (tmp == NULL)
+               return FALSE;
+       names = g_strsplit (tmp, ":", -1);
+       return g_strv_contains ((const gchar * const *) names, name);
+}
+
+/**
+ * gs_utils_widget_css_parsing_error_cb:
+ */
+static void
+gs_utils_widget_css_parsing_error_cb (GtkCssProvider *provider,
+                                     GtkCssSection *section,
+                                     GError *error,
+                                     gpointer user_data)
+{
+       g_warning ("CSS parse error %i:%i: %s",
+                  gtk_css_section_get_start_line (section),
+                  gtk_css_section_get_start_position (section),
+                  error->message);
+}
+
+/**
+ * gs_utils_widget_set_custom_css:
+ **/
+void
+gs_utils_widget_set_custom_css (GsApp *app, GtkWidget *widget, const gchar *metadata_css)
+{
+       GPtrArray *key_colors;
+       GString *str = g_string_sized_new (1024);
+       GString *css_str = g_string_new ("");
+       GtkStyleContext *context;
+       const gchar *css;
+       guint i;
+       g_autofree gchar *class_name = NULL;
+       g_autoptr(GtkCssProvider) provider = NULL;
+
+       g_return_if_fail (GS_IS_APP (app));
+
+       /* invalid */
+       css = gs_app_get_metadata_item (app, metadata_css);
+       if (css == NULL)
+               return;
+
+       /* replace any key colors */
+       css_str = g_string_new (css);
+       key_colors = gs_app_get_key_colors (app);
+       for (i = 0; i < key_colors->len; i++) {
+               GdkRGBA *color = g_ptr_array_index (key_colors, 1);
+               g_autofree gchar *key = NULL;
+               g_autofree gchar *value = NULL;
+               key = g_strdup_printf ("@keycolor-%02i@", i);
+               value = g_strdup_printf ("rgb(%.0f,%.0f,%.0f)",
+                                        color->red,
+                                        color->green,
+                                        color->blue);
+               as_utils_string_replace (css_str, key, value);
+       }
+
+       /* make into a proper CSS class */
+       class_name = g_strdup_printf ("themed-widget_%p", widget);
+       g_string_append_printf (str, ".%s {\n", class_name);
+       g_string_append_printf (str, "%s\n", css_str->str);
+       g_string_append (str, "}");
+
+       g_string_append_printf (str, ".%s:hover {\n", class_name);
+       g_string_append (str, "  opacity: 0.9;\n");
+       g_string_append (str, "}\n");
+
+       g_debug ("using custom CSS %s", str->str);
+
+       /* set the custom CSS class */
+       context = gtk_widget_get_style_context (widget);
+       gtk_style_context_add_class (context, class_name);
+
+       /* set up custom provider and store on the widget */
+       provider = gtk_css_provider_new ();
+       g_signal_connect (provider, "parsing-error",
+                         G_CALLBACK (gs_utils_widget_css_parsing_error_cb), NULL);
+       gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
+                                                  GTK_STYLE_PROVIDER (provider),
+                                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+       gtk_css_provider_load_from_data (provider, str->str, -1, NULL);
+       g_object_set_data_full (G_OBJECT (widget),
+                               "GnomeSoftware::provider",
+                               g_object_ref (provider),
+                               g_object_unref);
+}
+
+const gchar *
+gs_user_agent (void)
+{
+       return PACKAGE_NAME "/" PACKAGE_VERSION;
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-common.h b/src/gs-common.h
new file mode 100644
index 0000000..6e7ea92
--- /dev/null
+++ b/src/gs-common.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GS_COMMON_H
+#define __GS_COMMON_H
+
+#include <gio/gdesktopappinfo.h>
+#include <gtk/gtk.h>
+
+#include "gs-app.h"
+#include "gs-plugin-loader.h"
+
+G_BEGIN_DECLS
+
+void    gs_start_spinner               (GtkSpinner     *spinner);
+void    gs_stop_spinner                (GtkSpinner     *spinner);
+void    gs_container_remove_all        (GtkContainer   *container);
+void    gs_grab_focus_when_mapped      (GtkWidget      *widget);
+
+void    gs_app_notify_installed        (GsApp          *app);
+void    gs_app_notify_failed_modal     (GsApp          *app,
+                                        GtkWindow      *parent_window,
+                                        GsPluginLoaderAction action,
+                                        const GError   *error);
+GtkResponseType
+       gs_app_notify_unavailable       (GsApp          *app,
+                                        GtkWindow      *parent);
+void    gs_app_show_url                (GsApp          *app,
+                                        AsUrlKind       kind);
+
+void   gs_image_set_from_pixbuf_with_scale     (GtkImage               *image,
+                                                const GdkPixbuf        *pixbuf,
+                                                gint                    scale);
+void   gs_image_set_from_pixbuf                (GtkImage               *image,
+                                                const GdkPixbuf        *pixbuf);
+
+#if AS_CHECK_VERSION(0,5,12)
+const gchar    *gs_utils_get_content_rating    (void);
+#endif
+
+const gchar    *gs_user_agent                  (void);
+gboolean        gs_utils_is_current_desktop    (const gchar    *name);
+void            gs_utils_widget_set_custom_css (GsApp          *app,
+                                                GtkWidget      *widget,
+                                                const gchar    *metadata_css);
+gboolean        gs_utils_strv_fnmatch          (gchar          **strv,
+                                                const gchar    *str);
+
+G_END_DECLS
+
+#endif /* __GS_COMMON_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-feature-tile.c b/src/gs-feature-tile.c
index 77a122c..cba49e4 100644
--- a/src/gs-feature-tile.c
+++ b/src/gs-feature-tile.c
@@ -25,7 +25,7 @@
 #include <gtk/gtk.h>
 
 #include "gs-feature-tile.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 struct _GsFeatureTile
 {
diff --git a/src/gs-history-dialog.c b/src/gs-history-dialog.c
index 194c1e6..287dbd1 100644
--- a/src/gs-history-dialog.c
+++ b/src/gs-history-dialog.c
@@ -26,7 +26,7 @@
 #include <gtk/gtk.h>
 
 #include "gs-history-dialog.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 struct _GsHistoryDialog
 {
diff --git a/src/gs-page.c b/src/gs-page.c
index 5e8d2dc..49e4e30 100644
--- a/src/gs-page.c
+++ b/src/gs-page.c
@@ -27,7 +27,7 @@
 #include "gs-app.h"
 #include "gs-page.h"
 #include "gs-shell.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 typedef struct
 {
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 7b8f2c2..d96ba25 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -28,7 +28,7 @@
 #include "gs-plugin-loader.h"
 #include "gs-plugin.h"
 #include "gs-plugin-private.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 #define GS_PLUGIN_LOADER_UPDATES_CHANGED_DELAY 3       /* s */
 
diff --git a/src/gs-popular-tile.c b/src/gs-popular-tile.c
index 2fcdb24..cec0bc9 100644
--- a/src/gs-popular-tile.c
+++ b/src/gs-popular-tile.c
@@ -26,7 +26,7 @@
 
 #include "gs-popular-tile.h"
 #include "gs-star-widget.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 struct _GsPopularTile
 {
diff --git a/src/gs-screenshot-image.c b/src/gs-screenshot-image.c
index 5966645..8d17636 100644
--- a/src/gs-screenshot-image.c
+++ b/src/gs-screenshot-image.c
@@ -24,13 +24,14 @@
 
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
+#include <gnome-software.h>
 
 #define GNOME_DESKTOP_USE_UNSTABLE_API
 #include <libgnome-desktop/gnome-bg.h>
 #include <libgnome-desktop/gnome-desktop-thumbnail.h>
 
 #include "gs-screenshot-image.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 struct _GsScreenshotImage
 {
diff --git a/src/gs-shell-category.c b/src/gs-shell-category.c
index bc3e445..b6ca31e 100644
--- a/src/gs-shell-category.c
+++ b/src/gs-shell-category.c
@@ -25,7 +25,7 @@
 #include <string.h>
 #include <glib/gi18n.h>
 
-#include "gs-utils.h"
+#include "gs-common.h"
 #include "gs-app-tile.h"
 #include "gs-shell-category.h"
 
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 945982f..4c9cc82 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -24,9 +24,9 @@
 
 #include <string.h>
 #include <glib/gi18n.h>
-#include <appstream-glib.h>
+#include <gnome-software.h>
 
-#include "gs-utils.h"
+#include "gs-common.h"
 
 #include "gs-shell-details.h"
 #include "gs-app-addon-row.h"
diff --git a/src/gs-shell-extras.c b/src/gs-shell-extras.c
index 9c36dce..0c8b576 100644
--- a/src/gs-shell-extras.c
+++ b/src/gs-shell-extras.c
@@ -27,7 +27,7 @@
 #include "gs-app-row.h"
 #include "gs-language.h"
 #include "gs-shell.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 #include "gs-vendor.h"
 
 #include <glib/gi18n.h>
diff --git a/src/gs-shell-installed.c b/src/gs-shell-installed.c
index 39a2e81..8016370 100644
--- a/src/gs-shell-installed.c
+++ b/src/gs-shell-installed.c
@@ -29,7 +29,7 @@
 #include "gs-shell.h"
 #include "gs-shell-installed.h"
 #include "gs-app.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 #include "gs-app-row.h"
 #include "gs-app-folder-dialog.h"
 #include "gs-folders.h"
diff --git a/src/gs-shell-moderate.c b/src/gs-shell-moderate.c
index 414b704..227f23f 100644
--- a/src/gs-shell-moderate.c
+++ b/src/gs-shell-moderate.c
@@ -30,7 +30,7 @@
 #include "gs-review-row.h"
 #include "gs-shell.h"
 #include "gs-shell-moderate.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 struct _GsShellModerate
 {
diff --git a/src/gs-shell-overview.c b/src/gs-shell-overview.c
index bd35014..2f91112 100644
--- a/src/gs-shell-overview.c
+++ b/src/gs-shell-overview.c
@@ -30,7 +30,7 @@
 #include "gs-popular-tile.h"
 #include "gs-feature-tile.h"
 #include "gs-category-tile.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 #define N_TILES 8
 
diff --git a/src/gs-shell-search.c b/src/gs-shell-search.c
index a3ef887..0d2d3cb 100644
--- a/src/gs-shell-search.c
+++ b/src/gs-shell-search.c
@@ -27,7 +27,7 @@
 #include "gs-shell-search.h"
 #include "gs-shell.h"
 #include "gs-app.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 #include "gs-app-row.h"
 
 struct _GsShellSearch
diff --git a/src/gs-shell-updates.c b/src/gs-shell-updates.c
index 80cc6cc..d1a8c09 100644
--- a/src/gs-shell-updates.c
+++ b/src/gs-shell-updates.c
@@ -26,7 +26,7 @@
 
 #include "gs-shell.h"
 #include "gs-shell-updates.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 #include "gs-app.h"
 #include "gs-app-row.h"
 #include "gs-update-dialog.h"
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 474401b..09b1b07 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -25,7 +25,7 @@
 #include <string.h>
 #include <glib/gi18n.h>
 
-#include "gs-utils.h"
+#include "gs-common.h"
 #include "gs-shell.h"
 #include "gs-shell-details.h"
 #include "gs-shell-installed.h"
diff --git a/src/gs-sources-dialog.c b/src/gs-sources-dialog.c
index b78dd81..adc71c4 100644
--- a/src/gs-sources-dialog.c
+++ b/src/gs-sources-dialog.c
@@ -28,7 +28,7 @@
 #include "gs-sources-dialog.h"
 #include "gs-os-release.h"
 #include "gs-sources-dialog-row.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 struct _GsSourcesDialog
 {
diff --git a/src/gs-update-dialog.c b/src/gs-update-dialog.c
index 45bae7d..3f75cb9 100644
--- a/src/gs-update-dialog.c
+++ b/src/gs-update-dialog.c
@@ -27,7 +27,7 @@
 #include "gs-update-dialog.h"
 #include "gs-app-row.h"
 #include "gs-update-list.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 typedef struct {
        gchar           *title;
diff --git a/src/gs-upgrade-banner.c b/src/gs-upgrade-banner.c
index 5772352..699c6a9 100644
--- a/src/gs-upgrade-banner.c
+++ b/src/gs-upgrade-banner.c
@@ -27,7 +27,7 @@
 #include <stdlib.h>
 
 #include "gs-upgrade-banner.h"
-#include "gs-utils.h"
+#include "gs-common.h"
 
 typedef struct
 {
diff --git a/src/gs-utils.c b/src/gs-utils.c
index 977f183..3b4200f 100644
--- a/src/gs-utils.c
+++ b/src/gs-utils.c
@@ -21,8 +21,6 @@
 
 #include "config.h"
 
-#include <glib/gi18n.h>
-#include <gio/gdesktopappinfo.h>
 #include <errno.h>
 #include <fnmatch.h>
 
@@ -34,376 +32,6 @@
 #include "gs-utils.h"
 #include "gs-plugin.h"
 
-#define SPINNER_DELAY 500
-
-static gboolean
-fade_in (gpointer data)
-{
-       GtkWidget *spinner = data;
-       gdouble opacity;
-
-       opacity = gtk_widget_get_opacity (spinner);
-       opacity = opacity + 0.1;
-       gtk_widget_set_opacity (spinner, opacity);
-
-       if (opacity >= 1.0) {
-               g_object_steal_data (G_OBJECT (spinner), "fade-timeout");
-               return G_SOURCE_REMOVE;
-       }
-       return G_SOURCE_CONTINUE;
-}
-
-static void
-remove_source (gpointer data)
-{
-       g_source_remove (GPOINTER_TO_UINT (data));
-}
-
-static gboolean
-start_spinning (gpointer data)
-{
-       GtkWidget *spinner = data;
-       guint id;
-
-       gtk_widget_set_opacity (spinner, 0);
-       gtk_spinner_start (GTK_SPINNER (spinner));
-       id = g_timeout_add (100, fade_in, spinner);
-       g_object_set_data_full (G_OBJECT (spinner), "fade-timeout",
-                               GUINT_TO_POINTER (id), remove_source);
-
-       /* don't try to remove this source in the future */
-       g_object_steal_data (G_OBJECT (spinner), "start-timeout");
-       return G_SOURCE_REMOVE;
-}
-
-void
-gs_stop_spinner (GtkSpinner *spinner)
-{
-       g_object_set_data (G_OBJECT (spinner), "start-timeout", NULL);
-       gtk_spinner_stop (spinner);
-}
-
-void
-gs_start_spinner (GtkSpinner *spinner)
-{
-       gboolean active;
-       guint id;
-
-       /* Don't do anything if it's already spinning */
-       g_object_get (spinner, "active", &active, NULL);
-       if (active || g_object_get_data (G_OBJECT (spinner), "start-timeout") != NULL)
-               return;
-
-       gtk_widget_set_opacity (GTK_WIDGET (spinner), 0);
-       id = g_timeout_add (SPINNER_DELAY, start_spinning, spinner);
-       g_object_set_data_full (G_OBJECT (spinner), "start-timeout",
-                               GUINT_TO_POINTER (id), remove_source);
-}
-
-static void
-remove_all_cb (GtkWidget *widget, gpointer user_data)
-{
-       GtkContainer *container = GTK_CONTAINER (user_data);
-       gtk_container_remove (container, widget);
-}
-
-void
-gs_container_remove_all (GtkContainer *container)
-{
-       gtk_container_foreach (container, remove_all_cb, container);
-}
-
-static void
-grab_focus (GtkWidget *widget)
-{
-       g_signal_handlers_disconnect_by_func (widget, grab_focus, NULL);
-       gtk_widget_grab_focus (widget);
-}
-
-void
-gs_grab_focus_when_mapped (GtkWidget *widget)
-{
-       if (gtk_widget_get_mapped (widget))
-               gtk_widget_grab_focus (widget);
-       else
-               g_signal_connect_after (widget, "map",
-                                       G_CALLBACK (grab_focus), NULL);
-}
-
-void
-gs_app_notify_installed (GsApp *app)
-{
-       g_autofree gchar *summary = NULL;
-       g_autoptr(GNotification) n = NULL;
-
-       /* TRANSLATORS: this is the summary of a notification that an application
-        * has been successfully installed */
-       summary = g_strdup_printf (_("%s is now installed"), gs_app_get_name (app));
-       n = g_notification_new (summary);
-       if (gs_app_get_kind (app) == AS_APP_KIND_DESKTOP) {
-               /* TRANSLATORS: this is button that opens the newly installed application */
-               g_notification_add_button_with_target (n, _("Launch"),
-                                                      "app.launch", "s",
-                                                      gs_app_get_id (app));
-       }
-       g_notification_set_default_action_and_target  (n, "app.details", "(ss)",
-                                                      gs_app_get_id (app), "");
-       g_application_send_notification (g_application_get_default (), "installed", n);
-}
-
-/**
- * gs_app_notify_failed_modal:
- **/
-void
-gs_app_notify_failed_modal (GsApp *app,
-                           GtkWindow *parent_window,
-                           GsPluginLoaderAction action,
-                           const GError *error)
-{
-       GtkWidget *dialog;
-       const gchar *title;
-       gboolean show_detailed_error;
-       g_autoptr(GString) msg = NULL;
-
-       /* TRANSLATORS: install or removed failed */
-       title = _("Sorry, this did not work");
-
-       /* say what we tried to do */
-       msg = g_string_new ("");
-       switch (action) {
-       case GS_PLUGIN_LOADER_ACTION_INSTALL:
-               /* TRANSLATORS: this is when the install fails */
-               g_string_append_printf (msg, _("Installation of %s failed."),
-                                       gs_app_get_name (app));
-               break;
-       case GS_PLUGIN_LOADER_ACTION_REMOVE:
-               /* TRANSLATORS: this is when the remove fails */
-               g_string_append_printf (msg, _("Removal of %s failed."),
-                                       gs_app_get_name (app));
-               break;
-       case GS_PLUGIN_LOADER_ACTION_UPGRADE_DOWNLOAD:
-               /* TRANSLATORS: this is when the upgrade download fails */
-               g_string_append_printf (msg, _("Upgrade to %s failed."),
-                                       gs_app_get_name (app));
-               break;
-       default:
-               g_assert_not_reached ();
-               break;
-       }
-       g_string_append (msg, " ");
-
-       /* give details about the error */
-       switch (error->code) {
-       case GS_PLUGIN_ERROR_NO_NETWORK:
-               /* TRANSLATORS: the package manager needed to download
-                * something with no network available */
-               g_string_append (msg, _("Internet access was required but wasn’t available."));
-               g_string_append (msg, " ");
-               /* TRANSLATORS: plug in the network cable... */
-               g_string_append (msg, _("Please make sure that you have internet access and try again."));
-               show_detailed_error = FALSE;
-               break;
-       case GS_PLUGIN_ERROR_NO_SPACE:
-               /* TRANSLATORS: we ran out of disk space */
-               g_string_append (msg, _("There wasn’t enough disk space."));
-               g_string_append (msg, " ");
-               /* TRANSLATORS: delete some stuff! */
-               g_string_append (msg, _("Please free up some space and try again."));
-               show_detailed_error = FALSE;
-               break;
-       default:
-               /* TRANSLATORS: we didn't handle the error type */
-               g_string_append (msg, _("If the problem persists, contact your software provider."));
-               show_detailed_error = TRUE;
-       }
-
-       dialog = gtk_message_dialog_new (parent_window,
-                                        GTK_DIALOG_MODAL |
-                                        GTK_DIALOG_USE_HEADER_BAR |
-                                        GTK_DIALOG_DESTROY_WITH_PARENT,
-                                        GTK_MESSAGE_ERROR,
-                                        GTK_BUTTONS_CLOSE,
-                                        "%s", title);
-       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                                                 "%s", msg->str);
-
-       /* detailed error in an expander */
-       if (show_detailed_error) {
-               GtkWidget *vbox;
-               GtkWidget *expander;
-               GtkWidget *scrolled_window;
-               GtkWidget *label;
-
-               vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
-               /* TRANSLATORS: this is an expander title */
-               expander = gtk_expander_new (_("Show Details"));
-               gtk_widget_set_margin_start (expander, 36);
-               gtk_widget_set_margin_end (expander, 36);
-               scrolled_window = gtk_scrolled_window_new (NULL, NULL);
-               gtk_container_add (GTK_CONTAINER (expander), scrolled_window);
-               label = gtk_label_new (error->message);
-               gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-               gtk_container_add (GTK_CONTAINER (scrolled_window), label);
-               gtk_box_pack_end (GTK_BOX (vbox), expander, FALSE, TRUE, 4);
-               gtk_widget_show_all (expander);
-
-       }
-
-       g_signal_connect (dialog, "response",
-                         G_CALLBACK (gtk_widget_destroy), NULL);
-       gtk_window_present (GTK_WINDOW (dialog));
-}
-
-typedef enum {
-       GS_APP_LICENSE_FREE             = 0,
-       GS_APP_LICENSE_NONFREE          = 1,
-       GS_APP_LICENSE_PATENT_CONCERN   = 2
-} GsAppLicenseHint;
-
-/**
- * gs_app_notify_unavailable:
- **/
-GtkResponseType
-gs_app_notify_unavailable (GsApp *app, GtkWindow *parent)
-{
-       GsAppLicenseHint hint = GS_APP_LICENSE_FREE;
-       GtkResponseType response;
-       GtkWidget *dialog;
-       const gchar *license;
-       gboolean already_enabled = FALSE;       /* FIXME */
-       guint i;
-       struct {
-               const gchar     *str;
-               GsAppLicenseHint hint;
-       } keywords[] = {
-               { "NonFree",            GS_APP_LICENSE_NONFREE },
-               { "PatentConcern",      GS_APP_LICENSE_PATENT_CONCERN },
-               { "Proprietary",        GS_APP_LICENSE_NONFREE },
-               { NULL, 0 }
-       };
-       g_autofree gchar *origin_url = NULL;
-       g_autoptr(GSettings) settings = NULL;
-       g_autoptr(GString) body = NULL;
-       g_autoptr(GString) title = NULL;
-
-       /* this is very crude */
-       license = gs_app_get_license (app);
-       if (license != NULL) {
-               for (i = 0; keywords[i].str != NULL; i++) {
-                       if (g_strstr_len (license, -1, keywords[i].str) != NULL)
-                               hint |= keywords[i].hint;
-               }
-       } else {
-               /* use the worst-case assumption */
-               hint = GS_APP_LICENSE_NONFREE | GS_APP_LICENSE_PATENT_CONCERN;
-       }
-
-       /* check if the user has already dismissed */
-       settings = g_settings_new ("org.gnome.software");
-       if (!g_settings_get_boolean (settings, "prompt-for-nonfree"))
-               return GTK_RESPONSE_OK;
-
-       title = g_string_new ("");
-       if (already_enabled) {
-               g_string_append_printf (title, "<b>%s</b>",
-                                       /* TRANSLATORS: window title */
-                                       _("Install Third-Party Software?"));
-       } else {
-               g_string_append_printf (title, "<b>%s</b>",
-                                       /* TRANSLATORS: window title */
-                                       _("Enable Third-Party Software Source?"));
-       }
-       dialog = gtk_message_dialog_new (parent,
-                                        GTK_DIALOG_MODAL,
-                                        GTK_MESSAGE_QUESTION,
-                                        GTK_BUTTONS_CANCEL,
-                                        NULL);
-       gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), title->str);
-
-       /* FIXME: get the URL somehow... */
-       origin_url = g_strdup_printf ("<a href=\"\">%s</a>", gs_app_get_origin (app));
-       body = g_string_new ("");
-       if (hint & GS_APP_LICENSE_NONFREE) {
-               g_string_append_printf (body,
-                                       /* TRANSLATORS: the replacements are as follows:
-                                        * 1. Application name, e.g. "Firefox"
-                                        * 2. Software source name, e.g. fedora-optional
-                                        */
-                                       _("%s is not <a 
href=\"https://en.wikipedia.org/wiki/Free_and_open-source_software\";>"
-                                         "free and open source software</a>, "
-                                         "and is provided by “%s”."),
-                                       gs_app_get_name (app),
-                                       origin_url);
-       } else {
-               g_string_append_printf (body,
-                                       /* TRANSLATORS: the replacements are as follows:
-                                        * 1. Application name, e.g. "Firefox"
-                                        * 2. Software source name, e.g. fedora-optional */
-                                       _("%s is provided by “%s”."),
-                                       gs_app_get_name (app),
-                                       origin_url);
-       }
-
-       /* tell the use what needs to be done */
-       if (!already_enabled) {
-               g_string_append (body, " ");
-               g_string_append (body,
-                               /* TRANSLATORS: a software source is a repo */
-                               _("This software source must be "
-                                 "enabled to continue installation."));
-       }
-
-       /* be aware of patent clauses */
-       if (hint & GS_APP_LICENSE_PATENT_CONCERN) {
-               g_string_append (body, "\n\n");
-               if (gs_app_get_kind (app) != AS_APP_KIND_CODEC) {
-                       g_string_append_printf (body,
-                                               /* TRANSLATORS: Laws are geographical, urgh... */
-                                               _("It may be illegal to install "
-                                                 "or use %s in some countries."),
-                                               gs_app_get_name (app));
-               } else {
-                       g_string_append (body,
-                                       /* TRANSLATORS: Laws are geographical, urgh... */
-                                       _("It may be illegal to install or use "
-                                         "this codec in some countries."));
-               }
-       }
-
-       gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog), "%s", body->str);
-       /* TRANSLATORS: this is button text to not ask about non-free content again */
-       if (0) gtk_dialog_add_button (GTK_DIALOG (dialog), _("Don't Warn Again"), GTK_RESPONSE_YES);
-       if (already_enabled) {
-               gtk_dialog_add_button (GTK_DIALOG (dialog),
-                                      /* TRANSLATORS: button text */
-                                      _("Install"),
-                                      GTK_RESPONSE_OK);
-       } else {
-               gtk_dialog_add_button (GTK_DIALOG (dialog),
-                                      /* TRANSLATORS: button text */
-                                      _("Enable and Install"),
-                                      GTK_RESPONSE_OK);
-       }
-       response = gtk_dialog_run (GTK_DIALOG (dialog));
-       if (response == GTK_RESPONSE_YES) {
-               response = GTK_RESPONSE_OK;
-               g_settings_set_boolean (settings, "prompt-for-nonfree", FALSE);
-       }
-       gtk_widget_destroy (dialog);
-       return response;
-}
-
-void
-gs_app_show_url (GsApp *app, AsUrlKind kind)
-{
-       const gchar *url;
-       g_autoptr(GError) error = NULL;
-
-       url = gs_app_get_url (app, kind);
-       if (!gtk_show_uri (NULL, url, GDK_CURRENT_TIME, &error))
-               g_warning ("spawn of '%s' failed", url);
-}
-
 /**
  * gs_mkdir_parent:
  **/
@@ -425,29 +53,6 @@ gs_mkdir_parent (const gchar *path, GError **error)
 }
 
 /**
- * gs_image_set_from_pixbuf_with_scale:
- **/
-void
-gs_image_set_from_pixbuf_with_scale (GtkImage *image, const GdkPixbuf *pixbuf, gint scale)
-{
-       cairo_surface_t *surface;
-       surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
-       gtk_image_set_from_surface (image, surface);
-       cairo_surface_destroy (surface);
-}
-
-/**
- * gs_image_set_from_pixbuf:
- **/
-void
-gs_image_set_from_pixbuf (GtkImage *image, const GdkPixbuf *pixbuf)
-{
-       gint scale;
-       scale = gdk_pixbuf_get_width (pixbuf) / 64;
-       gs_image_set_from_pixbuf_with_scale (image, pixbuf, scale);
-}
-
-/**
  * gs_utils_get_file_age:
  *
  * Returns: The time in seconds since the file was modified
@@ -473,12 +78,6 @@ gs_utils_get_file_age (GFile *file)
        return now - mtime;
 }
 
-const gchar *
-gs_user_agent (void)
-{
-       return PACKAGE_NAME "/" PACKAGE_VERSION;
-}
-
 /**
  * gs_utils_get_cache_filename:
  *
@@ -588,227 +187,6 @@ gs_utils_get_permission (const gchar *id)
 #endif
 }
 
-#if AS_CHECK_VERSION(0,5,12)
-/**
- * gs_utils_get_content_rating:
- *
- * Note: These are strings marked for translation for comment.
- * This functionality is not currently used.
- **/
-const gchar *
-gs_utils_get_content_rating (void)
-{
-       struct {
-               const gchar             *id;
-               AsContentRatingValue     value;
-               const gchar             *desc;
-       } content_rating_oars[] =  {
-       { "violence-cartoon",   AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating violence-cartoon", "None") },
-       { "violence-cartoon",   AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Cartoon characters in unsafe situations") },
-       { "violence-cartoon",   AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Cartoon characters in aggressive conflict") },
-       { "violence-cartoon",   AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Graphic violence involving cartoon characters") },
-       { "violence-fantasy",   AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating violence-fantasy", "None") },
-       { "violence-fantasy",   AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Characters in unsafe situations easily distinguishable from reality") },
-       { "violence-fantasy",   AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Characters in aggressive conflict easily distinguishable from reality") },
-       { "violence-fantasy",   AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Graphic violence easily distinguishable from reality") },
-       { "violence-realistic", AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating violence-realistic", "None") },
-       { "violence-realistic", AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Mild realistic characters in unsafe situations") },
-       { "violence-realistic", AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Depictions of realistic characters in aggressive conflict") },
-       { "violence-realistic", AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Graphic violence involving realistic characters") },
-       { "violence-bloodshed", AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating violence-bloodshed", "None") },
-       { "violence-bloodshed", AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Unrealistic bloodshed") },
-       { "violence-bloodshed", AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Realistic bloodshed") },
-       { "violence-bloodshed", AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Depictions of bloodshed and the mutilation of body parts") },
-       { "violence-sexual",    AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating violence-sexual", "None") },
-       { "violence-sexual",    AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Rape or other violent sexual behavior") },
-       { "drugs-alcohol",      AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating drugs-alcohol", "None") },
-       { "drugs-alcohol",      AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("References to alcoholic beverages") },
-       { "drugs-alcohol",      AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Use of alcoholic beverages") },
-       { "drugs-narcotics",    AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating drugs-narcotics", "None") },
-       { "drugs-narcotics",    AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("References to illicit drugs") },
-       { "drugs-narcotics",    AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Use of illicit drugs") },
-       { "drugs-tobacco",      AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("References to tobacco products") },
-       { "drugs-tobacco",      AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Use of tobacco products") },
-       { "sex-nudity",         AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating sex-nudity", "None") },
-       { "sex-nudity",         AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Brief artistic nudity") },
-       { "sex-nudity",         AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Prolonged nudity") },
-       { "sex-themes",         AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating sex-themes", "None") },
-       { "sex-themes",         AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Provocative references or depictions") },
-       { "sex-themes",         AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Sexual references or depictions") },
-       { "sex-themes",         AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Graphic sexual behavior") },
-       { "language-profanity", AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating language-profanity", "None") },
-       { "language-profanity", AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Mild or infrequent use of profanity") },
-       { "language-profanity", AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Moderate use of profanity") },
-       { "language-profanity", AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Strong or frequent use of profanity") },
-       { "language-humor",     AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating language-humor", "None") },
-       { "language-humor",     AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Slapstick humor") },
-       { "language-humor",     AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Vulgar or bathroom humor") },
-       { "language-humor",     AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Mature or sexual humor") },
-       { "language-discrimination", AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating language-discrimination", "None") },
-       { "language-discrimination", AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Negativity towards a specific group of people") },
-       { "language-discrimination", AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Discrimation designed to cause emotional harm") },
-       { "language-discrimination", AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Explicit discrimination based on gender, sexuality, race or religion") },
-       { "money-advertising", AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating money-advertising", "None") },
-       { "money-advertising", AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Product placement") },
-       { "money-advertising", AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Explicit references to specific brands or trademarked products") },
-       { "money-advertising", AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Players are encouraged to purchase specific real-world items") },
-       { "money-gambling",     AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating money-gambling", "None") },
-       { "money-gambling",     AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Gambling on random events using tokens or credits") },
-       { "money-gambling",     AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Gambling using \"play\" money") },
-       { "money-gambling",     AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Gambling using real money") },
-       { "money-purchasing",   AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating money-purchasing", "None") },
-       { "money-purchasing",   AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Ability to spend real money in-game") },
-       { "social-chat",        AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating social-chat", "None") },
-       { "social-chat",        AS_CONTENT_RATING_VALUE_MILD,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Player-to-player game interactions without chat functionality") },
-       { "social-chat",        AS_CONTENT_RATING_VALUE_MODERATE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Player-to-player preset interactions without chat functionality") },
-       { "social-chat",        AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Uncontrolled chat functionality between players") },
-       { "social-audio",       AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating social-audio", "None") },
-       { "social-audio",       AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Uncontrolled audio or video chat functionality between players") },
-       { "social-contacts",    AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating social-contacts", "None") },
-       { "social-contacts",    AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Sharing social network usernames or email addresses") },
-       { "social-info",        AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating social-info", "None") },
-       { "social-info",        AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Sharing user information with 3rd parties") },
-       { "social-location",    AS_CONTENT_RATING_VALUE_NONE,
-       /* TRANSLATORS: content rating description */
-       C_("content rating social-location", "None") },
-       { "social-location",    AS_CONTENT_RATING_VALUE_INTENSE,
-       /* TRANSLATORS: content rating description: comments welcome */
-       _("Sharing physical location to other users") },
-       { NULL, 0, NULL } };
-       return content_rating_oars[0].desc;
-}
-#endif
-
 /**
  * gs_utils_get_content_type:
  */
@@ -835,103 +213,6 @@ gs_utils_get_content_type (GFile *file,
 }
 
 /**
- * gs_utils_is_current_desktop:
- */
-gboolean
-gs_utils_is_current_desktop (const gchar *name)
-{
-       const gchar *tmp;
-       g_auto(GStrv) names = NULL;
-       tmp = g_getenv ("XDG_CURRENT_DESKTOP");
-       if (tmp == NULL)
-               return FALSE;
-       names = g_strsplit (tmp, ":", -1);
-       return g_strv_contains ((const gchar * const *) names, name);
-}
-
-/**
- * gs_utils_widget_css_parsing_error_cb:
- */
-static void
-gs_utils_widget_css_parsing_error_cb (GtkCssProvider *provider,
-                                     GtkCssSection *section,
-                                     GError *error,
-                                     gpointer user_data)
-{
-       g_warning ("CSS parse error %i:%i: %s",
-                  gtk_css_section_get_start_line (section),
-                  gtk_css_section_get_start_position (section),
-                  error->message);
-}
-
-/**
- * gs_utils_widget_set_custom_css:
- **/
-void
-gs_utils_widget_set_custom_css (GsApp *app, GtkWidget *widget, const gchar *metadata_css)
-{
-       GPtrArray *key_colors;
-       GString *str = g_string_sized_new (1024);
-       GString *css_str = g_string_new ("");
-       GtkStyleContext *context;
-       const gchar *css;
-       guint i;
-       g_autofree gchar *class_name = NULL;
-       g_autoptr(GtkCssProvider) provider = NULL;
-
-       g_return_if_fail (GS_IS_APP (app));
-
-       /* invalid */
-       css = gs_app_get_metadata_item (app, metadata_css);
-       if (css == NULL)
-               return;
-
-       /* replace any key colors */
-       css_str = g_string_new (css);
-       key_colors = gs_app_get_key_colors (app);
-       for (i = 0; i < key_colors->len; i++) {
-               GdkRGBA *color = g_ptr_array_index (key_colors, 1);
-               g_autofree gchar *key = NULL;
-               g_autofree gchar *value = NULL;
-               key = g_strdup_printf ("@keycolor-%02i@", i);
-               value = g_strdup_printf ("rgb(%.0f,%.0f,%.0f)",
-                                        color->red,
-                                        color->green,
-                                        color->blue);
-               as_utils_string_replace (css_str, key, value);
-       }
-
-       /* make into a proper CSS class */
-       class_name = g_strdup_printf ("themed-widget_%p", widget);
-       g_string_append_printf (str, ".%s {\n", class_name);
-       g_string_append_printf (str, "%s\n", css_str->str);
-       g_string_append (str, "}");
-
-       g_string_append_printf (str, ".%s:hover {\n", class_name);
-       g_string_append (str, "  opacity: 0.9;\n");
-       g_string_append (str, "}\n");
-
-       g_debug ("using custom CSS %s", str->str);
-
-       /* set the custom CSS class */
-       context = gtk_widget_get_style_context (widget);
-       gtk_style_context_add_class (context, class_name);
-
-       /* set up custom provider and store on the widget */
-       provider = gtk_css_provider_new ();
-       g_signal_connect (provider, "parsing-error",
-                         G_CALLBACK (gs_utils_widget_css_parsing_error_cb), NULL);
-       gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
-                                                  GTK_STYLE_PROVIDER (provider),
-                                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-       gtk_css_provider_load_from_data (provider, str->str, -1, NULL);
-       g_object_set_data_full (G_OBJECT (widget),
-                               "GnomeSoftware::provider",
-                               g_object_ref (provider),
-                               g_object_unref);
-}
-
-/**
  * gs_utils_strv_fnmatch:
  */
 gboolean
diff --git a/src/gs-utils.h b/src/gs-utils.h
index 828a90b..9596006 100644
--- a/src/gs-utils.h
+++ b/src/gs-utils.h
@@ -26,7 +26,6 @@
 #include <gtk/gtk.h>
 
 #include "gs-app.h"
-#include "gs-plugin-loader.h"
 
 G_BEGIN_DECLS
 
@@ -36,54 +35,20 @@ typedef enum {
        GS_UTILS_CACHE_FLAG_LAST
 } GsUtilsCacheFlags;
 
-void    gs_start_spinner               (GtkSpinner     *spinner);
-void    gs_stop_spinner                (GtkSpinner     *spinner);
-void    gs_container_remove_all        (GtkContainer   *container);
-void    gs_grab_focus_when_mapped      (GtkWidget      *widget);
-
-guint   gs_utils_get_file_age          (GFile          *file);
-gchar  *gs_utils_get_content_type      (GFile          *file,
-                                        GCancellable   *cancellable,
-                                        GError         **error);
-
-void    gs_app_notify_installed        (GsApp          *app);
-void    gs_app_notify_failed_modal     (GsApp          *app,
-                                        GtkWindow      *parent_window,
-                                        GsPluginLoaderAction action,
-                                        const GError   *error);
-GtkResponseType
-       gs_app_notify_unavailable       (GsApp          *app,
-                                        GtkWindow      *parent);
-void    gs_app_show_url                (GsApp          *app,
-                                        AsUrlKind       kind);
-
-gboolean gs_mkdir_parent               (const gchar    *path,
-                                        GError         **error);
-
-void   gs_image_set_from_pixbuf_with_scale     (GtkImage               *image,
-                                                const GdkPixbuf        *pixbuf,
-                                                gint                    scale);
-void   gs_image_set_from_pixbuf                (GtkImage               *image,
-                                                const GdkPixbuf        *pixbuf);
-const gchar     *gs_user_agent                 (void);
-
+guint           gs_utils_get_file_age          (GFile          *file);
+gchar          *gs_utils_get_content_type      (GFile          *file,
+                                                GCancellable   *cancellable,
+                                                GError         **error);
+gboolean        gs_mkdir_parent                (const gchar    *path,
+                                                GError         **error);
 gchar          *gs_utils_get_cache_filename    (const gchar    *kind,
                                                 const gchar    *basename,
                                                 GsUtilsCacheFlags flags,
                                                 GError         **error);
 gchar          *gs_utils_get_user_hash         (GError         **error);
 GPermission    *gs_utils_get_permission        (const gchar    *id);
-#if AS_CHECK_VERSION(0,5,12)
-const gchar    *gs_utils_get_content_rating    (void);
-#endif
-
-gboolean        gs_utils_is_current_desktop    (const gchar    *name);
-void            gs_utils_widget_set_custom_css (GsApp          *app,
-                                                GtkWidget      *widget,
-                                                const gchar    *metadata_css);
 gboolean        gs_utils_strv_fnmatch          (gchar          **strv,
                                                 const gchar    *str);
-
 GDesktopAppInfo *gs_utils_get_desktop_app_info (const gchar    *id);
 
 G_END_DECLS


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