summaryrefslogtreecommitdiff
path: root/src/services
diff options
context:
space:
mode:
Diffstat (limited to 'src/services')
-rw-r--r--src/services/application-service.c211
-rw-r--r--src/services/application-service.h4
-rw-r--r--src/services/meson.build1
-rw-r--r--src/services/quick-answer-service.c317
-rw-r--r--src/services/quick-answer-service.h21
5 files changed, 451 insertions, 103 deletions
diff --git a/src/services/application-service.c b/src/services/application-service.c
index 3885719..42835ac 100644
--- a/src/services/application-service.c
+++ b/src/services/application-service.c
@@ -1,4 +1,5 @@
#include "services/application-service.h"
+#include "launcher-item.h"
#include "utils.h"
#include <gtk/gtk.h>
#include <gio/gdesktopappinfo.h>
@@ -21,29 +22,38 @@ struct _FoobarApplicationItem
enum
{
APP_PROP_ID = 1,
- APP_PROP_NAME,
- APP_PROP_DESCRIPTION,
APP_PROP_EXECUTABLE,
APP_PROP_CATEGORIES,
- APP_PROP_ICON,
APP_PROP_FREQUENCY,
+ APP_PROP_TITLE,
+ APP_PROP_DESCRIPTION,
+ APP_PROP_ICON,
N_APP_PROPS,
};
static GParamSpec* app_props[N_APP_PROPS] = { 0 };
-static void foobar_application_item_class_init ( FoobarApplicationItemClass* klass );
-static void foobar_application_item_init ( FoobarApplicationItem* self );
-static void foobar_application_item_get_property( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_application_item_finalize ( GObject* object );
-static FoobarApplicationItem* foobar_application_item_new ( FoobarApplicationService* service );
-static void foobar_application_item_set_info ( FoobarApplicationItem* self,
- GAppInfo* value );
+static void foobar_application_item_class_init ( FoobarApplicationItemClass* klass );
+static void foobar_application_item_launcher_item_interface_init( FoobarLauncherItemInterface* iface );
+static void foobar_application_item_init ( FoobarApplicationItem* self );
+static void foobar_application_item_get_property ( GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec );
+static void foobar_application_item_finalize ( GObject* object );
+static FoobarApplicationItem* foobar_application_item_new ( FoobarApplicationService* service );
+static gchar const* foobar_application_item_get_title ( FoobarLauncherItem* self );
+static gchar const* foobar_application_item_get_description ( FoobarLauncherItem* self );
+static GIcon* foobar_application_item_get_icon ( FoobarLauncherItem* self );
+static void foobar_application_item_activate ( FoobarLauncherItem* self );
+static void foobar_application_item_set_info ( FoobarApplicationItem* self,
+ GAppInfo* value );
-G_DEFINE_FINAL_TYPE( FoobarApplicationItem, foobar_application_item, G_TYPE_OBJECT )
+G_DEFINE_FINAL_TYPE_WITH_CODE(
+ FoobarApplicationItem,
+ foobar_application_item,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE( FOOBAR_TYPE_LAUNCHER_ITEM, foobar_application_item_launcher_item_interface_init ) )
//
// FoobarApplicationService:
@@ -124,24 +134,13 @@ void foobar_application_item_class_init( FoobarApplicationItemClass* klass )
object_klass->get_property = foobar_application_item_get_property;
object_klass->finalize = foobar_application_item_finalize;
+ gpointer launcher_item_iface = g_type_default_interface_peek( FOOBAR_TYPE_LAUNCHER_ITEM );
app_props[APP_PROP_ID] = g_param_spec_string(
"id",
"ID",
"Application identifier string.",
NULL,
G_PARAM_READABLE );
- app_props[APP_PROP_NAME] = g_param_spec_string(
- "name",
- "Name",
- "Displayed name for the application.",
- NULL,
- G_PARAM_READABLE );
- app_props[APP_PROP_DESCRIPTION] = g_param_spec_string(
- "description",
- "Description",
- "Brief description of the application.",
- NULL,
- G_PARAM_READABLE );
app_props[APP_PROP_EXECUTABLE] = g_param_spec_string(
"executable",
"Executable",
@@ -154,12 +153,6 @@ void foobar_application_item_class_init( FoobarApplicationItemClass* klass )
"Semicolon-separated list of application categories.",
NULL,
G_PARAM_READABLE );
- app_props[APP_PROP_ICON] = g_param_spec_object(
- "icon",
- "Icon",
- "Icon to show for the application.",
- G_TYPE_ICON,
- G_PARAM_READABLE );
app_props[APP_PROP_FREQUENCY] = g_param_spec_int64(
"frequency",
"Frequency",
@@ -168,10 +161,30 @@ void foobar_application_item_class_init( FoobarApplicationItemClass* klass )
INT64_MAX,
0,
G_PARAM_READABLE );
+ app_props[APP_PROP_TITLE] = g_param_spec_override(
+ "title",
+ g_object_interface_find_property( launcher_item_iface, "title" ) );
+ app_props[APP_PROP_DESCRIPTION] = g_param_spec_override(
+ "description",
+ g_object_interface_find_property( launcher_item_iface, "description" ) );
+ app_props[APP_PROP_ICON] = g_param_spec_override(
+ "icon",
+ g_object_interface_find_property( launcher_item_iface, "icon" ) );
g_object_class_install_properties( object_klass, N_APP_PROPS, app_props );
}
//
+// Static initialization of the FoobarLauncherItem interface.
+//
+void foobar_application_item_launcher_item_interface_init( FoobarLauncherItemInterface* iface )
+{
+ iface->get_title = foobar_application_item_get_title;
+ iface->get_description = foobar_application_item_get_description;
+ iface->get_icon = foobar_application_item_get_icon;
+ iface->activate = foobar_application_item_activate;
+}
+
+//
// Instance initialization for application items.
//
void foobar_application_item_init( FoobarApplicationItem* self )
@@ -195,24 +208,24 @@ void foobar_application_item_get_property(
case APP_PROP_ID:
g_value_set_string( value, foobar_application_item_get_id( self ) );
break;
- case APP_PROP_NAME:
- g_value_set_string( value, foobar_application_item_get_name( self ) );
- break;
- case APP_PROP_DESCRIPTION:
- g_value_set_string( value, foobar_application_item_get_description( self ) );
- break;
case APP_PROP_EXECUTABLE:
g_value_set_string( value, foobar_application_item_get_executable( self ) );
break;
case APP_PROP_CATEGORIES:
g_value_set_string( value, foobar_application_item_get_categories( self ) );
break;
- case APP_PROP_ICON:
- g_value_set_object( value, foobar_application_item_get_icon( self ) );
- break;
case APP_PROP_FREQUENCY:
g_value_set_int64( value, foobar_application_item_get_frequency( self ) );
break;
+ case APP_PROP_TITLE:
+ g_value_set_string( value, foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM ( self ) ) );
+ break;
+ case APP_PROP_DESCRIPTION:
+ g_value_set_string( value, foobar_launcher_item_get_description( FOOBAR_LAUNCHER_ITEM( self ) ) );
+ break;
+ case APP_PROP_ICON:
+ g_value_set_object( value, foobar_launcher_item_get_icon( FOOBAR_LAUNCHER_ITEM( self ) ) );
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
break;
@@ -251,62 +264,91 @@ gchar const* foobar_application_item_get_id( FoobarApplicationItem* self )
}
//
-// Get the application's human-readable name.
+// Get the application's executable name.
//
-gchar const* foobar_application_item_get_name( FoobarApplicationItem* self )
+gchar const* foobar_application_item_get_executable( FoobarApplicationItem* self )
{
g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), NULL );
- return self->info ? g_app_info_get_display_name( self->info ) : NULL;
+ return self->info ? g_app_info_get_executable( self->info ) : NULL;
}
//
-// Get an optional short description for the application.
+// Get the semicolon-separated list of categories for this application.
//
-gchar const* foobar_application_item_get_description( FoobarApplicationItem* self )
+gchar const* foobar_application_item_get_categories( FoobarApplicationItem* self )
{
g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), NULL );
- return self->info ? g_app_info_get_description( self->info ) : NULL;
+ return G_IS_DESKTOP_APP_INFO( self->info )
+ ? g_desktop_app_info_get_categories( G_DESKTOP_APP_INFO( self->info ) )
+ : NULL;
}
//
-// Get the application's executable name.
+// Get the number of times this application was opened using the launcher.
//
-gchar const* foobar_application_item_get_executable( FoobarApplicationItem* self )
+gint64 foobar_application_item_get_frequency( FoobarApplicationItem* self )
{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), NULL );
- return self->info ? g_app_info_get_executable( self->info ) : NULL;
+ g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), 0 );
+
+ if ( !self->service ) { return 0; }
+ gpointer value = g_hash_table_lookup( self->service->frequencies, foobar_application_item_get_id( self ) );
+ return GPOINTER_TO_SIZE( value );
}
//
-// Get the semicolon-separated list of categories for this application.
+// Get the application's human-readable name.
//
-gchar const* foobar_application_item_get_categories( FoobarApplicationItem* self )
+gchar const* foobar_application_item_get_title( FoobarLauncherItem* item )
{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), NULL );
- return G_IS_DESKTOP_APP_INFO( self->info )
- ? g_desktop_app_info_get_categories( G_DESKTOP_APP_INFO( self->info ) )
- : NULL;
+ FoobarApplicationItem* self = ( FoobarApplicationItem* )item;
+ return self->info ? g_app_info_get_display_name( self->info ) : NULL;
+}
+
+//
+// Get an optional short description for the application.
+//
+gchar const* foobar_application_item_get_description( FoobarLauncherItem* item )
+{
+ FoobarApplicationItem* self = ( FoobarApplicationItem* )item;
+ return self->info ? g_app_info_get_description( self->info ) : NULL;
}
//
// Get an icon representing the application.
//
-GIcon* foobar_application_item_get_icon( FoobarApplicationItem* self )
+GIcon* foobar_application_item_get_icon( FoobarLauncherItem* item )
{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), NULL );
+ FoobarApplicationItem* self = ( FoobarApplicationItem* )item;
return self->info ? g_app_info_get_icon( self->info ) : NULL;
}
//
-// Get the number of times this application was opened using the launcher.
+// Launch the application, increasing its frequency by one.
//
-gint64 foobar_application_item_get_frequency( FoobarApplicationItem* self )
+void foobar_application_item_activate( FoobarLauncherItem* item )
{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), 0 );
+ FoobarApplicationItem* self = ( FoobarApplicationItem* )item;
- if ( !self->service ) { return 0; }
- gpointer value = g_hash_table_lookup( self->service->frequencies, foobar_application_item_get_id( self ) );
- return GPOINTER_TO_SIZE( value );
+ if ( !self->info ) { return; }
+
+ g_autoptr( GError ) error = NULL;
+ if ( !g_app_info_launch( self->info, NULL, NULL, &error ) )
+ {
+ g_warning( "Unable to launch application: %s", error->message );
+ }
+
+ gchar const* id = foobar_application_item_get_id( self );
+ if ( !self->service || !id ) { return; }
+
+ gpointer value = g_hash_table_lookup( self->service->frequencies, id );
+ value = GSIZE_TO_POINTER( GPOINTER_TO_SIZE( value ) + 1 );
+ g_mutex_lock( &self->service->frequencies_mutex );
+ g_hash_table_insert( self->service->frequencies, g_strdup( id ), value );
+ g_mutex_unlock( &self->service->frequencies_mutex );
+
+ g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_FREQUENCY] );
+ gtk_sorter_changed( gtk_sort_list_model_get_sorter( self->service->sorted_items ), GTK_SORTER_CHANGE_DIFFERENT );
+ foobar_application_service_write_cache( self->service );
}
//
@@ -325,7 +367,7 @@ void foobar_application_item_set_info(
if ( value ) { self->info = g_object_ref( value ); }
g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_ID] );
- g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_NAME] );
+ g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_TITLE] );
g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_DESCRIPTION] );
g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_EXECUTABLE] );
g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_ICON] );
@@ -346,8 +388,8 @@ gboolean foobar_application_item_match(
for ( gchar const* const* it = terms; *it; ++it )
{
gchar const* term = *it;
- gchar const* name = foobar_application_item_get_name( self );
- gchar const* description = foobar_application_item_get_description( self );
+ gchar const* name = foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM( self ) );
+ gchar const* description = foobar_launcher_item_get_description( FOOBAR_LAUNCHER_ITEM( self ) );
gchar const* executable = foobar_application_item_get_executable( self );
gchar const* categories = foobar_application_item_get_categories( self );
gchar const* id = foobar_application_item_get_id( self );
@@ -363,35 +405,6 @@ gboolean foobar_application_item_match(
return TRUE;
}
-//
-// Launch the application, increasing its frequency by one.
-//
-void foobar_application_item_launch( FoobarApplicationItem* self )
-{
- g_return_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ) );
-
- if ( !self->info ) { return; }
-
- g_autoptr( GError ) error = NULL;
- if ( !g_app_info_launch( self->info, NULL, NULL, &error ) )
- {
- g_warning( "Unable to launch application: %s", error->message );
- }
-
- gchar const* id = foobar_application_item_get_id( self );
- if ( !self->service || !id ) { return; }
-
- gpointer value = g_hash_table_lookup( self->service->frequencies, id );
- value = GSIZE_TO_POINTER( GPOINTER_TO_SIZE( value ) + 1 );
- g_mutex_lock( &self->service->frequencies_mutex );
- g_hash_table_insert( self->service->frequencies, g_strdup( id ), value );
- g_mutex_unlock( &self->service->frequencies_mutex );
-
- g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_FREQUENCY] );
- gtk_sorter_changed( gtk_sort_list_model_get_sorter( self->service->sorted_items ), GTK_SORTER_CHANGE_DIFFERENT );
- foobar_application_service_write_cache( self->service );
-}
-
// ---------------------------------------------------------------------------------------------------------------------
// Service Implementation
// ---------------------------------------------------------------------------------------------------------------------
@@ -722,8 +735,8 @@ gint foobar_application_service_sort_func(
if ( freq_a > freq_b ) { return -1; }
if ( freq_a < freq_b ) { return 1; }
- gchar const* name_a = foobar_application_item_get_name( app_a );
- gchar const* name_b = foobar_application_item_get_name( app_b );
+ gchar const* name_a = foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM( app_a ) );
+ gchar const* name_b = foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM( app_b ) );
gint name_res = g_strcmp0( name_a, name_b );
if ( name_res ) { return name_res; }
diff --git a/src/services/application-service.h b/src/services/application-service.h
index 3c2622a..dd043fc 100644
--- a/src/services/application-service.h
+++ b/src/services/application-service.h
@@ -11,15 +11,11 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE( FoobarApplicationItem, foobar_application_item, FOOBAR, APPLICATION_ITEM, GObject )
gchar const* foobar_application_item_get_id ( FoobarApplicationItem* self );
-gchar const* foobar_application_item_get_name ( FoobarApplicationItem* self );
-gchar const* foobar_application_item_get_description( FoobarApplicationItem* self );
gchar const* foobar_application_item_get_executable ( FoobarApplicationItem* self );
gchar const* foobar_application_item_get_categories ( FoobarApplicationItem* self );
-GIcon* foobar_application_item_get_icon ( FoobarApplicationItem* self );
gint64 foobar_application_item_get_frequency ( FoobarApplicationItem* self );
gboolean foobar_application_item_match ( FoobarApplicationItem* self,
gchar const* const* terms );
-void foobar_application_item_launch ( FoobarApplicationItem* self );
G_DECLARE_FINAL_TYPE( FoobarApplicationService, foobar_application_service, FOOBAR, APPLICATION_SERVICE, GObject )
diff --git a/src/services/meson.build b/src/services/meson.build
index 990102b..e4f151d 100644
--- a/src/services/meson.build
+++ b/src/services/meson.build
@@ -8,5 +8,6 @@ foobar_sources += files(
'network-service.c',
'bluetooth-service.c',
'application-service.c',
+ 'quick-answer-service.c',
'configuration-service.c',
)
diff --git a/src/services/quick-answer-service.c b/src/services/quick-answer-service.c
new file mode 100644
index 0000000..b564a16
--- /dev/null
+++ b/src/services/quick-answer-service.c
@@ -0,0 +1,317 @@
+#include "services/quick-answer-service.h"
+#include "launcher-item.h"
+#include <gdk/gdk.h>
+
+struct _FoobarQuickAnswer
+{
+ GObject parent_instance;
+ gchar* title;
+ gchar* value;
+ GIcon* icon;
+};
+
+enum
+{
+ ANSWER_PROP_VALUE = 1,
+ ANSWER_PROP_TITLE,
+ ANSWER_PROP_DESCRIPTION,
+ ANSWER_PROP_ICON,
+ N_ANSWER_PROPS,
+};
+
+static GParamSpec* answer_props[N_ANSWER_PROPS] = { 0 };
+
+static void foobar_quick_answer_class_init ( FoobarQuickAnswerClass* klass );
+static void foobar_quick_answer_launcher_item_interface_init( FoobarLauncherItemInterface* iface );
+static void foobar_quick_answer_init ( FoobarQuickAnswer* self );
+static void foobar_quick_answer_get_property ( GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec );
+static void foobar_quick_answer_finalize ( GObject* object );
+static FoobarQuickAnswer* foobar_quick_answer_new ( void );
+static gchar const* foobar_quick_answer_get_title ( FoobarLauncherItem* self );
+static gchar const* foobar_quick_answer_get_description ( FoobarLauncherItem* self );
+static GIcon* foobar_quick_answer_get_icon ( FoobarLauncherItem* self );
+static void foobar_quick_answer_activate ( FoobarLauncherItem* self );
+static void foobar_quick_answer_set_title ( FoobarQuickAnswer* self,
+ gchar const* value );
+static void foobar_quick_answer_set_value ( FoobarQuickAnswer* self,
+ gchar const* value );
+static void foobar_quick_answer_set_icon ( FoobarQuickAnswer* self,
+ GIcon* value );
+
+G_DEFINE_FINAL_TYPE_WITH_CODE(
+ FoobarQuickAnswer,
+ foobar_quick_answer,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE( FOOBAR_TYPE_LAUNCHER_ITEM, foobar_quick_answer_launcher_item_interface_init ) )
+
+struct _FoobarQuickAnswerService
+{
+ GObject parent_instance;
+};
+
+static void foobar_quick_answer_service_class_init( FoobarQuickAnswerServiceClass* klass );
+static void foobar_quick_answer_service_init ( FoobarQuickAnswerService* self );
+
+G_DEFINE_FINAL_TYPE( FoobarQuickAnswerService, foobar_quick_answer_service, G_TYPE_OBJECT )
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Quick Answers
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Static initialization for quick answers.
+//
+void foobar_quick_answer_class_init( FoobarQuickAnswerClass* klass )
+{
+ GObjectClass* object_klass = G_OBJECT_CLASS( klass );
+ object_klass->get_property = foobar_quick_answer_get_property;
+ object_klass->finalize = foobar_quick_answer_finalize;
+
+ gpointer launcher_item_iface = g_type_default_interface_peek( FOOBAR_TYPE_LAUNCHER_ITEM );
+ answer_props[ANSWER_PROP_VALUE] = g_param_spec_string(
+ "value",
+ "Value",
+ "The raw value of the quick answer.",
+ NULL,
+ G_PARAM_READABLE );
+ answer_props[ANSWER_PROP_TITLE] = g_param_spec_override(
+ "title",
+ g_object_interface_find_property( launcher_item_iface, "title" ) );
+ answer_props[ANSWER_PROP_DESCRIPTION] = g_param_spec_override(
+ "description",
+ g_object_interface_find_property( launcher_item_iface, "description" ) );
+ answer_props[ANSWER_PROP_ICON] = g_param_spec_override(
+ "icon",
+ g_object_interface_find_property( launcher_item_iface, "icon" ) );
+ g_object_class_install_properties( object_klass, N_ANSWER_PROPS, answer_props );
+}
+
+//
+// Static initialization of the FoobarLauncherItem interface.
+//
+void foobar_quick_answer_launcher_item_interface_init( FoobarLauncherItemInterface* iface )
+{
+ iface->get_title = foobar_quick_answer_get_title;
+ iface->get_description = foobar_quick_answer_get_description;
+ iface->get_icon = foobar_quick_answer_get_icon;
+ iface->activate = foobar_quick_answer_activate;
+}
+
+//
+// Instance initialization for quick answers.
+//
+void foobar_quick_answer_init( FoobarQuickAnswer* self )
+{
+ (void)self;
+}
+
+//
+// Property getter implementation, mapping a property id to a method.
+//
+void foobar_quick_answer_get_property(
+ GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec )
+{
+ FoobarQuickAnswer* self = (FoobarQuickAnswer*)object;
+
+ switch ( prop_id )
+ {
+ case ANSWER_PROP_VALUE:
+ g_value_set_string( value, foobar_quick_answer_get_value( self ) );
+ break;
+ case ANSWER_PROP_TITLE:
+ g_value_set_string( value, foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM ( self ) ) );
+ break;
+ case ANSWER_PROP_DESCRIPTION:
+ g_value_set_string( value, foobar_launcher_item_get_description( FOOBAR_LAUNCHER_ITEM( self ) ) );
+ break;
+ case ANSWER_PROP_ICON:
+ g_value_set_object( value, foobar_launcher_item_get_icon( FOOBAR_LAUNCHER_ITEM( self ) ) );
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
+ break;
+ }
+}
+
+//
+// Instance cleanup for quick answers.
+//
+void foobar_quick_answer_finalize( GObject* object )
+{
+ FoobarQuickAnswer* self = (FoobarQuickAnswer*)object;
+
+ g_clear_pointer( &self->title, g_free );
+ g_clear_pointer( &self->value, g_free );
+ g_clear_object( &self->icon );
+
+ G_OBJECT_CLASS( foobar_quick_answer_parent_class )->finalize( object );
+}
+
+//
+// Create a new, default-initialized quick answer.
+//
+FoobarQuickAnswer* foobar_quick_answer_new( void )
+{
+ return g_object_new( FOOBAR_TYPE_QUICK_ANSWER, NULL );
+}
+
+//
+// Get the raw value of the quick answer.
+//
+gchar const* foobar_quick_answer_get_value( FoobarQuickAnswer* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_QUICK_ANSWER( self ), NULL );
+ return self->value;
+}
+
+//
+// Get the title for the quick answer.
+//
+gchar const* foobar_quick_answer_get_title( FoobarLauncherItem* item )
+{
+ FoobarQuickAnswer* self = ( FoobarQuickAnswer* )item;
+ return self->title;
+}
+
+//
+// Get the description shown below the title, always NULL for quick answers.
+//
+gchar const* foobar_quick_answer_get_description( FoobarLauncherItem* item )
+{
+ (void)item;
+ return NULL;
+}
+
+//
+// Get an icon representing the quick answer.
+//
+GIcon* foobar_quick_answer_get_icon( FoobarLauncherItem* item )
+{
+ FoobarQuickAnswer* self = ( FoobarQuickAnswer* )item;
+ return self->icon;
+}
+
+//
+// Copy the quick answer's value into the clipboard.
+//
+void foobar_quick_answer_activate( FoobarLauncherItem* item )
+{
+ FoobarQuickAnswer* self = ( FoobarQuickAnswer* )item;
+ if ( self->value )
+ {
+ GdkClipboard* clipboard = gdk_display_get_clipboard( gdk_display_get_default( ) );
+ gdk_clipboard_set_text( clipboard, self->value );
+ }
+}
+
+//
+// Update the raw value of the quick answer.
+//
+void foobar_quick_answer_set_value(
+ FoobarQuickAnswer* self,
+ gchar const* value )
+{
+ g_return_if_fail( FOOBAR_IS_QUICK_ANSWER( self ) );
+
+ if ( g_strcmp0( self->value, value ) )
+ {
+ g_clear_pointer( &self->value, g_free );
+ self->value = g_strdup( value );
+ g_object_notify_by_pspec( G_OBJECT( self ), answer_props[ANSWER_PROP_VALUE] );
+ }
+}
+
+//
+// Update the title for the quick answer.
+//
+void foobar_quick_answer_set_title(
+ FoobarQuickAnswer* self,
+ gchar const* value )
+{
+ g_return_if_fail( FOOBAR_IS_QUICK_ANSWER( self ) );
+
+ if ( g_strcmp0( self->title, value ) )
+ {
+ g_clear_pointer( &self->title, g_free );
+ self->title = g_strdup( value );
+ g_object_notify_by_pspec( G_OBJECT( self ), answer_props[ANSWER_PROP_TITLE] );
+ }
+}
+
+//
+// Update the icon representing the quick answer.
+//
+void foobar_quick_answer_set_icon(
+ FoobarQuickAnswer* self,
+ GIcon* value )
+{
+ g_return_if_fail( FOOBAR_IS_QUICK_ANSWER( self ) );
+
+ if ( self->icon != value )
+ {
+ g_clear_object( &self->icon );
+ self->icon = value ? g_object_ref( value ) : NULL;
+ g_object_notify_by_pspec( G_OBJECT( self ), answer_props[ANSWER_PROP_ICON] );
+ }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Service Implementation
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Static initialization for the quick answer service.
+//
+void foobar_quick_answer_service_class_init( FoobarQuickAnswerServiceClass* klass )
+{
+ (void)klass;
+}
+
+//
+// Instance initialization for the quick answer service.
+//
+void foobar_quick_answer_service_init( FoobarQuickAnswerService* self )
+{
+ (void)self;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+// Public API
+// ---------------------------------------------------------------------------------------------------------------------
+
+//
+// Create a new quick answer service instance.
+//
+FoobarQuickAnswerService* foobar_quick_answer_service_new( void )
+{
+ return g_object_new( FOOBAR_TYPE_QUICK_ANSWER_SERVICE, NULL );
+}
+
+//
+// Query the service for a quick answer.
+//
+FoobarQuickAnswer* foobar_quick_answer_service_query(
+ FoobarQuickAnswerService* self,
+ gchar const* query )
+{
+ (void)self;
+
+ if ( !g_strcmp0(query, "2+2") )
+ {
+ g_autoptr(GIcon) icon = g_themed_icon_new("fluent-calculator-symbolic");
+
+ FoobarQuickAnswer* answer = foobar_quick_answer_new();
+ foobar_quick_answer_set_value(answer, "4");
+ foobar_quick_answer_set_title(answer, "= 4");
+ foobar_quick_answer_set_icon(answer, icon);
+ return answer;
+ }
+
+ return NULL;
+}
diff --git a/src/services/quick-answer-service.h b/src/services/quick-answer-service.h
new file mode 100644
index 0000000..02a8e10
--- /dev/null
+++ b/src/services/quick-answer-service.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define FOOBAR_TYPE_QUICK_ANSWER foobar_quick_answer_get_type( )
+#define FOOBAR_TYPE_QUICK_ANSWER_SERVICE foobar_quick_answer_service_get_type( )
+
+G_DECLARE_FINAL_TYPE( FoobarQuickAnswer, foobar_quick_answer, FOOBAR, QUICK_ANSWER, GObject )
+
+gchar const* foobar_quick_answer_get_value( FoobarQuickAnswer* self );
+
+G_DECLARE_FINAL_TYPE( FoobarQuickAnswerService, foobar_quick_answer_service, FOOBAR, QUICK_ANSWER_SERVICE, GObject )
+
+FoobarQuickAnswerService* foobar_quick_answer_service_new ( void );
+FoobarQuickAnswer* foobar_quick_answer_service_query ( FoobarQuickAnswerService* self,
+ gchar const* query );
+
+G_END_DECLS