summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2024-07-01 17:00:08 +0200
committerHannes Schulze <haschu0103@outlook.de>2024-07-01 17:00:08 +0200
commit16483d65744488146e96d149b41f5869a2597a4c (patch)
tree4e1c5a9d00deababc675a94064010d3d23bc5996 /src
parentf82081215e1f29140ab9c70e897ac62e9b456919 (diff)
add quick answers
Diffstat (limited to 'src')
-rw-r--r--src/application.c5
-rw-r--r--src/launcher-item.c73
-rw-r--r--src/launcher-item.h28
-rw-r--r--src/launcher.c83
-rw-r--r--src/launcher.h2
-rw-r--r--src/meson.build1
-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
11 files changed, 616 insertions, 130 deletions
diff --git a/src/application.c b/src/application.c
index a32e1ce..bad62b1 100644
--- a/src/application.c
+++ b/src/application.c
@@ -7,6 +7,7 @@
#include "services/battery-service.h"
#include "services/clock-service.h"
#include "services/brightness-service.h"
+#include "services/quick-answer-service.h"
#include "services/workspace-service.h"
#include "services/notification-service.h"
#include "services/audio-service.h"
@@ -38,6 +39,7 @@ struct _FoobarApplication
FoobarNetworkService* network_service;
FoobarBluetoothService* bluetooth_service;
FoobarApplicationService* application_service;
+ FoobarQuickAnswerService* quick_answer_service;
FoobarConfigurationService* configuration_service;
gulong config_handler_id;
FoobarServer* server_skeleton;
@@ -180,6 +182,7 @@ void foobar_application_activate( GApplication* app )
self->network_service = foobar_network_service_new( );
self->bluetooth_service = foobar_bluetooth_service_new( );
self->application_service = foobar_application_service_new( );
+ self->quick_answer_service = foobar_quick_answer_service_new( );
self->configuration_service = foobar_configuration_service_new( );
// Enforce a uniform style by forcing Adwaita and shipping our own icons.
@@ -231,6 +234,7 @@ void foobar_application_activate( GApplication* app )
self->launcher = foobar_launcher_new(
self->application_service,
+ self->quick_answer_service,
self->configuration_service );
g_object_ref( self->launcher );
@@ -358,6 +362,7 @@ void foobar_application_finalize( GObject* object )
g_clear_object( &self->network_service );
g_clear_object( &self->bluetooth_service );
g_clear_object( &self->application_service );
+ g_clear_object( &self->quick_answer_service );
g_clear_object( &self->configuration_service );
g_clear_object( &self->style_provider );
g_clear_object( &self->server_skeleton );
diff --git a/src/launcher-item.c b/src/launcher-item.c
new file mode 100644
index 0000000..c4de70b
--- /dev/null
+++ b/src/launcher-item.c
@@ -0,0 +1,73 @@
+#include "launcher-item.h"
+
+static void foobar_launcher_item_default_init( FoobarLauncherItemInterface* iface );
+
+G_DEFINE_INTERFACE( FoobarLauncherItem, foobar_launcher_item, G_TYPE_OBJECT )
+
+//
+// Static initialization for the launcher item interface.
+//
+void foobar_launcher_item_default_init( FoobarLauncherItemInterface* iface )
+{
+ g_object_interface_install_property( iface, g_param_spec_string(
+ "title",
+ "Title",
+ "The title text displayed in the launcher.",
+ NULL,
+ G_PARAM_READABLE ) );
+ g_object_interface_install_property( iface, g_param_spec_string(
+ "description",
+ "Description",
+ "Text displayed below the title.",
+ NULL,
+ G_PARAM_READABLE ) );
+ g_object_interface_install_property( iface, g_param_spec_object(
+ "icon",
+ "Icon",
+ "Icon to show for the item.",
+ G_TYPE_ICON,
+ G_PARAM_READABLE ) );
+}
+
+//
+// Get the title text displayed in the launcher.
+//
+gchar const* foobar_launcher_item_get_title( FoobarLauncherItem* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_LAUNCHER_ITEM( self ), NULL );
+ FoobarLauncherItemInterface* iface = FOOBAR_LAUNCHER_ITEM_GET_IFACE( self );
+ g_return_val_if_fail( iface->get_title != NULL, NULL );
+ return iface->get_title( self );
+}
+
+//
+// Get the text displayed below the title.
+//
+gchar const* foobar_launcher_item_get_description( FoobarLauncherItem* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_LAUNCHER_ITEM( self ), NULL );
+ FoobarLauncherItemInterface* iface = FOOBAR_LAUNCHER_ITEM_GET_IFACE( self );
+ g_return_val_if_fail( iface->get_description != NULL, NULL );
+ return iface->get_description( self );
+}
+
+//
+// Get the icon to show for the item.
+//
+GIcon* foobar_launcher_item_get_icon( FoobarLauncherItem* self )
+{
+ g_return_val_if_fail( FOOBAR_IS_LAUNCHER_ITEM( self ), NULL );
+ FoobarLauncherItemInterface* iface = FOOBAR_LAUNCHER_ITEM_GET_IFACE( self );
+ g_return_val_if_fail( iface->get_icon != NULL, NULL );
+ return iface->get_icon( self );
+}
+
+//
+// Activate the item, e.g. by launching the application.
+//
+void foobar_launcher_item_activate( FoobarLauncherItem* self )
+{
+ g_return_if_fail( FOOBAR_IS_LAUNCHER_ITEM( self ) );
+ FoobarLauncherItemInterface* iface = FOOBAR_LAUNCHER_ITEM_GET_IFACE( self );
+ if ( iface->activate ) { iface->activate( self ); }
+}
diff --git a/src/launcher-item.h b/src/launcher-item.h
new file mode 100644
index 0000000..b2d274a
--- /dev/null
+++ b/src/launcher-item.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define FOOBAR_TYPE_LAUNCHER_ITEM foobar_launcher_item_get_type( )
+
+G_DECLARE_INTERFACE( FoobarLauncherItem, foobar_launcher_item, FOOBAR, LAUNCHER_ITEM, GObject )
+
+struct _FoobarLauncherItemInterface
+{
+ GTypeInterface parent_interface;
+
+ gchar const* ( *get_title ) ( FoobarLauncherItem* self );
+ gchar const* ( *get_description )( FoobarLauncherItem* self );
+ GIcon* ( *get_icon ) ( FoobarLauncherItem* self );
+ void ( *activate ) ( FoobarLauncherItem* self );
+};
+
+gchar const* foobar_launcher_item_get_title ( FoobarLauncherItem* self );
+gchar const* foobar_launcher_item_get_description( FoobarLauncherItem* self );
+GIcon* foobar_launcher_item_get_icon ( FoobarLauncherItem* self );
+void foobar_launcher_item_activate ( FoobarLauncherItem* self );
+
+G_END_DECLS
+
diff --git a/src/launcher.c b/src/launcher.c
index da37715..f4b34d8 100644
--- a/src/launcher.c
+++ b/src/launcher.c
@@ -1,4 +1,5 @@
#include "launcher.h"
+#include "launcher-item.h"
#include "widgets/limit-container.h"
#include <gtk4-layer-shell.h>
#include <gdk/gdkkeysyms.h>
@@ -21,8 +22,11 @@ struct _FoobarLauncher
GtkWidget* list_view;
GtkWidget* limit_container;
GtkFilterListModel* filter_model;
+ GListStore* quick_answer_model;
+ GtkFlattenListModel* flatten_model;
GtkSingleSelection* selection_model;
FoobarApplicationService* application_service;
+ FoobarQuickAnswerService* quick_answer_service;
FoobarConfigurationService* configuration_service;
gulong config_handler_id;
};
@@ -119,9 +123,28 @@ void foobar_launcher_init( FoobarLauncher* self )
GtkWidget *separator = gtk_separator_new( GTK_ORIENTATION_HORIZONTAL );
+ // Set up the results list view and an event controller for auto-switching focus to the input.
+
+ GtkListItemFactory* item_factory = gtk_signal_list_item_factory_new( );
+ g_signal_connect( item_factory, "setup", G_CALLBACK( foobar_launcher_handle_item_setup ), NULL );
+
+ GtkCustomFilter* filter = gtk_custom_filter_new( foobar_launcher_filter_func, self, NULL );
+
+ self->filter_model = gtk_filter_list_model_new( NULL, GTK_FILTER( filter ) );
+
+ self->quick_answer_model = g_list_store_new( FOOBAR_TYPE_LAUNCHER_ITEM );
+
+ GListStore* flattened_models = g_list_store_new( G_TYPE_LIST_MODEL );
+ g_list_store_append( flattened_models, self->quick_answer_model );
+ g_list_store_append( flattened_models, self->filter_model );
+
+ self->flatten_model = gtk_flatten_list_model_new( G_LIST_MODEL( flattened_models ) );
+
+ self->selection_model = gtk_single_selection_new( G_LIST_MODEL( g_object_ref( self->flatten_model ) ) );
+
{
- GtkExpression* list_expr = gtk_constant_expression_new( GTK_TYPE_FILTER_LIST_MODEL, self->filter_model );
- GtkExpression* count_expr = gtk_property_expression_new( GTK_TYPE_FILTER_LIST_MODEL, list_expr, "n-items" );
+ GtkExpression* list_expr = gtk_constant_expression_new( GTK_TYPE_FLATTEN_LIST_MODEL, self->flatten_model );
+ GtkExpression* count_expr = gtk_property_expression_new( GTK_TYPE_FLATTEN_LIST_MODEL, list_expr, "n-items" );
GtkExpression* visible_params[] = { count_expr };
GtkExpression* visible_expr = gtk_cclosure_expression_new(
G_TYPE_BOOLEAN,
@@ -134,17 +157,6 @@ void foobar_launcher_init( FoobarLauncher* self )
gtk_expression_bind( visible_expr, separator, "visible", NULL );
}
- // Set up the results list view and an event controller for auto-switching focus to the input.
-
- GtkListItemFactory* item_factory = gtk_signal_list_item_factory_new( );
- g_signal_connect( item_factory, "setup", G_CALLBACK( foobar_launcher_handle_item_setup ), NULL );
-
- GtkCustomFilter* filter = gtk_custom_filter_new( foobar_launcher_filter_func, self, NULL );
-
- self->filter_model = gtk_filter_list_model_new( NULL, GTK_FILTER( filter ) );
-
- self->selection_model = gtk_single_selection_new( G_LIST_MODEL( g_object_ref( self->filter_model ) ) );
-
GtkEventController* list_controller = gtk_event_controller_key_new( );
g_signal_connect( list_controller, "key-pressed", G_CALLBACK( foobar_launcher_handle_list_key ), self );
g_signal_connect( list_controller, "key-released", G_CALLBACK( foobar_launcher_handle_list_key ), self );
@@ -201,8 +213,11 @@ void foobar_launcher_finalize( GObject* object )
g_clear_signal_handler( &self->config_handler_id, self->configuration_service );
g_clear_object( &self->filter_model );
+ g_clear_object( &self->quick_answer_model );
+ g_clear_object( &self->flatten_model );
g_clear_object( &self->selection_model );
g_clear_object( &self->application_service );
+ g_clear_object( &self->quick_answer_service );
g_clear_object( &self->configuration_service );
g_clear_pointer( &self->search_terms, g_strfreev );
@@ -218,13 +233,16 @@ void foobar_launcher_finalize( GObject* object )
//
FoobarLauncher* foobar_launcher_new(
FoobarApplicationService* application_service,
+ FoobarQuickAnswerService* quick_answer_service,
FoobarConfigurationService* configuration_service )
{
g_return_val_if_fail( FOOBAR_IS_APPLICATION_SERVICE( application_service ), NULL );
+ g_return_val_if_fail( FOOBAR_IS_QUICK_ANSWER_SERVICE( quick_answer_service ), NULL );
g_return_val_if_fail( FOOBAR_IS_CONFIGURATION_SERVICE( configuration_service ), NULL );
FoobarLauncher* self = g_object_new( FOOBAR_TYPE_LAUNCHER, NULL );
self->application_service = g_object_ref( application_service );
+ self->quick_answer_service = g_object_ref( quick_answer_service );
self->configuration_service = g_object_ref( configuration_service );
// Set up the result list view's source model.
@@ -299,6 +317,17 @@ void foobar_launcher_handle_search_changed(
{
FoobarLauncher* self = (FoobarLauncher*)userdata;
+ // Update quick answer.
+
+ g_autoptr(FoobarQuickAnswer) answer = foobar_quick_answer_service_query( self->quick_answer_service, gtk_editable_get_text( editable ) );
+ g_list_store_remove_all( self->quick_answer_model );
+ if (answer)
+ {
+ g_list_store_append(self->quick_answer_model, answer);
+ }
+
+ // Update filter.
+
GStrvBuilder* terms_builder = g_strv_builder_new( );
g_autofree gchar* query = g_strdup( gtk_editable_get_text( editable ) );
gchar* query_start = query;
@@ -416,24 +445,24 @@ void foobar_launcher_handle_item_setup(
gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
gtk_widget_add_css_class( icon, "icon" );
- GtkWidget* name = gtk_label_new( NULL );
- gtk_label_set_justify( GTK_LABEL( name ), GTK_JUSTIFY_LEFT );
- gtk_label_set_xalign( GTK_LABEL( name ), 0 );
- gtk_label_set_wrap( GTK_LABEL( name ), FALSE );
- gtk_label_set_ellipsize( GTK_LABEL( name ), PANGO_ELLIPSIZE_END );
- gtk_widget_add_css_class( name, "name" );
+ GtkWidget* title = gtk_label_new( NULL );
+ gtk_label_set_justify( GTK_LABEL( title ), GTK_JUSTIFY_LEFT );
+ gtk_label_set_xalign( GTK_LABEL( title ), 0 );
+ gtk_label_set_wrap( GTK_LABEL( title ), FALSE );
+ gtk_label_set_ellipsize( GTK_LABEL( title ), PANGO_ELLIPSIZE_END );
+ gtk_widget_add_css_class( title, "name" );
GtkWidget* description = gtk_label_new( NULL );
gtk_label_set_justify( GTK_LABEL( description ), GTK_JUSTIFY_LEFT );
gtk_label_set_xalign( GTK_LABEL( description ), 0 );
- gtk_label_set_wrap( GTK_LABEL( name ), FALSE );
+ gtk_label_set_wrap( GTK_LABEL( description ), FALSE );
gtk_label_set_ellipsize( GTK_LABEL( description ), PANGO_ELLIPSIZE_END );
gtk_widget_add_css_class( description, "description" );
GtkWidget* column = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
gtk_widget_set_hexpand( column, TRUE );
gtk_widget_set_valign( column, GTK_ALIGN_CENTER );
- gtk_box_append( GTK_BOX( column ), name );
+ gtk_box_append( GTK_BOX( column ), title );
gtk_box_append( GTK_BOX( column ), description );
GtkWidget* row = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
@@ -447,7 +476,7 @@ void foobar_launcher_handle_item_setup(
{
GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* icon_expr = gtk_property_expression_new( FOOBAR_TYPE_APPLICATION_ITEM, item_expr, "icon" );
+ GtkExpression* icon_expr = gtk_property_expression_new( FOOBAR_TYPE_LAUNCHER_ITEM, item_expr, "icon" );
gtk_expression_bind( gtk_expression_ref( icon_expr ), icon, "gicon", list_item );
GtkExpression* visible_params[] = { icon_expr };
GtkExpression* visible_expr = gtk_cclosure_expression_new(
@@ -463,13 +492,13 @@ void foobar_launcher_handle_item_setup(
{
GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* name_expr = gtk_property_expression_new( FOOBAR_TYPE_APPLICATION_ITEM, item_expr, "name" );
- gtk_expression_bind( name_expr, name, "label", list_item );
+ GtkExpression* title_expr = gtk_property_expression_new( FOOBAR_TYPE_LAUNCHER_ITEM, item_expr, "title" );
+ gtk_expression_bind( title_expr, title, "label", list_item );
}
{
GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* description_expr = gtk_property_expression_new( FOOBAR_TYPE_APPLICATION_ITEM, item_expr, "description" );
+ GtkExpression* description_expr = gtk_property_expression_new( FOOBAR_TYPE_LAUNCHER_ITEM, item_expr, "description" );
gtk_expression_bind( gtk_expression_ref( description_expr ), description, "label", list_item );
GtkExpression* visible_params[] = { description_expr };
GtkExpression* visible_expr = gtk_cclosure_expression_new(
@@ -496,8 +525,8 @@ void foobar_launcher_handle_item_activate(
FoobarLauncher* self = (FoobarLauncher*)userdata;
GListModel* list = G_LIST_MODEL( gtk_list_view_get_model( view ) );
- FoobarApplicationItem* item = g_list_model_get_item( list, position );
- foobar_application_item_launch( item );
+ FoobarLauncherItem* item = g_list_model_get_item( list, position );
+ foobar_launcher_item_activate( item );
gtk_widget_set_visible( GTK_WIDGET( self ), FALSE );
}
diff --git a/src/launcher.h b/src/launcher.h
index 4c3e495..5d92607 100644
--- a/src/launcher.h
+++ b/src/launcher.h
@@ -3,6 +3,7 @@
#include <gtk/gtk.h>
#include "services/application-service.h"
#include "services/configuration-service.h"
+#include "services/quick-answer-service.h"
G_BEGIN_DECLS
@@ -11,6 +12,7 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE( FoobarLauncher, foobar_launcher, FOOBAR, LAUNCHER, GtkWindow )
FoobarLauncher* foobar_launcher_new ( FoobarApplicationService* application_service,
+ FoobarQuickAnswerService* quick_answer_service,
FoobarConfigurationService* configuration_service );
void foobar_launcher_apply_configuration( FoobarLauncher* self,
FoobarLauncherConfiguration const* config );
diff --git a/src/meson.build b/src/meson.build
index b089bce..78e0575 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -4,6 +4,7 @@ foobar_sources = files(
'utils.c',
'panel.c',
'launcher.c',
+ 'launcher-item.c',
'control-center.c',
'notification-area.c',
)
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