[gnome-software] Use the AppStream data to populate the category tree
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Use the AppStream data to populate the category tree
- Date: Mon, 2 Sep 2013 12:30:56 +0000 (UTC)
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]