search: Introduce a search engine

This is architectured in such a way that it will
allow us to introduce other searcheable terms in
the future.

All the functionality that in the past was in
GcalManager is now spread across GcalSearchModel,
GcalSearchHitEvent and GcalSearchEngine.
This commit is contained in:
Georges Basile Stavracas Neto
2019-04-29 17:08:53 -03:00
parent 6ecb7465eb
commit 6994723d79
14 changed files with 1140 additions and 62 deletions
+29
View File
@@ -33,6 +33,7 @@ struct _GcalContext
GcalClock *clock;
GoaClient *goa_client;
GcalManager *manager;
GcalSearchEngine *search_engine;
GSettings *settings;
GcalTimeFormat time_format;
GcalWeatherService *weather_service;
@@ -49,6 +50,7 @@ enum
PROP_CLOCK,
PROP_GOA_CLIENT,
PROP_MANAGER,
PROP_SEARCH_ENGINE,
PROP_SETTINGS,
PROP_TIME_FORMAT,
PROP_TIMEZONE,
@@ -140,6 +142,10 @@ gcal_context_get_property (GObject *object,
g_value_set_object (value, self->manager);
break;
case PROP_SEARCH_ENGINE:
g_value_set_object (value, self->search_engine);
break;
case PROP_SETTINGS:
g_value_set_object (value, self->settings);
break;
@@ -172,6 +178,7 @@ gcal_context_set_property (GObject *object,
case PROP_CLOCK:
case PROP_GOA_CLIENT:
case PROP_MANAGER:
case PROP_SEARCH_ENGINE:
case PROP_SETTINGS:
case PROP_TIME_FORMAT:
case PROP_TIMEZONE:
@@ -208,6 +215,12 @@ gcal_context_class_init (GcalContextClass *klass)
GCAL_TYPE_MANAGER,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_SEARCH_ENGINE] = g_param_spec_object ("search-engine",
"Search engine",
"Search engine",
GCAL_TYPE_SEARCH_ENGINE,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_SETTINGS] = g_param_spec_object ("settings",
"GNOME Calendar settings",
"GNOME Calendar settings",
@@ -244,6 +257,7 @@ gcal_context_init (GcalContext *self)
self->manager = gcal_manager_new ();
self->settings = g_settings_new ("org.gnome.calendar");
self->weather_service = gcal_weather_service_new ();
self->search_engine = gcal_search_engine_new (self);
self->timezone_monitor = gcal_time_zone_monitor_new ();
g_signal_connect_object (self->timezone_monitor,
@@ -321,6 +335,21 @@ gcal_context_get_manager (GcalContext *self)
return self->manager;
}
/**
* gcal_context_get_search_engine:
*
* Retrieves the #GcalSearchEngine from @self.
*
* Returns: (transfer none): a #GcalSearchEngine
*/
GcalSearchEngine*
gcal_context_get_search_engine (GcalContext *self)
{
g_return_val_if_fail (GCAL_IS_CONTEXT (self), NULL);
return self->search_engine;
}
/**
* gcal_context_get_settings:
*
+3
View File
@@ -23,6 +23,7 @@
#include "gcal-clock.h"
#include "gcal-enums.h"
#include "gcal-manager.h"
#include "gcal-search-engine.h"
#include "weather/gcal-weather-service.h"
#include <glib-object.h>
@@ -41,6 +42,8 @@ GoaClient* gcal_context_get_goa_client (GcalContext
GcalManager* gcal_context_get_manager (GcalContext *self);
GcalSearchEngine* gcal_context_get_search_engine (GcalContext *self);
GSettings* gcal_context_get_settings (GcalContext *self);
GcalTimeFormat gcal_context_get_time_format (GcalContext *self);
+12 -60
View File
@@ -81,7 +81,6 @@ struct _GcalManager
ECredentialsPrompter *credentials_prompter;
ECalDataModel *e_data_model;
ECalDataModel *search_data_model;
ECalDataModel *shell_search_data_model;
ViewStateData *search_view_data;
@@ -171,8 +170,6 @@ remove_source (GcalManager *self,
e_cal_data_model_remove_client (self->e_data_model,
e_source_get_uid (source));
e_cal_data_model_remove_client (self->search_data_model,
e_source_get_uid (source));
unit = g_hash_table_lookup (self->clients, source);
if (unit && unit->client)
@@ -303,7 +300,6 @@ on_client_connected (GObject *source_object,
if (enabled)
{
e_cal_data_model_add_client (self->e_data_model, client);
e_cal_data_model_add_client (self->search_data_model, client);
if (self->shell_search_data_model != NULL)
e_cal_data_model_add_client (self->shell_search_data_model, client);
}
@@ -655,7 +651,6 @@ gcal_manager_finalize (GObject *object)
GCAL_ENTRY;
g_clear_object (&self->e_data_model);
g_clear_object (&self->search_data_model);
g_clear_object (&self->shell_search_data_model);
if (self->search_view_data)
@@ -1080,56 +1075,6 @@ gcal_manager_set_subscriber (GcalManager *self,
GCAL_EXIT;
}
/**
* gcal_manager_set_search_subscriber:
* @self: a #GcalManager
* @subscriber: a #ECalDataModelSubscriber
* @range_start: the start of the range
* @range_end: the end of the range
*
* Sets the @subscriber to show events between @range_start
* and @range_end.
*/
void
gcal_manager_set_search_subscriber (GcalManager *self,
ECalDataModelSubscriber *subscriber,
time_t range_start,
time_t range_end)
{
GCAL_ENTRY;
g_return_if_fail (GCAL_IS_MANAGER (self));
e_cal_data_model_subscribe (self->search_data_model,
subscriber,
range_start,
range_end);
GCAL_EXIT;
}
/**
* gcal_manager_set_query:
* @self: A #GcalManager instance
* @query: (nullable): query terms or %NULL
*
* Set the query terms of the #ECalDataModel or clear it if %NULL is
* passed
*/
void
gcal_manager_set_query (GcalManager *self,
const gchar *query)
{
GCAL_ENTRY;
g_return_if_fail (GCAL_IS_MANAGER (self));
e_cal_data_model_set_filter (self->search_data_model,
query != NULL ? query : "#t");
GCAL_EXIT;
}
/**
* gcal_manager_query_client_data:
*
@@ -1246,7 +1191,6 @@ gcal_manager_enable_source (GcalManager *self,
}
e_cal_data_model_add_client (self->e_data_model, unit->client);
e_cal_data_model_add_client (self->search_data_model, unit->client);
if (self->shell_search_data_model)
e_cal_data_model_add_client (self->shell_search_data_model, unit->client);
@@ -1291,7 +1235,6 @@ gcal_manager_disable_source (GcalManager *self,
source_uid = e_source_get_uid (source);
e_cal_data_model_remove_client (self->e_data_model, source_uid);
e_cal_data_model_remove_client (self->search_data_model, source_uid);
if (self->shell_search_data_model != NULL)
e_cal_data_model_remove_client (self->shell_search_data_model, source_uid);
@@ -1821,12 +1764,9 @@ gcal_manager_startup (GcalManager *self)
/* create data model */
self->e_data_model = e_cal_data_model_new (gcal_thread_submit_job);
self->search_data_model = e_cal_data_model_new (gcal_thread_submit_job);
e_cal_data_model_set_expand_recurrences (self->e_data_model, TRUE);
e_cal_data_model_set_timezone (self->e_data_model, e_cal_util_get_system_timezone ());
e_cal_data_model_set_expand_recurrences (self->search_data_model, TRUE);
e_cal_data_model_set_timezone (self->search_data_model, e_cal_util_get_system_timezone ());
sources = e_source_registry_list_enabled (self->source_registry, E_SOURCE_EXTENSION_CALENDAR);
self->sources_at_launch = g_list_length (sources);
@@ -1838,3 +1778,15 @@ gcal_manager_startup (GcalManager *self)
GCAL_EXIT;
}
ECalClient*
gcal_manager_get_client (GcalManager *self,
ESource *source)
{
GcalManagerUnit *unit;
g_return_val_if_fail (GCAL_IS_MANAGER (self), NULL);
unit = g_hash_table_lookup (self->clients, source);
return unit ? unit->client : NULL;
}
+3
View File
@@ -121,6 +121,9 @@ GList* gcal_manager_get_shell_search_events (GcalManager
void gcal_manager_startup (GcalManager *self);
ECalClient* gcal_manager_get_client (GcalManager *self,
ESource *source);
G_END_DECLS
#endif /* __GCAL_MANAGER_H__ */
+4 -1
View File
@@ -21,6 +21,10 @@ gcal_deps = [
sources = files(
'search/gcal-search-button.c',
'search/gcal-search-engine.c',
'search/gcal-search-hit.c',
'search/gcal-search-hit-event.c',
'search/gcal-search-model.c',
'utils/gcal-date-time-utils.c',
'utils/gcal-thread-utils.c',
'utils/gcal-utils.c',
@@ -51,7 +55,6 @@ sources = files(
'gcal-night-light-monitor.c',
'gcal-quick-add-popover.c',
'gcal-recurrence.c',
'gcal-search-popover.c',
'gcal-shell-search-provider.c',
'gcal-source-dialog.c',
'gcal-time-selector.c',
+87 -1
View File
@@ -23,10 +23,16 @@
#include "gcal-context.h"
#include "gcal-search-button.h"
#include <math.h>
#define MIN_WIDTH 450
struct _GcalSearchButton
{
DzlSuggestionButton parent;
GCancellable *cancellable;
GcalContext *context;
};
@@ -46,6 +52,74 @@ static GParamSpec *properties [N_PROPS];
* Callbacks
*/
static void
position_suggestion_popover_func (DzlSuggestionEntry *entry,
GdkRectangle *area,
gboolean *is_absolute,
gpointer user_data)
{
gint new_width;
#define RIGHT_MARGIN 6
dzl_suggestion_entry_window_position_func (entry, area, is_absolute, NULL);
new_width = MAX (area->width * 2 / 5, MIN_WIDTH);
area->x += area->width - new_width;
area->width = new_width - RIGHT_MARGIN;
area->y -= 3;
#undef RIGHT_MARGIN
}
static void
on_search_finished_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr (GListModel) model = NULL;
g_autoptr (GError) error = NULL;
DzlSuggestionEntry *entry;
GcalSearchButton *self;
self = GCAL_SEARCH_BUTTON (user_data);
model = gcal_search_engine_search_finish (GCAL_SEARCH_ENGINE (source_object), result, &error);
entry = dzl_suggestion_button_get_entry (DZL_SUGGESTION_BUTTON (self));
dzl_suggestion_entry_set_model (entry, model);
}
static void
on_search_entry_changed_cb (GcalSearchButton *self)
{
g_autofree gchar *sexp_query = NULL;
DzlSuggestionEntry *entry;
GcalSearchEngine *search_engine;
const gchar *typed_text;
entry = dzl_suggestion_button_get_entry (DZL_SUGGESTION_BUTTON (self));
typed_text = dzl_suggestion_entry_get_typed_text (entry);
g_cancellable_cancel (self->cancellable);
if (dzl_str_empty0 (typed_text))
{
g_debug ("Cancelling search");
dzl_suggestion_entry_set_model (entry, NULL);
return;
}
sexp_query = g_strdup_printf ("(contains? \"summary\" \"%s\")", typed_text);
search_engine = gcal_context_get_search_engine (self->context);
gcal_search_engine_search (search_engine,
sexp_query,
50,
self->cancellable,
on_search_finished_cb,
g_object_ref (self));
}
static void
on_unfocus_action_activated_cb (GSimpleAction *action,
GVariant *param,
@@ -89,6 +163,8 @@ gcal_search_button_finalize (GObject *object)
{
GcalSearchButton *self = (GcalSearchButton *)object;
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
g_clear_object (&self->context);
G_OBJECT_CLASS (gcal_search_button_parent_class)->finalize (object);
@@ -177,8 +253,18 @@ gcal_search_button_init (GcalSearchButton *self)
gtk_widget_insert_action_group (GTK_WIDGET (self), "search", G_ACTION_GROUP (group));
entry = dzl_suggestion_button_get_entry (DZL_SUGGESTION_BUTTON (self));
controller = dzl_shortcut_controller_find (GTK_WIDGET (entry));
g_signal_connect_object (entry,
"changed",
G_CALLBACK (on_search_entry_changed_cb),
self,
G_CONNECT_SWAPPED);
dzl_suggestion_entry_set_position_func (entry,
position_suggestion_popover_func,
self,
NULL);
controller = dzl_shortcut_controller_find (GTK_WIDGET (entry));
dzl_shortcut_controller_add_command_callback (controller,
"org.gnome.calendar.search",
"<Primary>f",
+331
View File
@@ -0,0 +1,331 @@
/* gcal-search-engine.c
*
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "GcalSearchEngine"
#include "gcal-context.h"
#include "gcal-date-time-utils.h"
#include "gcal-search-engine.h"
#include "gcal-search-model.h"
#include "gcal-thread-utils.h"
#include <dazzle.h>
typedef struct
{
GcalSearchEngine *engine;
gchar *query;
gint max_results;
GDateTime *range_start;
GDateTime *range_end;
} SearchData;
struct _GcalSearchEngine
{
GObject parent;
ECalDataModel *data_model;
GcalContext *context;
};
G_DEFINE_TYPE (GcalSearchEngine, gcal_search_engine, G_TYPE_OBJECT)
enum
{
PROP_0,
PROP_CONTEXT,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
/*
* Auxiliary methods
*/
static void
search_data_free (gpointer data)
{
SearchData *search_data = data;
if (!data)
return;
gcal_clear_date_time (&search_data->range_start);
gcal_clear_date_time (&search_data->range_end);
g_clear_pointer (&search_data->query, g_free);
g_clear_object (&search_data->engine);
g_slice_free (SearchData, data);
}
/*
* Callbacks
*/
static void
search_func (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autoptr (GcalSearchModel) model = NULL;
GcalSearchEngine *self;
SearchData *data;
time_t start;
time_t end;
self = GCAL_SEARCH_ENGINE (source_object);
data = (SearchData*) task_data;
start = g_date_time_to_unix (data->range_start);
end = g_date_time_to_unix (data->range_end);
model = gcal_search_model_new (cancellable, data->max_results);
e_cal_data_model_set_filter (self->data_model, data->query);
e_cal_data_model_subscribe (self->data_model,
E_CAL_DATA_MODEL_SUBSCRIBER (model),
start,
end);
//e_cal_data_model_unsubscribe (self->data_model, E_CAL_DATA_MODEL_SUBSCRIBER (model));
g_task_return_pointer (task, g_steal_pointer (&model), g_object_unref);
}
static void
on_manager_source_added_cb (GcalManager *manager,
ESource *source,
gboolean enabled,
GcalSearchEngine *self)
{
ECalClient *client;
g_debug ("Removing source %s from search results", e_source_get_uid (source));
client = gcal_manager_get_client (manager, source);
if (enabled)
e_cal_data_model_add_client (self->data_model, client);
}
static void
on_manager_source_enabled_cb (GcalManager *manager,
ESource *source,
gboolean enabled,
GcalSearchEngine *self)
{
g_debug ("Changing source %s from search results", e_source_get_uid (source));
if (enabled)
{
ECalClient *client;
client = gcal_manager_get_client (manager, source);
e_cal_data_model_add_client (self->data_model, client);
}
else
{
e_cal_data_model_remove_client (self->data_model, e_source_get_uid (source));
}
}
static void
on_manager_source_removed_cb (GcalManager *manager,
ESource *source,
GcalSearchEngine *self)
{
g_debug ("Removing source %s from search results", e_source_get_uid (source));
e_cal_data_model_remove_client (self->data_model, e_source_get_uid (source));
}
static void
on_timezone_changed_cb (GcalContext *context,
GParamSpec *pspec,
GcalSearchEngine *self)
{
g_debug ("Timezone changed");
}
/*
* GObject overrides
*/
static void
gcal_search_engine_finalize (GObject *object)
{
GcalSearchEngine *self = (GcalSearchEngine *)object;
g_clear_object (&self->context);
g_clear_object (&self->data_model);
G_OBJECT_CLASS (gcal_search_engine_parent_class)->finalize (object);
}
static void
gcal_search_engine_constructed (GObject *object)
{
GcalSearchEngine *self = (GcalSearchEngine *)object;
GcalManager *manager;
G_OBJECT_CLASS (gcal_search_engine_parent_class)->constructed (object);
/* Setup the data model */
self->data_model = e_cal_data_model_new (gcal_thread_submit_job);
e_cal_data_model_set_expand_recurrences (self->data_model, TRUE);
e_cal_data_model_set_timezone (self->data_model, e_cal_util_get_system_timezone ());
manager = gcal_context_get_manager (self->context);
g_signal_connect_object (manager, "source-added", G_CALLBACK (on_manager_source_added_cb), self, 0);
g_signal_connect_object (manager, "source-removed", G_CALLBACK (on_manager_source_removed_cb), self, 0);
g_signal_connect_object (manager, "source-enabled", G_CALLBACK (on_manager_source_enabled_cb), self, 0);
g_signal_connect_object (self->context,
"notify::timezone",
G_CALLBACK (on_timezone_changed_cb),
self,
0);
}
static void
gcal_search_engine_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GcalSearchEngine *self = GCAL_SEARCH_ENGINE (object);
switch (prop_id)
{
case PROP_CONTEXT:
g_value_set_object (value, self->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_search_engine_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GcalSearchEngine *self = GCAL_SEARCH_ENGINE (object);
switch (prop_id)
{
case PROP_CONTEXT:
g_assert (self->context == NULL);
self->context = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_search_engine_class_init (GcalSearchEngineClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gcal_search_engine_finalize;
object_class->constructed = gcal_search_engine_constructed;
object_class->get_property = gcal_search_engine_get_property;
object_class->set_property = gcal_search_engine_set_property;
/**
* GcalSearchEngine::context:
*
* The #GcalContext of the application.
*/
properties[PROP_CONTEXT] = g_param_spec_object ("context",
"Data context",
"Data context",
GCAL_TYPE_CONTEXT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
gcal_search_engine_init (GcalSearchEngine *self)
{
}
GcalSearchEngine *
gcal_search_engine_new (GcalContext *context)
{
return g_object_new (GCAL_TYPE_SEARCH_ENGINE,
"context", context,
NULL);
}
void
gcal_search_engine_search (GcalSearchEngine *self,
const gchar *search_query,
gint max_results,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr (GDateTime) now = NULL;
g_autoptr (GTask) task = NULL;
SearchData *data = NULL;
GTimeZone *timezone;
g_return_if_fail (GCAL_IS_SEARCH_ENGINE (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
timezone = gcal_context_get_timezone (self->context);
now = g_date_time_new_now (timezone);
data = g_slice_new0 (SearchData);
data->engine = g_object_ref (self);
data->query = g_strdup (search_query);
data->max_results = max_results;
data->range_start = g_date_time_add_weeks (now, -1);
data->range_end = g_date_time_add_weeks (now, 3);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gcal_search_engine_search);
g_task_set_priority (task, G_PRIORITY_LOW);
g_task_set_task_data (task, data, (GDestroyNotify) search_data_free);
g_task_run_in_thread (task, search_func);
}
GListModel*
gcal_search_engine_search_finish (GcalSearchEngine *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GCAL_IS_SEARCH_ENGINE (self), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
+45
View File
@@ -0,0 +1,45 @@
/* gcal-search-engine.h
*
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gio/gio.h>
G_BEGIN_DECLS
#define GCAL_TYPE_SEARCH_ENGINE (gcal_search_engine_get_type())
G_DECLARE_FINAL_TYPE (GcalSearchEngine, gcal_search_engine, GCAL, SEARCH_ENGINE, GObject)
typedef struct _GcalContext GcalContext;
GcalSearchEngine* gcal_search_engine_new (GcalContext *context);
void gcal_search_engine_search (GcalSearchEngine *self,
const gchar *search_query,
gint max_results,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GListModel* gcal_search_engine_search_finish (GcalSearchEngine *self,
GAsyncResult *result,
GError **error);
G_END_DECLS
+225
View File
@@ -0,0 +1,225 @@
/* gcal-search-hit-event.c
*
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "GcalSearchHitEvent"
#include "gcal-search-hit.h"
#include "gcal-search-hit-event.h"
#include "gcal-utils.h"
struct _GcalSearchHitEvent
{
DzlSuggestion parent;
GcalEvent *event;
};
static void gcal_search_hit_interface_init (GcalSearchHitInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GcalSearchHitEvent, gcal_search_hit_event, DZL_TYPE_SUGGESTION,
G_IMPLEMENT_INTERFACE (GCAL_TYPE_SEARCH_HIT, gcal_search_hit_interface_init))
enum
{
PROP_0,
PROP_EVENT,
N_PROPS,
};
static GParamSpec *properties [N_PROPS];
/*
* Auxiliary methods
*/
static void
set_event (GcalSearchHitEvent *self,
GcalEvent *event)
{
g_autofree gchar *date_string = NULL;
DzlSuggestion *suggestion;
self->event = g_object_ref (event);
suggestion = DZL_SUGGESTION (self);
dzl_suggestion_set_id (suggestion, gcal_event_get_uid (event));
dzl_suggestion_set_title (suggestion, gcal_event_get_summary (event));
/* FIXME: use a better date description */
date_string = g_date_time_format (gcal_event_get_date_start (event), "%x %X %z");
dzl_suggestion_set_subtitle (suggestion, date_string);
}
/*
* DzlSuggestion overrides
*/
static cairo_surface_t*
gcal_search_hit_event_get_icon_surface (DzlSuggestion *suggestion,
GtkWidget *widget)
{
GcalSearchHitEvent *self;
cairo_surface_t *surface;
ESource *source;
GdkRGBA color;
self = GCAL_SEARCH_HIT_EVENT (suggestion);
source = gcal_event_get_source (self->event);
get_color_name_from_source (source, &color);
surface = get_circle_surface_from_color (&color, 16);
/* Inject our custom style class into the given widget */
gtk_style_context_add_class (gtk_widget_get_style_context (widget), "calendar-color-image");
return surface;
}
/*
* GcalSearchHit interface
*/
static gint
gcal_search_hit_event_get_priority (GcalSearchHit *search_hit)
{
return 0;
}
static gint
gcal_search_hit_event_compare (GcalSearchHit *a,
GcalSearchHit *b)
{
GcalEvent *event_a;
GcalEvent *event_b;
time_t now_utc;
g_assert (GCAL_IS_SEARCH_HIT_EVENT (a));
g_assert (GCAL_IS_SEARCH_HIT_EVENT (b));
event_a = GCAL_SEARCH_HIT_EVENT (a)->event;
event_b = GCAL_SEARCH_HIT_EVENT (b)->event;
now_utc = time (NULL);
return -gcal_event_compare_with_current (event_a, event_b, now_utc);
}
static void
gcal_search_hit_interface_init (GcalSearchHitInterface *iface)
{
iface->get_priority = gcal_search_hit_event_get_priority;
iface->compare = gcal_search_hit_event_compare;
}
/*
* GObject overrides
*/
static void
gcal_search_hit_event_finalize (GObject *object)
{
GcalSearchHitEvent *self = (GcalSearchHitEvent *)object;
g_clear_object (&self->event);
G_OBJECT_CLASS (gcal_search_hit_event_parent_class)->finalize (object);
}
static void
gcal_search_hit_event_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GcalSearchHitEvent *self = GCAL_SEARCH_HIT_EVENT (object);
switch (prop_id)
{
case PROP_EVENT:
g_value_set_object (value, self->event);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_search_hit_event_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GcalSearchHitEvent *self = GCAL_SEARCH_HIT_EVENT (object);
switch (prop_id)
{
case PROP_EVENT:
g_assert (self->event == NULL);
set_event (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gcal_search_hit_event_class_init (GcalSearchHitEventClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
DzlSuggestionClass *suggestion_class = DZL_SUGGESTION_CLASS (klass);
object_class->finalize = gcal_search_hit_event_finalize;
object_class->get_property = gcal_search_hit_event_get_property;
object_class->set_property = gcal_search_hit_event_set_property;
suggestion_class->get_icon_surface = gcal_search_hit_event_get_icon_surface;
properties[PROP_EVENT] = g_param_spec_object ("event",
"Event",
"Event",
GCAL_TYPE_EVENT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
gcal_search_hit_event_init (GcalSearchHitEvent *self)
{
}
GcalSearchHitEvent *
gcal_search_hit_event_new (GcalEvent *event)
{
return g_object_new (GCAL_TYPE_SEARCH_HIT_EVENT,
"event", event,
NULL);
}
GcalEvent*
gcal_search_hit_event_get_event (GcalSearchHitEvent *self)
{
g_return_val_if_fail (GCAL_IS_SEARCH_HIT_EVENT (self), NULL);
return self->event;
}
+36
View File
@@ -0,0 +1,36 @@
/* gcal-search-hit-event.h
*
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "gcal-event.h"
#include <dazzle.h>
G_BEGIN_DECLS
#define GCAL_TYPE_SEARCH_HIT_EVENT (gcal_search_hit_event_get_type())
G_DECLARE_FINAL_TYPE (GcalSearchHitEvent, gcal_search_hit_event, GCAL, SEARCH_HIT_EVENT, DzlSuggestion)
GcalSearchHitEvent* gcal_search_hit_event_new (GcalEvent *event);
GcalEvent* gcal_search_hit_event_get_event (GcalSearchHitEvent *self);
G_END_DECLS
+47
View File
@@ -0,0 +1,47 @@
/* gcal-search-hit.c
*
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "gcal-search-hit.h"
G_DEFINE_INTERFACE (GcalSearchHit, gcal_search_hit, DZL_TYPE_SUGGESTION)
static void
gcal_search_hit_default_init (GcalSearchHitInterface *iface)
{
}
gint
gcal_search_hit_get_priority (GcalSearchHit *self)
{
g_return_val_if_fail (GCAL_IS_SEARCH_HIT (self), 0);
g_return_val_if_fail (GCAL_SEARCH_HIT_GET_IFACE (self)->get_priority, 0);
return GCAL_SEARCH_HIT_GET_IFACE (self)->get_priority (self);
}
gint
gcal_search_hit_compare (GcalSearchHit *a,
GcalSearchHit *b)
{
g_return_val_if_fail (GCAL_IS_SEARCH_HIT (a), 0);
g_return_val_if_fail (GCAL_SEARCH_HIT_GET_IFACE (a)->compare, 0);
return GCAL_SEARCH_HIT_GET_IFACE (a)->compare (a, b);
}
+45
View File
@@ -0,0 +1,45 @@
/* gcal-search-hit.h
*
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <dazzle.h>
G_BEGIN_DECLS
#define GCAL_TYPE_SEARCH_HIT (gcal_search_hit_get_type ())
G_DECLARE_INTERFACE (GcalSearchHit, gcal_search_hit, GCAL, SEARCH_HIT, DzlSuggestion)
struct _GcalSearchHitInterface
{
GTypeInterface parent;
gint (*get_priority) (GcalSearchHit *self);
gint (*compare) (GcalSearchHit *a,
GcalSearchHit *b);
};
gint gcal_search_hit_get_priority (GcalSearchHit *self);
gint gcal_search_hit_compare (GcalSearchHit *a,
GcalSearchHit *b);
G_END_DECLS
+240
View File
@@ -0,0 +1,240 @@
/* gcal-search-model.c
*
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "GcalSearchModel"
#include "e-cal-data-model.h"
#include "gcal-debug.h"
#include "gcal-search-hit.h"
#include "gcal-search-hit-event.h"
#include "gcal-search-model.h"
#include "gcal-utils.h"
#include <dazzle.h>
struct _GcalSearchModel
{
GObject parent;
GCancellable *cancellable;
gint max_results;
GListModel *model;
};
static void e_cal_data_model_subscriber_interface_init (ECalDataModelSubscriberInterface *iface);
static void g_list_model_interface_init (GListModelInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GcalSearchModel, gcal_search_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (E_TYPE_CAL_DATA_MODEL_SUBSCRIBER,
e_cal_data_model_subscriber_interface_init)
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
g_list_model_interface_init))
/*
* Callbacks
*/
static void
on_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GcalSearchModel *self)
{
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
}
/*
* ECalDataModelSubscriber interface
*/
static gint
compare_search_hits_cb (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
GcalSearchHit *hit_a, *hit_b;
gint result;
hit_a = (GcalSearchHit *) a;
hit_b = (GcalSearchHit *) b;
/* Priority */
result = gcal_search_hit_get_priority (hit_a) - gcal_search_hit_get_priority (hit_b);
if (result != 0)
return result;
/* Compare func */
return gcal_search_hit_compare (hit_a, hit_b);
}
static void
gcal_search_model_component_added (ECalDataModelSubscriber *subscriber,
ECalClient *client,
ECalComponent *component)
{
g_autoptr (GcalSearchHitEvent) search_hit = NULL;
g_autoptr (GcalEvent) event = NULL;
g_autoptr (GError) error = NULL;
GcalSearchModel *self;
ESource *source;
self = GCAL_SEARCH_MODEL (subscriber);
if (g_list_model_get_n_items (self->model) > self->max_results)
return;
source = e_client_get_source (E_CLIENT (client));
event = gcal_event_new (source, component, &error);
if (error)
{
g_warning ("Error adding event to search results: %s", error->message);
return;
}
GCAL_TRACE_MSG ("Adding search hit '%s'", gcal_event_get_summary (event));
search_hit = gcal_search_hit_event_new (event);
g_list_store_insert_sorted (G_LIST_STORE (self->model),
search_hit,
compare_search_hits_cb,
self);
}
static void
gcal_search_model_component_modified (ECalDataModelSubscriber *subscriber,
ECalClient *client,
ECalComponent *comp)
{
}
static void
gcal_search_model_component_removed (ECalDataModelSubscriber *subscriber,
ECalClient *client,
const gchar *uid,
const gchar *rid)
{
}
static void
gcal_search_model_freeze (ECalDataModelSubscriber *subscriber)
{
}
static void
gcal_search_model_thaw (ECalDataModelSubscriber *subscriber)
{
}
static void
e_cal_data_model_subscriber_interface_init (ECalDataModelSubscriberInterface *iface)
{
iface->component_added = gcal_search_model_component_added;
iface->component_modified = gcal_search_model_component_modified;
iface->component_removed = gcal_search_model_component_removed;
iface->freeze = gcal_search_model_freeze;
iface->thaw = gcal_search_model_thaw;
}
/*
* GListModel interface
*/
static GType
gcal_search_model_get_item_type (GListModel *model)
{
return DZL_TYPE_SUGGESTION;
}
static guint
gcal_search_model_get_n_items (GListModel *model)
{
GcalSearchModel *self = (GcalSearchModel *)model;
return g_list_model_get_n_items (self->model);
}
static gpointer
gcal_search_model_get_item (GListModel *model,
guint position)
{
GcalSearchModel *self = (GcalSearchModel *)model;
return g_list_model_get_item (self->model, position);
}
static void
g_list_model_interface_init (GListModelInterface *iface)
{
iface->get_item_type = gcal_search_model_get_item_type;
iface->get_n_items = gcal_search_model_get_n_items;
iface->get_item = gcal_search_model_get_item;
}
/*
* GObject overrides
*/
static void
gcal_search_model_finalize (GObject *object)
{
GcalSearchModel *self = (GcalSearchModel *)object;
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
g_clear_object (&self->model);
G_OBJECT_CLASS (gcal_search_model_parent_class)->finalize (object);
}
static void
gcal_search_model_class_init (GcalSearchModelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gcal_search_model_finalize;
}
static void
gcal_search_model_init (GcalSearchModel *self)
{
self->model = (GListModel*) g_list_store_new (DZL_TYPE_SUGGESTION);
g_signal_connect_object (self->model, "items-changed", G_CALLBACK (on_model_items_changed_cb), self, 0);
}
GcalSearchModel *
gcal_search_model_new (GCancellable *cancellable,
gint max_results)
{
GcalSearchModel *model;
model = g_object_new (GCAL_TYPE_SEARCH_MODEL, NULL);
model->max_results = max_results;
model->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
return model;
}
+33
View File
@@ -0,0 +1,33 @@
/* gcal-search-model.h
*
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gio/gio.h>
G_BEGIN_DECLS
#define GCAL_TYPE_SEARCH_MODEL (gcal_search_model_get_type())
G_DECLARE_FINAL_TYPE (GcalSearchModel, gcal_search_model, GCAL, SEARCH_MODEL, GObject)
GcalSearchModel* gcal_search_model_new (GCancellable *cancellable,
gint max_results);
G_END_DECLS