[gnome-software] Use the AppStream data to populate the category tree



commit 8e83ad51a19b4cfe9c6b879210634e978188d6fe
Author: Richard Hughes <richard hughsie com>
Date:   Mon Sep 2 08:14:04 2013 +0100

    Use the AppStream data to populate the category tree

 src/gs-plugin-loader.c            |    8 ++-
 src/gs-plugin.h                   |    7 ++-
 src/gs-shell-category.c           |  133 +++++++++++++++++++++++++-----------
 src/gs-shell-category.h           |    4 +-
 src/gs-shell-overview.c           |    6 +-
 src/gs-shell.c                    |    4 +-
 src/plugins/gs-plugin-appstream.c |   75 ++++++++++++++++++++-
 src/plugins/gs-plugin-dummy.c     |   22 ++++++
 8 files changed, 207 insertions(+), 52 deletions(-)
---
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index df2dfe2..d7e2b4d 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -359,6 +359,7 @@ typedef struct {
        GSimpleAsyncResult              *res;
        GsPluginLoader                  *plugin_loader;
        gchar                           *value;
+       GsCategory                      *category;
 } GsPluginLoaderAsyncState;
 
 /******************************************************************************/
@@ -1155,7 +1156,7 @@ cd_plugin_loader_get_category_apps_thread_cb (GSimpleAsyncResult *res,
        GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) g_object_get_data (G_OBJECT 
(cancellable), "state");
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
        GsPlugin *plugin;
-       GsPluginSearchFunc plugin_func = NULL;
+       GsPluginCategoryFunc plugin_func = NULL;
        guint i;
 
        /* run each plugin */
@@ -1178,7 +1179,7 @@ cd_plugin_loader_get_category_apps_thread_cb (GSimpleAsyncResult *res,
                g_debug ("run %s on %s", function_name,
                         g_module_name (plugin->module));
                g_timer_start (plugin->timer);
-               ret = plugin_func (plugin, state->value, &state->list, cancellable, &error);
+               ret = plugin_func (plugin, state->category, &state->list, cancellable, &error);
                if (!ret) {
                        cd_plugin_loader_get_all_state_finish (state, error);
                        g_error_free (error);
@@ -1220,6 +1221,7 @@ cd_plugin_loader_get_category_apps_thread_cb (GSimpleAsyncResult *res,
 
        /* success */
        state->ret = TRUE;
+       g_object_unref (state->category);
        cd_plugin_loader_get_all_state_finish (state, NULL);
 out:
        return;
@@ -1248,7 +1250,7 @@ gs_plugin_loader_get_category_apps_async (GsPluginLoader *plugin_loader,
                                                user_data,
                                                gs_plugin_loader_get_category_apps_async);
        state->plugin_loader = g_object_ref (plugin_loader);
-       state->value = g_strdup (gs_category_get_id (category));
+       state->category = g_object_ref (category);
        if (cancellable != NULL)
                state->cancellable = g_object_ref (cancellable);
 
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 6fd56c6..fe56d28 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -81,6 +81,11 @@ typedef gboolean      (*GsPluginSearchFunc)          (GsPlugin       *plugin,
                                                         GList          **list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+typedef gboolean        (*GsPluginCategoryFunc)        (GsPlugin       *plugin,
+                                                        GsCategory     *category,
+                                                        GList          **list,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 typedef gboolean        (*GsPluginResultsFunc)         (GsPlugin       *plugin,
                                                         GList          **list,
                                                         GCancellable   *cancellable,
@@ -122,7 +127,7 @@ gboolean     gs_plugin_add_categories               (GsPlugin       *plugin,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
 gboolean        gs_plugin_add_category_apps            (GsPlugin       *plugin,
-                                                        const gchar    *id,
+                                                        GsCategory     *category,
                                                         GList          **list,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
diff --git a/src/gs-shell-category.c b/src/gs-shell-category.c
index 55118a1..c9e46ba 100644
--- a/src/gs-shell-category.c
+++ b/src/gs-shell-category.c
@@ -28,7 +28,9 @@
 #include "gs-shell-category.h"
 
 struct GsShellCategoryPrivate {
+       GsPluginLoader    *plugin_loader;
         GtkBuilder        *builder;
+       GCancellable      *cancellable;
         GsShell           *shell;
         GsCategory        *category;
 };
@@ -46,7 +48,7 @@ gs_shell_category_refresh (GsShellCategory *shell)
         gtk_widget_show (widget);
         widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "application_details_header"));
         gtk_widget_show (widget);
-        category = priv->category;
+        category = g_object_ref (priv->category);
         if (gs_category_get_parent (category))
                 category = gs_category_get_parent (category);
         gtk_label_set_label (GTK_LABEL (widget), gs_category_get_name (category));
@@ -104,14 +106,54 @@ create_app_tile (GsShellCategory *shell, GsApp *app)
         return button;
 }
 
+/**
+ * gs_shell_category_get_apps_cb:
+ **/
+static void
+gs_shell_category_get_apps_cb (GObject *source_object,
+                               GAsyncResult *res,
+                               gpointer user_data)
+{
+        GError *error = NULL;
+        gint i = 0;
+        GList *l;
+        GList *list;
+        GsApp *app;
+        GtkWidget *grid;
+        GtkWidget *tile;
+        GsShellCategory *shell = GS_SHELL_CATEGORY (user_data);
+        GsShellCategoryPrivate *priv = shell->priv;
+        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
+
+        list = gs_plugin_loader_get_category_apps_finish (plugin_loader,
+                                                          res,
+                                                          &error);
+       if (list == NULL) {
+               g_warning ("failed to get apps for category apps: %s", error->message);
+               g_error_free (error);
+               goto out;
+       }
+        grid = GTK_WIDGET (gtk_builder_get_object (priv->builder, "category_detail_grid"));
+        for (l = list; l != NULL; l = l->next) {
+                app = GS_APP (l->data);
+                tile = create_app_tile (shell, app);
+                if (gs_category_get_parent (priv->category) != NULL)
+                        gtk_grid_attach (GTK_GRID (grid), tile, 1 + (i % 2), i / 2, 1, 1);
+                else
+                        gtk_grid_attach (GTK_GRID (grid), tile, i % 3, i / 3, 1, 1);
+                i++;
+        }
+out:
+       g_list_free (list);
+
+}
+
 static void
 gs_shell_category_populate_filtered (GsShellCategory *shell, GsCategory *category)
 {
         GsShellCategoryPrivate *priv = shell->priv;
-        gint i;
-        GtkWidget *tile;
-        GsApp *app;
         GtkWidget *grid;
+        GsCategory *parent;
 
         grid = GTK_WIDGET (gtk_builder_get_object (priv->builder, "category_detail_grid"));
         gtk_grid_remove_column (GTK_GRID (grid), 2);
@@ -119,24 +161,20 @@ gs_shell_category_populate_filtered (GsShellCategory *shell, GsCategory *categor
         if (!category)
                 gtk_grid_remove_column (GTK_GRID (grid), 0);
 
-        /* FIXME load apps for this category and filter */
-        app = gs_app_new ("gnome-boxes");
-        gs_app_set_name (app, "Boxes");
-        gs_app_set_summary (app, "View and use virtual machines");
-        gs_app_set_url (app, "http://www.box.org";);
-        gs_app_set_kind (app, GS_APP_KIND_NORMAL);
-        gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
-        gs_app_set_pixbuf (app, gdk_pixbuf_new_from_file 
("/usr/share/icons/hicolor/48x48/apps/gnome-boxes.png", NULL));
-
-        for (i = 0; i < 30; i++) {
-                tile = create_app_tile (shell, app);
-                if (category)
-                        gtk_grid_attach (GTK_GRID (grid), tile, 1 + (i % 2), i / 2, 1, 1);
-                else
-                        gtk_grid_attach (GTK_GRID (grid), tile, i % 3, i / 3, 1, 1);
+        parent = gs_category_get_parent (category);
+        if (parent == NULL) {
+                g_debug ("search using %s",
+                         gs_category_get_id (category));
+        } else {
+                g_debug ("search using %s/%s",
+                         gs_category_get_id (parent),
+                         gs_category_get_id (category));
         }
-
-        g_object_unref (app);
+        gs_plugin_loader_get_category_apps_async (priv->plugin_loader,
+                                                  category,
+                                                  priv->cancellable,
+                                                  gs_shell_category_get_apps_cb,
+                                                  shell);
 }
 
 static void
@@ -211,32 +249,37 @@ gs_shell_category_create_filter_list (GsShellCategory *shell, GsCategory *catego
 void
 gs_shell_category_set_category (GsShellCategory *shell, GsCategory *category)
 {
-       GsShellCategoryPrivate *priv = shell->priv;
-        GsCategory *parent, *sub;
+        GsShellCategoryPrivate *priv = shell->priv;
+        GsCategory *sub;
+        GsCategory *selected = NULL;
         GList *list;
+        GList *l;
 
-        if (gs_category_get_parent (category)) {
-                parent = gs_category_get_parent (category);
-                sub = category;
-        }
-        else {
-                parent = category;
-                sub = NULL;
-                list = gs_category_get_subcategories (category);
-                if (list) {
-                        sub = GS_CATEGORY (list->data);
-                        g_list_free (list);
+        /* this means we've come from the app-view -> back */
+        if (gs_category_get_parent (category) != NULL)
+                return;
+
+        /* select favourites by default */
+        list = gs_category_get_subcategories (category);
+        for (l = list; l != NULL; l = l->next) {
+                sub = GS_CATEGORY (l->data);
+                if (g_strcmp0 (gs_category_get_id (sub), "favourites") == 0) {
+                        selected = sub;
+                        break;
                 }
         }
 
+        /* okay, no favourites, so just select the first entry */
+        if (selected == NULL && list != NULL)
+                selected = GS_CATEGORY (list->data);
+
+        /* save this */
         g_clear_object (&priv->category);
-        if (sub)
-                priv->category = g_object_ref (sub);
-        else
-                priv->category = g_object_ref (parent);
+        priv->category = g_object_ref (selected);
 
-        gs_shell_category_create_filter_list (shell, parent, sub);
-        gs_shell_category_populate_filtered (shell, sub);
+        /* find apps in this group */
+        gs_shell_category_create_filter_list (shell, category, selected);
+        g_list_free (list);
 }
 
 GsCategory *
@@ -259,6 +302,8 @@ gs_shell_category_finalize (GObject *object)
 
        g_clear_object (&priv->builder);
         g_clear_object (&priv->category);
+       g_clear_object (&priv->plugin_loader);
+       g_clear_object (&priv->cancellable);
 
        G_OBJECT_CLASS (gs_shell_category_parent_class)->finalize (object);
 }
@@ -274,11 +319,17 @@ gs_shell_category_class_init (GsShellCategoryClass *klass)
 }
 
 void
-gs_shell_category_setup (GsShellCategory *shell_category, GsShell *shell, GtkBuilder *builder)
+gs_shell_category_setup (GsShellCategory *shell_category,
+                         GsShell *shell,
+                         GsPluginLoader *plugin_loader,
+                         GtkBuilder *builder,
+                         GCancellable *cancellable)
 {
        GsShellCategoryPrivate *priv = shell_category->priv;
 
+       priv->plugin_loader = g_object_ref (plugin_loader);
         priv->builder = g_object_ref (builder);
+       priv->cancellable = g_object_ref (cancellable);
         priv->shell = shell;
 }
 
diff --git a/src/gs-shell-category.h b/src/gs-shell-category.h
index d732b4c..840d4f8 100644
--- a/src/gs-shell-category.h
+++ b/src/gs-shell-category.h
@@ -60,6 +60,8 @@ GsCategory      *gs_shell_category_get_category (GsShellCategory *shell_category
 void            gs_shell_category_refresh      (GsShellCategory *shell_category);
 void             gs_shell_category_setup        (GsShellCategory *shell_category,
                                                  GsShell         *shell,
-                                                 GtkBuilder      *builder);
+                                                 GsPluginLoader  *plugin_loader,
+                                                 GtkBuilder      *builder,
+                                                 GCancellable    *cancellable);
 
 #endif /* __GS_SHELL_CATEGORY_H */
diff --git a/src/gs-shell-overview.c b/src/gs-shell-overview.c
index 7cabb1f..a7ec07c 100644
--- a/src/gs-shell-overview.c
+++ b/src/gs-shell-overview.c
@@ -312,9 +312,9 @@ gs_shell_overview_get_categories_cb (GObject *source_object,
        GtkWidget *grid;
        GtkWidget *tile;
 
-       list = gs_plugin_loader_get_featured_finish (plugin_loader,
-                                                    res,
-                                                    &error);
+       list = gs_plugin_loader_get_categories_finish (plugin_loader,
+                                                      res,
+                                                      &error);
        if (list == NULL) {
                g_warning ("failed to get categories: %s", error->message);
                g_error_free (error);
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 862a5be..2b8e289 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -431,7 +431,9 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
                                priv->cancellable);
        gs_shell_category_setup (priv->shell_category,
                                  shell,
-                                priv->builder);
+                                priv->plugin_loader,
+                                priv->builder,
+                                priv->cancellable);
 
         /* set up search */
         widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "entry_search"));
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index ac8ed60..93be7e4 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -45,6 +45,7 @@ typedef struct {
        gchar           *name;
        gchar           *summary;
        gchar           *icon;
+       GPtrArray       *appcategories;
 } GsAppstreamItem;
 
 struct GsPluginPrivate {
@@ -69,6 +70,7 @@ gs_appstream_item_free (gpointer data)
        g_free (item->name);
        g_free (item->summary);
        g_free (item->icon);
+       g_ptr_array_unref (item->appcategories);
        g_free (item);
 }
 
@@ -241,6 +243,7 @@ gs_appstream_start_element_cb (GMarkupParseContext *context,
                        return;
                }
                plugin->priv->item_temp = g_new0 (GsAppstreamItem, 1);
+               plugin->priv->item_temp->appcategories = g_ptr_array_new_with_free_func (g_free);
                break;
        case GS_APPSTREAM_XML_SECTION_ID:
        case GS_APPSTREAM_XML_SECTION_PKGNAME:
@@ -328,9 +331,19 @@ gs_appstream_text_cb (GMarkupParseContext *context,
        case GS_APPSTREAM_XML_SECTION_APPLICATIONS:
        case GS_APPSTREAM_XML_SECTION_APPLICATION:
        case GS_APPSTREAM_XML_SECTION_APPCATEGORIES:
-       case GS_APPSTREAM_XML_SECTION_APPCATEGORY:
                /* ignore */
                break;
+       case GS_APPSTREAM_XML_SECTION_APPCATEGORY:
+               if (plugin->priv->item_temp == NULL) {
+                       g_set_error_literal (error,
+                                            GS_PLUGIN_ERROR,
+                                            GS_PLUGIN_ERROR_FAILED,
+                                            "item_temp category invalid");
+                       return;
+               }
+               g_ptr_array_add (plugin->priv->item_temp->appcategories,
+                                g_strndup (text, text_len));
+               break;
        case GS_APPSTREAM_XML_SECTION_ID:
                if (plugin->priv->item_temp == NULL ||
                    plugin->priv->item_temp->id != NULL) {
@@ -505,7 +518,6 @@ gs_plugin_destroy (GsPlugin *plugin)
        g_hash_table_unref (plugin->priv->hash_pkgname);
 }
 
-
 /**
  * gs_plugin_startup:
  */
@@ -692,3 +704,62 @@ gs_plugin_refine (GsPlugin *plugin,
 out:
        return ret;
 }
+
+static gboolean
+in_array (GPtrArray *array, const gchar *search)
+{
+       const gchar *tmp;
+       guint i;
+
+       for (i = 0; i < array->len; i++) {
+               tmp = g_ptr_array_index (array, i);
+               if (g_strcmp0 (tmp, search) == 0)
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+/**
+ * gs_plugin_add_category_apps:
+ */
+gboolean
+gs_plugin_add_category_apps (GsPlugin *plugin,
+                            GsCategory *category,
+                            GList **list,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       const gchar *search_id1;
+       const gchar *search_id2 = NULL;
+       gboolean ret = TRUE;
+       GsApp *app;
+       GsAppstreamItem *item;
+       GsCategory *parent;
+       guint i;
+
+       /* get the two search terms */
+       search_id1 = gs_category_get_id (category);
+       parent = gs_category_get_parent (category);
+       if (parent != NULL)
+               search_id2 = gs_category_get_id (parent);
+
+       /* just look at each app in turn */
+       for (i = 0; i < plugin->priv->array->len; i++) {
+               item = g_ptr_array_index (plugin->priv->array, i);
+               if (item->id == NULL)
+                       continue;
+               if (!in_array (item->appcategories, search_id1))
+                       continue;
+               if (search_id2 != NULL && !in_array (item->appcategories, search_id2))
+                       continue;
+
+               /* got a search match, so add all the data we can */
+               app = gs_app_new (item->id);
+               ret = gs_plugin_refine_item (plugin, app, item, error);
+               if (!ret)
+                       goto out;
+               gs_plugin_add_app (list, app);
+       }
+out:
+       return ret;
+}
diff --git a/src/plugins/gs-plugin-dummy.c b/src/plugins/gs-plugin-dummy.c
index 9474af7..542b582 100644
--- a/src/plugins/gs-plugin-dummy.c
+++ b/src/plugins/gs-plugin-dummy.c
@@ -184,3 +184,25 @@ gs_plugin_refine (GsPlugin *plugin,
        }
        return TRUE;
 }
+
+/**
+ * gs_plugin_add_category_apps:
+ */
+gboolean
+gs_plugin_add_category_apps (GsPlugin *plugin,
+                            GsCategory *category,
+                            GList **list,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       GsApp *app;
+       app = gs_app_new ("gnome-boxes");
+       gs_app_set_name (app, "Boxes");
+       gs_app_set_summary (app, "View and use virtual machines");
+       gs_app_set_url (app, "http://www.box.org";);
+       gs_app_set_kind (app, GS_APP_KIND_NORMAL);
+       gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
+       gs_app_set_pixbuf (app, gdk_pixbuf_new_from_file 
("/usr/share/icons/hicolor/48x48/apps/gnome-boxes.png", NULL));
+       gs_plugin_add_app (list, app);
+       return TRUE;
+}


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