[gnome-software: 3/25] lib: Add a GsCategoryManager




commit 71d56594558fe2068c37a6bae9aaab91e0e0c2d3
Author: Philip Withnall <pwithnall endlessos org>
Date:   Mon Feb 1 18:41:26 2021 +0000

    lib: Add a GsCategoryManager
    
    This is a single place which keeps track of the `GsCategory` objects and
    allows looking them up, which ensures that if one plugin modifies a
    `GsCategory` object (for example, to change the number of apps in it),
    all other plugins will see that change.
    
    It allows decoupling the enumeration of categories (which should always
    be sync, since they’re coming from a static array) from refining them
    (which should be async, as it depends on data loaded by plugins).
    
    This commit adds the category manager, but doesn’t use it yet.
    
    Signed-off-by: Philip Withnall <pwithnall endlessos org>

 lib/gnome-software.h      |   1 +
 lib/gs-category-manager.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/gs-category-manager.h |  32 +++++++++
 lib/gs-plugin-loader.c    |  27 ++++++++
 lib/gs-plugin-loader.h    |   3 +
 lib/meson.build           |   2 +
 6 files changed, 238 insertions(+)
---
diff --git a/lib/gnome-software.h b/lib/gnome-software.h
index ef53be562..a19a27c38 100644
--- a/lib/gnome-software.h
+++ b/lib/gnome-software.h
@@ -17,6 +17,7 @@
 #include <gs-app-collation.h>
 #include <gs-autocleanups.h>
 #include <gs-category.h>
+#include <gs-category-manager.h>
 #include <gs-desktop-data.h>
 #include <gs-enums.h>
 #include <gs-metered.h>
diff --git a/lib/gs-category-manager.c b/lib/gs-category-manager.c
new file mode 100644
index 000000000..fd42857de
--- /dev/null
+++ b/lib/gs-category-manager.c
@@ -0,0 +1,173 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2021 Endless OS Foundation, Inc
+ *
+ * Author: Philip Withnall <pwithnall endlessos org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/**
+ * SECTION:gs-category-manager
+ * @short_description: A container to store #GsCategory instances in
+ *
+ * #GsCategoryManager is a container object which stores #GsCategory instances,
+ * so that they can be consistently reused by other code, without creating
+ * multiple #GsCategory instances for the same category ID.
+ *
+ * It is intended to be used as a singleton, and typically accessed by calling
+ * gs_plugin_loader_get_category_manager().
+ *
+ * Since: 40
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include "gs-category-manager.h"
+#include "gs-desktop-data.h"
+
+struct _GsCategoryManager
+{
+       GObject          parent;
+
+       /* Array of #GsCategory instances corresponding to the entries in gs_desktop_get_data()
+        * The +1 is for a NULL terminator */
+       GsCategory      *categories[GS_DESKTOP_DATA_N_ENTRIES + 1];
+};
+
+G_DEFINE_TYPE (GsCategoryManager, gs_category_manager, G_TYPE_OBJECT)
+
+static void
+gs_category_manager_dispose (GObject *object)
+{
+       GsCategoryManager *self = GS_CATEGORY_MANAGER (object);
+
+       for (gsize i = 0; i < G_N_ELEMENTS (self->categories); i++)
+               g_clear_object (&self->categories[i]);
+
+       G_OBJECT_CLASS (gs_category_manager_parent_class)->dispose (object);
+}
+
+static void
+gs_category_manager_class_init (GsCategoryManagerClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->dispose = gs_category_manager_dispose;
+}
+
+static void
+gs_category_manager_init (GsCategoryManager *self)
+{
+       const GsDesktopData *msdata;
+
+       /* Set up the category data, and check our expectations about the length
+        * of gs_desktop_get_data() match reality. */
+       msdata = gs_desktop_get_data ();
+       for (gsize i = 0; msdata[i].id != NULL; i++) {
+               g_autoptr(GsCategory) category = NULL;
+               g_autofree gchar *msgctxt = NULL;
+
+               /* add parent category */
+               category = gs_category_new (msdata[i].id);
+               gs_category_set_icon (category, msdata[i].icon);
+               gs_category_set_name (category, gettext (msdata[i].name));
+               gs_category_set_score (category, msdata[i].score);
+               msgctxt = g_strdup_printf ("Menu of %s", msdata[i].name);
+
+               /* add subcategories */
+               for (gsize j = 0; msdata[i].mapping[j].id != NULL; j++) {
+                       const GsDesktopMap *map = &msdata[i].mapping[j];
+                       g_autoptr(GsCategory) sub = gs_category_new (map->id);
+                       for (gsize k = 0; map->fdo_cats[k] != NULL; k++)
+                               gs_category_add_desktop_group (sub, map->fdo_cats[k]);
+                       gs_category_set_name (sub, g_dpgettext2 (GETTEXT_PACKAGE,
+                                                                msgctxt,
+                                                                map->name));
+                       gs_category_add_child (category, sub);
+               }
+
+               g_assert (i < G_N_ELEMENTS (self->categories) - 1);
+               self->categories[i] = g_steal_pointer (&category));
+       }
+
+       g_assert (self->categories[G_N_ELEMENTS (self->categories) - 2] != NULL);
+       g_assert (self->categories[G_N_ELEMENTS (self->categories) - 1] == NULL);
+}
+
+/**
+ * gs_category_manager_new:
+ *
+ * Create a new #GsCategoryManager. It will contain all the categories, but
+ * their sizes will not be set until gs_category_increment_size() is called
+ * on them.
+ *
+ * Returns: (transfer full): a new #GsCategoryManager
+ * Since: 40
+ */
+GsCategoryManager *
+gs_category_manager_new (void)
+{
+       return g_object_new (GS_TYPE_CATEGORY_MANAGER, NULL);
+}
+
+/**
+ * gs_category_manager_lookup:
+ * @self: a #GsCategoryManager
+ * @id: ID of the category to look up
+ *
+ * Look up a category by its ID. If the category is not found, %NULL is
+ * returned.
+ *
+ * Returns: (transfer full) (nullable): the #GsCategory, or %NULL
+ * Since: 40
+ */
+GsCategory *
+gs_category_manager_lookup (GsCategoryManager *self,
+                            const gchar       *id)
+{
+       g_return_val_if_fail (GS_IS_CATEGORY_MANAGER (self), NULL);
+       g_return_val_if_fail (id != NULL && *id != '\0', NULL);
+
+       /* There are only on the order of 10 categories, so this is quick */
+       for (gsize i = 0; i < G_N_ELEMENTS (self->categories) - 1; i++) {
+               if (g_str_equal (gs_category_get_id (self->categories[i]), id))
+                       return g_object_ref (self->categories[i]);
+       }
+
+       return NULL;
+}
+
+/**
+ * gs_category_manager_get_categories:
+ * @self: a #GsCategoryManager
+ * @out_n_categories: (optional) (out caller-allocates): return location for
+ *     the number of categories in the return value, or %NULL to ignore
+ *
+ * Get the full list of categories from the category manager. The returned array
+ * is %NULL terminated and guaranteed to be non-%NULL (although it may be
+ * empty).
+ *
+ * If @out_n_categories is provided, it will be set to the number of #GsCategory
+ * objects in the return value, not including the %NULL terminator.
+ *
+ * Returns: (array length=out_n_categories) (transfer none) (not nullable): the
+ *     categories; do not free this memory
+ * Since: 40
+ */
+GsCategory * const *
+gs_category_manager_get_categories (GsCategoryManager *self,
+                                    gsize             *out_n_categories)
+{
+       g_return_val_if_fail (GS_IS_CATEGORY_MANAGER (self), NULL);
+
+       if (out_n_categories != NULL)
+               *out_n_categories = G_N_ELEMENTS (self->categories) - 1;
+
+       return self->categories;
+}
diff --git a/lib/gs-category-manager.h b/lib/gs-category-manager.h
new file mode 100644
index 000000000..1b7f6e52d
--- /dev/null
+++ b/lib/gs-category-manager.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2021 Endless OS Foundation, Inc
+ *
+ * Author: Philip Withnall <pwithnall endlessos org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gs-category.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_CATEGORY_MANAGER (gs_category_manager_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsCategoryManager, gs_category_manager, GS, CATEGORY_MANAGER, GObject)
+
+GsCategoryManager      *gs_category_manager_new        (void);
+
+GsCategory             *gs_category_manager_lookup     (GsCategoryManager      *self,
+                                                        const gchar            *id);
+
+GsCategory * const     *gs_category_manager_get_categories (GsCategoryManager  *self,
+                                                             gsize             *out_n_categories);
+
+G_END_DECLS
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index b951cdc70..a2fb52074 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -21,6 +21,7 @@
 #include "gs-app-collation.h"
 #include "gs-app-private.h"
 #include "gs-app-list-private.h"
+#include "gs-category-manager.h"
 #include "gs-category-private.h"
 #include "gs-ioprio.h"
 #include "gs-plugin-loader.h"
@@ -67,6 +68,8 @@ typedef struct
        gulong                   network_available_notify_handler;
        gulong                   network_metered_notify_handler;
 
+       GsCategoryManager       *category_manager;
+
 #ifdef HAVE_SYSPROF
        SysprofCaptureWriter    *sysprof_writer;  /* (owned) (nullable) */
 #endif
@@ -2771,6 +2774,8 @@ gs_plugin_loader_dispose (GObject *object)
        g_clear_object (&priv->soup_session);
        g_clear_object (&priv->settings);
        g_clear_pointer (&priv->pending_apps, g_ptr_array_unref);
+       g_clear_object (&priv->category_manager);
+
 #ifdef HAVE_SYSPROF
        g_clear_pointer (&priv->sysprof_writer, sysprof_capture_writer_unref);
 #endif
@@ -2938,6 +2943,9 @@ gs_plugin_loader_init (GsPluginLoader *plugin_loader)
                priv->locale = g_strdup (setlocale (LC_MESSAGES, NULL));
        }
 
+       /* get the category manager */
+       priv->category_manager = gs_category_manager_new ();
+
        /* the settings key sets the initial override */
        priv->disallow_updates = g_hash_table_new (g_direct_hash, g_direct_equal);
        gs_plugin_loader_allow_updates_recheck (plugin_loader);
@@ -3984,3 +3992,22 @@ gs_plugin_loader_get_locale (GsPluginLoader *plugin_loader)
        GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
        return priv->locale;
 }
+
+/**
+ * gs_plugin_loader_get_category_manager:
+ * @plugin_loader: a #GsPluginLoader
+ *
+ * Get the category manager singleton.
+ *
+ * Returns: (transfer none): a category manager
+ * Since: 40
+ */
+GsCategoryManager *
+gs_plugin_loader_get_category_manager (GsPluginLoader *plugin_loader)
+{
+       GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+
+       g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), NULL);
+
+       return priv->category_manager;
+}
diff --git a/lib/gs-plugin-loader.h b/lib/gs-plugin-loader.h
index f72957252..7019d1de6 100644
--- a/lib/gs-plugin-loader.h
+++ b/lib/gs-plugin-loader.h
@@ -13,6 +13,7 @@
 
 #include "gs-app.h"
 #include "gs-category.h"
+#include "gs-category-manager.h"
 #include "gs-plugin-event.h"
 #include "gs-plugin-private.h"
 #include "gs-plugin-job.h"
@@ -97,4 +98,6 @@ void            gs_plugin_loader_set_max_parallel_ops  (GsPluginLoader *plugin_l
 
 const gchar    *gs_plugin_loader_get_locale            (GsPluginLoader *plugin_loader);
 
+GsCategoryManager *gs_plugin_loader_get_category_manager (GsPluginLoader *plugin_loader);
+
 G_END_DECLS
diff --git a/lib/meson.build b/lib/meson.build
index 8ea280678..95d2a4f4c 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -8,6 +8,7 @@ libgnomesoftware_public_headers = [
   'gs-app-list.h',
   'gs-autocleanups.h',
   'gs-category.h',
+  'gs-category-manager.h',
   'gs-desktop-data.h',
   'gs-ioprio.h',
   'gs-metered.h',
@@ -64,6 +65,7 @@ libgnomesoftware = static_library(
     'gs-app.c',
     'gs-app-list.c',
     'gs-category.c',
+    'gs-category-manager.c',
     'gs-debug.c',
     'gs-desktop-data.c',
     'gs-ioprio.c',


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