diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:45:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:45:20 +0000 |
commit | ae1c76ff830d146d41e88d6fba724c0a54bce868 (patch) | |
tree | 3c354bec95af07be35fc71a4b738268496f1a1c4 /search-provider/cc-search-provider.c | |
parent | Initial commit. (diff) | |
download | gnome-control-center-upstream/1%43.6.tar.xz gnome-control-center-upstream/1%43.6.zip |
Adding upstream version 1:43.6.upstream/1%43.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'search-provider/cc-search-provider.c')
-rw-r--r-- | search-provider/cc-search-provider.c | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/search-provider/cc-search-provider.c b/search-provider/cc-search-provider.c new file mode 100644 index 0000000..8a8981a --- /dev/null +++ b/search-provider/cc-search-provider.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2012 Giovanni Campagna <scampa.giovanni@gmail.com> + * + * The Control Center 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. + * + * The Control Center 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 the Control Center; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <config.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gio/gdesktopappinfo.h> +#include <gtk/gtk.h> +#include <string.h> + +#include <shell/cc-panel.h> +#include <shell/cc-shell-model.h> +#include <shell/cc-panel-loader.h> + +#include "cc-util.h" + +#include "control-center-search-provider.h" +#include "cc-search-provider.h" + +struct _CcSearchProvider +{ + GObject parent; + + CcShellSearchProvider2 *skeleton; + + GHashTable *iter_table; /* COL_ID -> GtkTreeIter */ +}; + +typedef enum { + MATCH_NONE, + MATCH_PREFIX, + MATCH_SUBSTRING +} PanelSearchMatch; + +G_DEFINE_TYPE (CcSearchProvider, cc_search_provider, G_TYPE_OBJECT) + +static char ** +get_casefolded_terms (char **terms) +{ + char **casefolded_terms; + int i, n; + + n = g_strv_length ((char**) terms); + casefolded_terms = g_new (char*, n + 1); + + for (i = 0; i < n; i++) + casefolded_terms[i] = cc_util_normalize_casefold_and_unaccent (terms[i]); + casefolded_terms[n] = NULL; + + return casefolded_terms; +} + +static gboolean +matches_all_terms (GtkTreeModel *model, + GtkTreeIter *iter, + char **terms) +{ + int i; + + for (i = 0; terms[i]; i++) + { + if (!cc_shell_model_iter_matches_search (CC_SHELL_MODEL (model), + iter, + terms[i])) + return FALSE; + } + + return TRUE; +} + +static GtkTreeModel * +get_model (void) +{ + CcSearchProviderApp *app; + + app = cc_search_provider_app_get (); + return GTK_TREE_MODEL (cc_search_provider_app_get_model (app)); +} + +static gchar ** +get_results (gchar **terms) +{ + g_auto(GStrv) casefolded_terms = NULL; + GtkTreeModel *model = get_model (); + GtkTreeIter iter; + GPtrArray *results; + gboolean ok; + + casefolded_terms = get_casefolded_terms (terms); + results = g_ptr_array_new (); + + cc_shell_model_set_sort_terms (CC_SHELL_MODEL (model), casefolded_terms); + + ok = gtk_tree_model_get_iter_first (model, &iter); + while (ok) + { + if (matches_all_terms (model, &iter, casefolded_terms)) + { + gchar *id; + gtk_tree_model_get (model, &iter, COL_ID, &id, -1); + g_ptr_array_add (results, id); + } + + ok = gtk_tree_model_iter_next (model, &iter); + } + + g_ptr_array_add (results, NULL); + + return (char**) g_ptr_array_free (results, FALSE); +} + +static gboolean +handle_get_initial_result_set (CcShellSearchProvider2 *skeleton, + GDBusMethodInvocation *invocation, + char **terms, + CcSearchProvider *self) +{ + g_auto(GStrv) results = get_results (terms); + cc_shell_search_provider2_complete_get_initial_result_set (skeleton, + invocation, + (const char* const*) results); + return TRUE; +} + +static gboolean +handle_get_subsearch_result_set (CcShellSearchProvider2 *skeleton, + GDBusMethodInvocation *invocation, + char **previous_results, + char **terms, + CcSearchProvider *self) +{ + /* We ignore the previous results here since the model re-sorts for + * the new terms. This means that we're not really doing a subsearch + * but, on the other hand, the results are consistent with the + * control center's own search. In any case, the number of elements + * in the model is always small enough that we don't need to worry + * about this taking too long. + */ + g_auto(GStrv) results = get_results (terms); + cc_shell_search_provider2_complete_get_subsearch_result_set (skeleton, + invocation, + (const char* const*) results); + return TRUE; +} + +static GtkTreeIter * +get_iter_for_result (CcSearchProvider *self, + const gchar *result) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean ok; + gchar *id; + + /* Caching GtkTreeIters in this way is only OK because the model is + * a GtkListStore which guarantees that while a row exists, the iter + * is persistent. + */ + if (self->iter_table) + goto lookup; + + self->iter_table = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) gtk_tree_iter_free); + + model = get_model (); + ok = gtk_tree_model_get_iter_first (model, &iter); + while (ok) + { + gtk_tree_model_get (model, &iter, COL_ID, &id, -1); + + g_hash_table_replace (self->iter_table, id, gtk_tree_iter_copy (&iter)); + + ok = gtk_tree_model_iter_next (model, &iter); + } + + lookup: + return g_hash_table_lookup (self->iter_table, result); +} + +static gboolean +handle_get_result_metas (CcShellSearchProvider2 *skeleton, + GDBusMethodInvocation *invocation, + char **results, + CcSearchProvider *self) +{ + GtkTreeModel *model = get_model (); + GtkTreeIter *iter; + int i; + GVariantBuilder builder; + const char *id; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); + + for (i = 0; results[i]; i++) + { + g_autofree gchar *description = NULL; + g_autofree gchar *name = NULL; + g_autoptr(GAppInfo) app = NULL; + g_autoptr(GIcon) icon = NULL; + + iter = get_iter_for_result (self, results[i]); + if (!iter) + continue; + + gtk_tree_model_get (model, iter, + COL_APP, &app, + COL_NAME, &name, + COL_GICON, &icon, + COL_DESCRIPTION, &description, + -1); + id = g_app_info_get_id (app); + + g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&builder, "{sv}", + "id", g_variant_new_string (id)); + g_variant_builder_add (&builder, "{sv}", + "name", g_variant_new_string (name)); + g_variant_builder_add (&builder, "{sv}", + "icon", g_icon_serialize (icon)); + g_variant_builder_add (&builder, "{sv}", + "description", g_variant_new_string (description)); + g_variant_builder_close (&builder); + } + + cc_shell_search_provider2_complete_get_result_metas (skeleton, + invocation, + g_variant_builder_end (&builder)); + return TRUE; +} + +static gboolean +handle_activate_result (CcShellSearchProvider2 *skeleton, + GDBusMethodInvocation *invocation, + char *identifier, + char **results, + guint timestamp, + CcSearchProvider *self) +{ + GdkAppLaunchContext *launch_context; + g_autoptr(GError) error = NULL; + GAppInfo *app; + + launch_context = gdk_display_get_app_launch_context (gdk_display_get_default ()); + gdk_app_launch_context_set_timestamp (launch_context, timestamp); + + app = G_APP_INFO (g_desktop_app_info_new (identifier)); + + if (!g_app_info_launch (app, NULL, G_APP_LAUNCH_CONTEXT (launch_context), &error)) + g_dbus_method_invocation_return_gerror (invocation, error); + else + cc_shell_search_provider2_complete_activate_result (skeleton, invocation); + + return TRUE; +} + +static gboolean +handle_launch_search (CcShellSearchProvider2 *skeleton, + GDBusMethodInvocation *invocation, + char **terms, + guint timestamp, + CcSearchProvider *self) +{ + GdkAppLaunchContext *launch_context; + g_autoptr(GError) error = NULL; + char *joined_terms, *command_line; + GAppInfo *app; + + launch_context = gdk_display_get_app_launch_context (gdk_display_get_default ()); + gdk_app_launch_context_set_timestamp (launch_context, timestamp); + + joined_terms = g_strjoinv (" ", terms); + command_line = g_strdup_printf ("gnome-control-center -s '%s'", joined_terms); + app = g_app_info_create_from_commandline (command_line, + "gnome-control-center.desktop", + G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION, + &error); + if (!app) + { + g_dbus_method_invocation_return_gerror (invocation, error); + return TRUE; + } + + if (!g_app_info_launch (app, NULL, G_APP_LAUNCH_CONTEXT (launch_context), &error)) + g_dbus_method_invocation_return_gerror (invocation, error); + else + cc_shell_search_provider2_complete_launch_search (skeleton, invocation); + + return TRUE; +} + +static void +cc_search_provider_init (CcSearchProvider *self) +{ + self->skeleton = cc_shell_search_provider2_skeleton_new (); + + g_signal_connect (self->skeleton, "handle-get-initial-result-set", + G_CALLBACK (handle_get_initial_result_set), self); + g_signal_connect (self->skeleton, "handle-get-subsearch-result-set", + G_CALLBACK (handle_get_subsearch_result_set), self); + g_signal_connect (self->skeleton, "handle-get-result-metas", + G_CALLBACK (handle_get_result_metas), self); + g_signal_connect (self->skeleton, "handle-activate-result", + G_CALLBACK (handle_activate_result), self); + g_signal_connect (self->skeleton, "handle-launch-search", + G_CALLBACK (handle_launch_search), self); +} + +gboolean +cc_search_provider_dbus_register (CcSearchProvider *self, + GDBusConnection *connection, + const gchar *object_path, + GError **error) +{ + GDBusInterfaceSkeleton *skeleton; + + skeleton = G_DBUS_INTERFACE_SKELETON (self->skeleton); + + return g_dbus_interface_skeleton_export (skeleton, connection, object_path, error); +} + +void +cc_search_provider_dbus_unregister (CcSearchProvider *self, + GDBusConnection *connection, + const gchar *object_path) +{ + GDBusInterfaceSkeleton *skeleton; + + skeleton = G_DBUS_INTERFACE_SKELETON (self->skeleton); + + if (g_dbus_interface_skeleton_has_connection (skeleton, connection)) + g_dbus_interface_skeleton_unexport_from_connection (skeleton, connection); +} + +static void +cc_search_provider_dispose (GObject *object) +{ + CcSearchProvider *self; + + self = CC_SEARCH_PROVIDER (object); + + g_clear_object (&self->skeleton); + g_clear_pointer (&self->iter_table, g_hash_table_destroy); + + G_OBJECT_CLASS (cc_search_provider_parent_class)->dispose (object); +} + +static void +cc_search_provider_class_init (CcSearchProviderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = cc_search_provider_dispose; +} + +CcSearchProvider * +cc_search_provider_new (void) +{ + return g_object_new (CC_TYPE_SEARCH_PROVIDER, NULL); +} + |