Commit 59f873ec authored by Giovanni Campagna's avatar Giovanni Campagna

Add a gnome-shell search-provider for settings panel

Replace the gnome-shell builtin settings search, which relied on removed
menu files, with one that uses the remote search infrastructure and
CcSearchModel, and features the ability to continue searching within the
control center application.

https://bugzilla.gnome.org/show_bug.cgi?id=690577
parent 3911ef1c
ACLOCAL_AMFLAGS = -I m4 -I libgd ${ACLOCAL_FLAGS}
SUBDIRS = libgd po egg-list-box panels shell
SUBDIRS = libgd po egg-list-box panels shell search-provider
if BUILD_DOCUMENTATION
SUBDIRS += man
endif
DIST_SUBDIRS = libgd egg-list-box po panels shell man
DIST_SUBDIRS = libgd egg-list-box po panels shell man search-provider
MAINTAINERCLEANFILES = \
$(srcdir)/INSTALL \
......
......@@ -522,6 +522,7 @@ panels/wacom/calibrator/Makefile
panels/wacom/gnome-wacom-panel.desktop.in
po/Makefile.in
libgd/Makefile
search-provider/Makefile
shell/Makefile
shell/gnome-control-center.desktop.in
man/Makefile
......
dbus_shell_search_provider_built_sources = \
cc-shell-search-provider-generated.c \
cc-shell-search-provider-generated.h
# The upstream for the DBus interface definition is
# at http://git.gnome.org/browse/gnome-shell/plain/data/org.gnome.ShellSearchProvider2.xml
$(dbus_shell_search_provider_built_sources) : Makefile.am $(srcdir)/org.gnome.ShellSearchProvider2.xml
gdbus-codegen \
--interface-prefix org.gnome. \
--c-namespace Cc \
--generate-c-code cc-shell-search-provider-generated \
$(srcdir)/org.gnome.ShellSearchProvider2.xml \
$(NULL)
INCLUDES = \
-DCC_PANEL_LOADER_NO_GTYPES \
-I$(top_srcdir) \
$(SHELL_CFLAGS) \
-I$(top_srcdir)/libgd
libexec_PROGRAMS = gnome-control-center-search-provider
BUILT_SOURCES = $(dbus_shell_search_provider_built_sources)
gnome_control_center_search_provider_SOURCES = \
$(BUILT_SOURCES) \
$(top_srcdir)/shell/cc-panel-loader.c \
$(top_srcdir)/shell/cc-panel-loader.h \
control-center-search-provider.c \
control-center-search-provider.h \
cc-search-provider.c \
cc-search-provider.h
gnome_control_center_search_provider_LDADD = \
$(top_builddir)/shell/libshell.la \
$(SHELL_LIBS)
AM_CPPFLAGS = -DGNOMELOCALEDIR="\"$(datadir)/locale\""
CLEANFILES = $(BUILT_SOURCES) $(service_DATA)
servicedir = $(datadir)/dbus-1/services
service_DATA = $(service_in_files:.service.in=.service)
service_in_files = \
org.gnome.ControlCenter.SearchProvider.service.in
org.gnome.ControlCenter.SearchProvider.service: org.gnome.ControlCenter.SearchProvider.service.in Makefile
$(AM_V_GEN) sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
EXTRA_DIST = $(service_in_files) org.gnome.ShellSearchProvider2.xml
searchproviderdir = $(datadir)/gnome-shell/search-providers
dist_searchprovider_DATA = gnome-control-center-search-provider.ini
-include $(top_srcdir)/git.mk
/*
* 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 <shell/cc-util.h>
#include "control-center-search-provider.h"
#include "cc-search-provider.h"
struct _CcSearchProvider
{
GObject parent;
CcShellSearchProvider2 *skeleton;
};
struct _CcSearchProviderClass
{
GObjectClass parent_class;
};
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_multiple_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 TRUE;
}
return FALSE;
}
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 gboolean
handle_get_initial_result_set (CcShellSearchProvider2 *skeleton,
GDBusMethodInvocation *invocation,
char **terms,
CcSearchProvider *self)
{
GtkTreeModel *model = get_model ();
GtkTreeIter iter;
GPtrArray *results;
gboolean ok;
char **casefolded_terms;
char **results_as_strings;
casefolded_terms = get_casefolded_terms (terms);
results = g_ptr_array_new ();
ok = gtk_tree_model_get_iter_first (model, &iter);
while (ok)
{
if (matches_multiple_terms (model, &iter,
casefolded_terms))
{
g_ptr_array_add (results,
gtk_tree_model_get_string_from_iter (model,
&iter));
}
ok = gtk_tree_model_iter_next (model, &iter);
}
g_ptr_array_add (results, NULL);
results_as_strings = (char**) g_ptr_array_free (results, FALSE);
cc_shell_search_provider2_complete_get_initial_result_set (skeleton,
invocation,
(const char* const*) results_as_strings);
g_strfreev (casefolded_terms);
g_strfreev (results_as_strings);
return TRUE;
}
static gboolean
handle_get_subsearch_result_set (CcShellSearchProvider2 *skeleton,
GDBusMethodInvocation *invocation,
char **previous_results,
char **terms,
CcSearchProvider *self)
{
GtkTreeModel *model = get_model ();
GtkTreeIter iter;
GPtrArray *results;
char **casefolded_terms;
char **results_as_strings;
int i;
casefolded_terms = get_casefolded_terms (terms);
results = g_ptr_array_new ();
for (i = 0; previous_results[i]; i++)
{
if (gtk_tree_model_get_iter_from_string (model, &iter,
previous_results[i]) &&
matches_multiple_terms (model, &iter, casefolded_terms))
{
g_ptr_array_add (results, g_strdup (previous_results[i]));
}
}
g_ptr_array_add (results, NULL);
results_as_strings = (char**) g_ptr_array_free (results, FALSE);
cc_shell_search_provider2_complete_get_subsearch_result_set (skeleton,
invocation,
(const char* const*) results_as_strings);
g_strfreev (casefolded_terms);
g_strfreev (results_as_strings);
return TRUE;
}
static gboolean
handle_get_result_metas (CcShellSearchProvider2 *skeleton,
GDBusMethodInvocation *invocation,
char **results,
CcSearchProvider *self)
{
GtkTreeModel *model = get_model ();
GtkTreeIter iter;
int i;
GVariantBuilder builder;
GAppInfo *app;
const char *id;
char *name, *description, *icon_string;
GIcon *icon;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
for (i = 0; results[i]; i++)
{
if (!gtk_tree_model_get_iter_from_string (model, &iter, results[i]))
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);
icon_string = g_icon_to_string (icon);
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}",
"gicon", g_variant_new_string (icon_string));
g_variant_builder_add (&builder, "{sv}",
"description", g_variant_new_string (description));
g_variant_builder_close (&builder);
g_free (name);
g_free (description);
g_free (icon_string);
g_object_unref (app);
g_object_unref (icon);
}
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;
GAppInfo *app;
GError *error;
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));
error = NULL;
if (!g_app_info_launch (app, NULL, G_APP_LAUNCH_CONTEXT (launch_context), &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (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;
char *joined_terms, *command_line;
GAppInfo *app;
GError *error;
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);
error = NULL;
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);
g_error_free (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);
g_error_free (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_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);
}
/*
* 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
*
*/
#ifndef _CC_SEARCH_PROVIDER_H
#define _CC_SEARCH_PROVIDER_H
#include <glib-object.h>
#include <gio/gio.h>
#include "cc-shell-search-provider-generated.h"
#include <shell/cc-shell-model.h>
G_BEGIN_DECLS
#define CC_TYPE_SEARCH_PROVIDER cc_search_provider_get_type()
#define CC_SEARCH_PROVIDER(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
CC_TYPE_SEARCH_PROVIDER, CcSearchProvider))
#define CC_SEARCH_PROVIDER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
CC_TYPE_SEARCH_PROVIDER, CcSearchProviderClass))
#define CC_IS_SEARCH_PROVIDER(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
CC_TYPE_SEARCH_PROVIDER))
#define CC_IS_SEARCH_PROVIDER_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
CC_TYPE_SEARCH_PROVIDER))
#define CC_SEARCH_PROVIDER_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
CC_TYPE_SEARCH_PROVIDER, CcSearchProviderClass))
typedef struct _CcSearchProvider CcSearchProvider;
typedef struct _CcSearchProviderClass CcSearchProviderClass;
GType cc_search_provider_get_type (void) G_GNUC_CONST;
CcSearchProvider *cc_search_provider_new (void);
gboolean cc_search_provider_dbus_register (CcSearchProvider *provider,
GDBusConnection *connection,
const char *object_path,
GError **error);
void cc_search_provider_dbus_unregister (CcSearchProvider *provider,
GDBusConnection *connection,
const char *object_path);
G_END_DECLS
#endif /* _CC_SEARCH_PROVIDER_H */
/*
* 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 <stdlib.h>
#include <gio/gio.h>
#include <shell/cc-panel-loader.h>
#include <shell/cc-shell-model.h>
#include "cc-search-provider.h"
#include "control-center-search-provider.h"
G_DEFINE_TYPE (CcSearchProviderApp, cc_search_provider_app, GTK_TYPE_APPLICATION);
#define INACTIVITY_TIMEOUT 60 * 1000 /* One minute, in milliseconds */
static gboolean
cc_search_provider_app_dbus_register (GApplication *application,
GDBusConnection *connection,
const gchar *object_path,
GError **error)
{
CcSearchProviderApp *self;
if (!G_APPLICATION_CLASS (cc_search_provider_app_parent_class)->dbus_register (application,
connection,
object_path,
error))
return FALSE;
self = CC_SEARCH_PROVIDER_APP (application);
return cc_search_provider_dbus_register (self->search_provider, connection,
object_path, error);
}
static void
cc_search_provider_app_dbus_unregister (GApplication *application,
GDBusConnection *connection,
const gchar *object_path)
{
CcSearchProviderApp *self;
self = CC_SEARCH_PROVIDER_APP (application);
if (self->search_provider)
cc_search_provider_dbus_unregister (self->search_provider, connection, object_path);
G_APPLICATION_CLASS (cc_search_provider_app_parent_class)->dbus_unregister (application,
connection,
object_path);
}
static void
cc_search_provider_app_dispose (GObject *object)
{
CcSearchProviderApp *self;
self = CC_SEARCH_PROVIDER_APP (object);
g_clear_object (&self->model);
g_clear_object (&self->search_provider);
G_OBJECT_CLASS (cc_search_provider_app_parent_class)->dispose (object);
}
static void
cc_search_provider_app_init (CcSearchProviderApp *self)
{
self->model = cc_shell_model_new ();
cc_panel_loader_fill_model (self->model);
self->search_provider = cc_search_provider_new ();
g_application_set_inactivity_timeout (G_APPLICATION (self),
INACTIVITY_TIMEOUT);
/* HACK: get the inactivity timeout started */
g_application_hold (G_APPLICATION (self));
g_application_release (G_APPLICATION (self));
}
static void
cc_search_provider_app_class_init (CcSearchProviderAppClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GApplicationClass *app_class = G_APPLICATION_CLASS (klass);
object_class->dispose = cc_search_provider_app_dispose;
app_class->dbus_register = cc_search_provider_app_dbus_register;
app_class->dbus_unregister = cc_search_provider_app_dbus_unregister;
}
CcShellModel *
cc_search_provider_app_get_model (CcSearchProviderApp *application)
{
return application->model;
}
CcSearchProviderApp *
cc_search_provider_app_get ()
{
static CcSearchProviderApp *singleton;
if (singleton)
return singleton;
singleton = g_object_new (CC_TYPE_SEARCH_PROVIDER_APP,
"application-id", "org.gnome.ControlCenter.SearchProvider",
"flags", G_APPLICATION_IS_SERVICE,
NULL);
return singleton;
}
int main (int argc, char **argv)
{
GApplication *app;
app = G_APPLICATION (cc_search_provider_app_get ());
return g_application_run (app, argc, argv);
}
/*
* 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
*
*/
#ifndef _CONTROL_CENTER_SEARCH_PROVIDER_H
#define _CONTROL_CENTER_SEARCH_PROVIDER_H
#include <gtk/gtk.h>
#include <shell/cc-shell-model.h>
#include "cc-search-provider.h"
typedef struct {
GtkApplication parent;
CcShellModel *model;
CcSearchProvider *search_provider;
} CcSearchProviderApp;
typedef struct {
GtkApplicationClass parent_class;
} CcSearchProviderAppClass;
#define CC_TYPE_SEARCH_PROVIDER_APP cc_search_provider_app_get_type ()
#define CC_SEARCH_PROVIDER_APP(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
GNOME_TYPE_CONTROL_CENTER_APP, CcSearchProviderApp))
GType cc_search_provider_app_get_type (void) G_GNUC_CONST;
CcSearchProviderApp *cc_search_provider_app_get (void);
CcShellModel *cc_search_provider_app_get_model (CcSearchProviderApp *application);
#endif
[Shell Search Provider]
DesktopId=gnome-control-center.desktop
BusName=org.gnome.ControlCenter.SearchProvider
ObjectPath=/org/gnome/ControlCenter/SearchProvider
Version=2
[D-BUS Service]
Name=org.gnome.ControlCenter.SearchProvider
Exec=@libexecdir@/gnome-control-center-search-provider
\ No newline at end of file
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
org.gnome.Shell.SearchProvider2:
@short_description: Search provider interface