summaryrefslogtreecommitdiffstats
path: root/src/gs-folders.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gs-folders.c')
-rw-r--r--src/gs-folders.c610
1 files changed, 610 insertions, 0 deletions
diff --git a/src/gs-folders.c b/src/gs-folders.c
new file mode 100644
index 0000000..295ec39
--- /dev/null
+++ b/src/gs-folders.c
@@ -0,0 +1,610 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2013 Richard Hughes <richard@hughsie.com>
+ * Copyright (C) 2013 Matthias Clasen <mclasen@redhat.com>
+ * Copyright (C) 2015 Kalev Lember <klember@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gs-folders.h"
+
+#define APP_FOLDER_SCHEMA "org.gnome.desktop.app-folders"
+#define APP_FOLDER_CHILD_SCHEMA "org.gnome.desktop.app-folders.folder"
+
+/* We are loading folders from a settings with type
+ * a{sas}, which maps folder ids to list of app ids.
+ *
+ * For convenience, we unfold this variant into GsFolder
+ * structs and two hash tables, one with folder ids
+ * as keys, and one with app ids.
+ */
+
+typedef struct
+{
+ gchar *id;
+ gchar *name;
+ gchar *translated;
+ gboolean translate;
+ GHashTable *apps;
+ GHashTable *categories;
+ GHashTable *excluded_apps;
+} GsFolder;
+
+struct _GsFolders
+{
+ GObject parent_instance;
+
+ GSettings *settings;
+ GHashTable *folders;
+ GHashTable *apps;
+ GHashTable *categories;
+};
+
+G_DEFINE_TYPE (GsFolders, gs_folders, G_TYPE_OBJECT)
+
+#if 0
+static void
+dump_set (GHashTable *set)
+{
+ GHashTableIter iter;
+ const gchar *key;
+
+ g_hash_table_iter_init (&iter, set);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL)) {
+ g_print ("\t\t%s\n", key);
+ }
+}
+
+static void
+dump_map (GHashTable *map)
+{
+ GHashTableIter iter;
+ const gchar *key;
+ GsFolder *folder;
+
+ g_hash_table_iter_init (&iter, map);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&folder)) {
+ g_print ("\t%s -> %s\n", key, folder->id);
+ }
+}
+
+static void
+dump (GsFolders *folders)
+{
+ GHashTableIter iter;
+ GsFolder *folder;
+
+ g_hash_table_iter_init (&iter, folders->folders);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&folder)) {
+ g_print ("folder %s\n", folder->id);
+ g_print ("\tname %s\n", folder->name);
+ g_print ("\ttranslate %d\n", folder->translate);
+ if (g_hash_table_size (folder->apps) > 0) {
+ g_print ("\tapps\n");
+ dump_set (folder->apps);
+ }
+ if (g_hash_table_size (folder->categories) > 0) {
+ g_print ("\tcategories\n");
+ dump_set (folder->categories);
+ }
+ if (g_hash_table_size (folder->excluded_apps) > 0) {
+ g_print ("\texcluded\n");
+ dump_set (folder->excluded_apps);
+ }
+ }
+
+ g_print ("app mapping\n");
+ dump_map (folders->apps);
+ g_print ("category mapping\n");
+ dump_map (folders->categories);
+}
+#endif
+
+static gchar *
+lookup_folder_name (const gchar *id)
+{
+ gchar *name = NULL;
+ g_autofree gchar *file = NULL;
+ g_autoptr(GKeyFile) key_file = NULL;
+
+ file = g_build_filename ("desktop-directories", id, NULL);
+ key_file = g_key_file_new ();
+ if (g_key_file_load_from_data_dirs (key_file, file, NULL, G_KEY_FILE_NONE, NULL)) {
+ name = g_key_file_get_locale_string (key_file, "Desktop Entry", "Name", NULL, NULL);
+ }
+ return name;
+}
+
+static GsFolder *
+gs_folder_new (const gchar *id, const gchar *name, gboolean translate)
+{
+ GsFolder *folder;
+
+ folder = g_new0 (GsFolder, 1);
+ folder->id = g_strdup (id);
+ folder->name = g_strdup (name);
+ folder->translate = translate;
+ if (translate) {
+ folder->translated = lookup_folder_name (name);
+ }
+ folder->apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ folder->categories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ folder->excluded_apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ return folder;
+}
+
+static void
+gs_folder_free (GsFolder *folder)
+{
+ g_free (folder->id);
+ g_free (folder->name);
+ g_free (folder->translated);
+ g_hash_table_destroy (folder->apps);
+ g_hash_table_destroy (folder->categories);
+ g_hash_table_destroy (folder->excluded_apps);
+ g_free (folder);
+}
+
+static void
+load (GsFolders *folders)
+{
+ GsFolder *folder;
+ guint i, j;
+ gboolean translate;
+ GHashTableIter iter;
+ gchar *app;
+ gchar *category;
+ g_autofree gchar *path = NULL;
+ g_auto(GStrv) ids = NULL;
+
+ folders->folders = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)gs_folder_free);
+ folders->apps = g_hash_table_new (g_str_hash, g_str_equal);
+ folders->categories = g_hash_table_new (g_str_hash, g_str_equal);
+
+ ids = g_settings_get_strv (folders->settings, "folder-children");
+ g_object_get (folders->settings, "path", &path, NULL);
+ for (i = 0; ids[i]; i++) {
+ g_auto(GStrv) apps = NULL;
+ g_auto(GStrv) categories = NULL;
+ g_autofree gchar *child_path = NULL;
+ g_auto(GStrv) excluded_apps = NULL;
+ g_autofree gchar *name = NULL;
+ g_autoptr(GSettings) settings = NULL;
+
+ child_path = g_strconcat (path, "folders/", ids[i], "/", NULL);
+ settings = g_settings_new_with_path (APP_FOLDER_CHILD_SCHEMA, child_path);
+ if (settings == NULL) {
+ g_warning ("ignoring folder child %s as invalid", ids[i]);
+ continue;
+ }
+ name = g_settings_get_string (settings, "name");
+ translate = g_settings_get_boolean (settings, "translate");
+ folder = gs_folder_new (ids[i], name, translate);
+
+ excluded_apps = g_settings_get_strv (settings, "excluded-apps");
+ for (j = 0; excluded_apps[j]; j++) {
+ g_hash_table_add (folder->excluded_apps, g_strdup (excluded_apps[j]));
+ }
+
+ apps = g_settings_get_strv (settings, "apps");
+ for (j = 0; apps[j]; j++) {
+ if (!g_hash_table_contains (folder->excluded_apps, apps[j]))
+ g_hash_table_add (folder->apps, g_strdup (apps[j]));
+ }
+
+ categories = g_settings_get_strv (settings, "categories");
+ for (j = 0; categories[j]; j++) {
+ g_hash_table_add (folder->categories, g_strdup (categories[j]));
+ }
+
+ g_hash_table_insert (folders->folders, (gpointer)folder->id, folder);
+ g_hash_table_iter_init (&iter, folder->apps);
+ while (g_hash_table_iter_next (&iter, (gpointer*)&app, NULL)) {
+ g_hash_table_insert (folders->apps, app, folder);
+ }
+
+ g_hash_table_iter_init (&iter, folder->categories);
+ while (g_hash_table_iter_next (&iter, (gpointer*)&category, NULL)) {
+ g_hash_table_insert (folders->categories, category, folder);
+ }
+ }
+}
+
+static void
+save (GsFolders *folders)
+{
+ GHashTableIter iter;
+ GsFolder *folder;
+ gpointer keys;
+ g_autofree gchar *path = NULL;
+ g_autofree gpointer apps = NULL;
+
+ g_object_get (folders->settings, "path", &path, NULL);
+ g_hash_table_iter_init (&iter, folders->folders);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&folder)) {
+ g_autofree gchar *child_path = NULL;
+ g_autoptr(GSettings) settings = NULL;
+
+ child_path = g_strconcat (path, "folders/", folder->id, "/", NULL);
+ settings = g_settings_new_with_path (APP_FOLDER_CHILD_SCHEMA, child_path);
+ g_settings_set_string (settings, "name", folder->name);
+ g_settings_set_boolean (settings, "translate", folder->translate);
+ keys = g_hash_table_get_keys_as_array (folder->apps, NULL);
+ g_settings_set_strv (settings, "apps", (const gchar * const *)keys);
+ g_free (keys);
+
+ keys = g_hash_table_get_keys_as_array (folder->excluded_apps, NULL);
+ g_settings_set_strv (settings, "excluded-apps", (const gchar * const *)keys);
+ g_free (keys);
+
+ keys = g_hash_table_get_keys_as_array (folder->categories, NULL);
+ g_settings_set_strv (settings, "categories", (const gchar * const *)keys);
+ g_free (keys);
+ }
+
+ apps = gs_folders_get_nonempty_folders (folders);
+ g_settings_set_strv (folders->settings, "folder-children",
+ (const gchar * const *)apps);
+}
+
+static void
+clear (GsFolders *folders)
+{
+ g_hash_table_unref (folders->apps);
+ g_hash_table_unref (folders->categories);
+ g_hash_table_unref (folders->folders);
+
+ folders->apps = NULL;
+ folders->categories = NULL;
+ folders->folders = NULL;
+}
+
+static void
+gs_folders_dispose (GObject *object)
+{
+ GsFolders *folders = GS_FOLDERS (object);
+
+ g_clear_object (&folders->settings);
+
+ G_OBJECT_CLASS (gs_folders_parent_class)->dispose (object);
+}
+
+static void
+gs_folders_finalize (GObject *object)
+{
+ GsFolders *folders = GS_FOLDERS (object);
+
+ clear (folders);
+
+ G_OBJECT_CLASS (gs_folders_parent_class)->finalize (object);
+}
+
+static void
+gs_folders_class_init (GsFoldersClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = gs_folders_dispose;
+ object_class->finalize = gs_folders_finalize;
+}
+
+static void
+gs_folders_init (GsFolders *folders)
+{
+ folders->settings = g_settings_new (APP_FOLDER_SCHEMA);
+ load (folders);
+}
+
+static GsFolders *
+gs_folders_new (void)
+{
+ return GS_FOLDERS (g_object_new (GS_TYPE_FOLDERS, NULL));
+}
+
+static GsFolders *singleton;
+
+GsFolders *
+gs_folders_get (void)
+{
+ if (!singleton)
+ singleton = gs_folders_new ();
+
+ return g_object_ref (singleton);
+}
+
+gchar **
+gs_folders_get_folders (GsFolders *folders)
+{
+ return (gchar**) g_hash_table_get_keys_as_array (folders->folders, NULL);
+}
+
+gchar **
+gs_folders_get_nonempty_folders (GsFolders *folders)
+{
+ GHashTableIter iter;
+ GsFolder *folder;
+ g_autoptr(GHashTable) tmp = NULL;
+
+ tmp = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_iter_init (&iter, folders->apps);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&folder)) {
+ g_hash_table_add (tmp, folder->id);
+ }
+
+ g_hash_table_iter_init (&iter, folders->categories);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&folder)) {
+ g_hash_table_add (tmp, folder->id);
+ }
+
+ return (gchar **) g_hash_table_get_keys_as_array (tmp, NULL);
+}
+
+static void
+canonicalize_key (gchar *key)
+{
+ gchar *p;
+
+ for (p = key; *p != 0; p++) {
+ gchar c = *p;
+
+ if (c != '-' &&
+ (c < '0' || c > '9') &&
+ (c < 'A' || c > 'Z') &&
+ (c < 'a' || c > 'z'))
+ *p = '-';
+ }
+}
+
+const gchar *
+gs_folders_add_folder (GsFolders *folders, const gchar *id)
+{
+ GsFolder *folder;
+ g_autofree gchar *key = NULL;
+
+ key = g_strdup (id);
+ canonicalize_key (key);
+ folder = g_hash_table_lookup (folders->folders, key);
+ if (!folder) {
+ folder = gs_folder_new (key, id, FALSE);
+ g_hash_table_insert (folders->folders, folder->id, folder);
+ }
+
+ return folder->id;
+}
+
+void
+gs_folders_remove_folder (GsFolders *folders, const gchar *id)
+{
+ GsFolder *folder = NULL;
+ GHashTableIter iter;
+
+ if (id == NULL)
+ return;
+
+ g_hash_table_iter_init (&iter, folders->apps);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer*)&folder)) {
+ if (folder && g_strcmp0 (id, folder->id) == 0) {
+ g_hash_table_iter_remove (&iter);
+ }
+ }
+
+ g_hash_table_iter_init (&iter, folders->categories);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer*)&folder)) {
+ if (folder && g_strcmp0 (id, folder->id) == 0) {
+ g_hash_table_iter_remove (&iter);
+ }
+ }
+
+ if (folder != NULL)
+ g_hash_table_remove (folders->folders, folder->id);
+}
+
+const gchar *
+gs_folders_get_folder_name (GsFolders *folders, const gchar *id)
+{
+ GsFolder *folder;
+
+ folder = g_hash_table_lookup (folders->folders, id);
+
+ if (folder) {
+ if (folder->translated)
+ return folder->translated;
+
+ return folder->name;
+ }
+
+ return NULL;
+}
+
+void
+gs_folders_set_folder_name (GsFolders *folders, const gchar *id, const gchar *name)
+{
+ GsFolder *folder;
+
+ folder = g_hash_table_lookup (folders->folders, id);
+ if (folder) {
+ g_free (folder->name);
+ g_free (folder->translated);
+ folder->name = g_strdup (name);
+ folder->translate = FALSE;
+ }
+}
+
+static GsFolder *
+get_app_folder (GsFolders *folders, const gchar *app, GPtrArray *categories)
+{
+ GsFolder *folder;
+ const gchar *category;
+ guint i;
+
+ folder = g_hash_table_lookup (folders->apps, app);
+ if (!folder && categories) {
+ for (i = 0; i < categories->len; i++) {
+ category = g_ptr_array_index (categories, i);
+ if (category == NULL)
+ continue;
+
+ folder = g_hash_table_lookup (folders->categories, category);
+ if (folder) {
+ break;
+ }
+ }
+ }
+ if (folder) {
+ if (g_hash_table_contains (folder->excluded_apps, app)) {
+ folder = NULL;
+ }
+ }
+
+ return folder;
+}
+
+const gchar *
+gs_folders_get_app_folder (GsFolders *folders, const gchar *app, GPtrArray *categories)
+{
+ GsFolder *folder;
+
+ if (app == NULL)
+ return NULL;
+
+ folder = get_app_folder (folders, app, categories);
+
+ return folder ? folder->id : NULL;
+}
+
+void
+gs_folders_set_app_folder (GsFolders *folders, const gchar *app, GPtrArray *categories, const gchar *id)
+{
+ GsFolder *folder;
+
+ folder = get_app_folder (folders, app, categories);
+
+ if (folder) {
+ g_hash_table_remove (folders->apps, app);
+ g_hash_table_remove (folder->apps, app);
+ }
+
+ if (id) {
+ gchar *app_id;
+
+ app_id = g_strdup (app);
+ folder = g_hash_table_lookup (folders->folders, id);
+ g_hash_table_add (folder->apps, app_id);
+ g_hash_table_remove (folder->excluded_apps, app);
+ g_hash_table_insert (folders->apps, app_id, folder);
+ } else {
+ guint i;
+ gchar *category;
+
+ for (i = 0; i < categories->len; i++) {
+ category = g_ptr_array_index (categories, i);
+ folder = g_hash_table_lookup (folders->categories, category);
+ if (folder) {
+ g_hash_table_add (folder->excluded_apps, g_strdup (app));
+ }
+ }
+ }
+}
+
+void
+gs_folders_save (GsFolders *folders)
+{
+ save (folders);
+}
+
+void
+gs_folders_revert (GsFolders *folders)
+{
+ clear (folders);
+ load (folders);
+}
+
+/* Ensure we have the default folders for Utilities and YaST.
+ * We can't do this as default values, since the schemas have
+ * no fixed path.
+ *
+ * The app lists come from gnome-menus: layout/gnome-applications.menu
+ */
+void
+gs_folders_convert (void)
+{
+ g_autoptr(GSettings) settings = NULL;
+ g_auto(GStrv) ids = NULL;
+
+ settings = g_settings_new (APP_FOLDER_SCHEMA);
+ ids = g_settings_get_strv (settings, "folder-children");
+ if (g_strv_length (ids) == 0) {
+ const gchar * const children[] = {
+ "Utilities",
+ "YaST",
+ NULL
+ };
+ const gchar * const utilities_categories[] = {
+ "X-GNOME-Utilities",
+ NULL
+ };
+ const gchar * const utilities_apps[] = {
+ "gnome-abrt.desktop",
+ "gnome-system-log.desktop",
+ "gnome-system-monitor.desktop",
+ "gucharmap.desktop",
+ "nm-connection-editor.desktop",
+ "org.gnome.baobab.desktop",
+ "org.gnome.Calculator.desktop",
+ "org.gnome.DejaDup.desktop",
+ "org.gnome.Dictionary.desktop",
+ "org.gnome.DiskUtility.desktop",
+ "org.gnome.eog.desktop",
+ "org.gnome.Evince.desktop",
+ "org.gnome.FileRoller.desktop",
+ "org.gnome.fonts.desktop",
+ "org.gnome.Screenshot.desktop",
+ "org.gnome.seahorse.Application.desktop",
+ "org.gnome.Terminal.desktop",
+ "org.gnome.tweaks.desktop",
+ "org.gnome.Usage.desktop",
+ "simple-scan.desktop",
+ "vinagre.desktop",
+ "yelp.desktop",
+ NULL
+ };
+ const gchar * const yast_categories[] = {
+ "X-SuSE-YaST",
+ NULL
+ };
+
+ gchar *path;
+ gchar *child_path;
+ GSettings *child;
+
+ g_settings_set_strv (settings, "folder-children", children);
+ g_object_get (settings, "path", &path, NULL);
+
+ child_path = g_strconcat (path, "folders/Utilities/", NULL);
+ child = g_settings_new_with_path (APP_FOLDER_CHILD_SCHEMA, child_path);
+ g_settings_set_string (child, "name", "X-GNOME-Utilities.directory");
+ g_settings_set_boolean (child, "translate", TRUE);
+ g_settings_set_strv (child, "categories", utilities_categories);
+ g_settings_set_strv (child, "apps", utilities_apps);
+
+ g_object_unref (child);
+ g_free (child_path);
+
+ child_path = g_strconcat (path, "folders/YaST/", NULL);
+ child = g_settings_new_with_path (APP_FOLDER_CHILD_SCHEMA, child_path);
+ g_settings_set_string (child, "name", "suse-yast.directory");
+ g_settings_set_boolean (child, "translate", TRUE);
+ g_settings_set_strv (child, "categories", yast_categories);
+
+ g_object_unref (child);
+ g_free (child_path);
+
+ }
+}