Files
federated-calendar/src/search/gcal-search-model.c
T
Georges Basile Stavracas Neto da84b727b3 core/subscriber: Pass old event on update
Now that we keep precise control over GcalEvent instances, and
can use simple pointer comparisons everywhere, GcalMonthView
implemented its update function as a simple remove + add pair.

The problem is that we're not passing the old event instance,
so removals never actually happen, since the new instance is
not yet available to subscribers.

Pass old event in additio to the new one, and use it where it
makes sense.

Closes https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/1073
2023-08-18 10:41:41 -03:00

317 lines
8.8 KiB
C

/* gcal-search-model.c
*
* Copyright 2019-2020 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 "gcal-application.h"
#include "gcal-context.h"
#include "gcal-debug.h"
#include "gcal-timeline-subscriber.h"
#include "gcal-search-hit.h"
#include "gcal-search-hit-event.h"
#include "gcal-search-model.h"
#include "gcal-utils.h"
#define MIN_RESULTS 5
#define WAIT_FOR_RESULTS_MS 0.150
struct _GcalSearchModel
{
GObject parent;
GCancellable *cancellable;
GDateTime *range_start;
GDateTime *range_end;
GListModel *model;
GTimer *timer;
guint idle_id;
};
static void gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *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 (GCAL_TYPE_TIMELINE_SUBSCRIBER,
gcal_timeline_subscriber_interface_init)
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
g_list_model_interface_init))
/*
* Callbacks
*/
static gboolean
check_for_search_hits_cb (gpointer user_data)
{
GcalSearchModel *self;
GTask *task;
task = G_TASK (user_data);
self = GCAL_SEARCH_MODEL (g_task_get_source_object (task));
if (g_task_return_error_if_cancelled (task))
goto stop_idle;
if (g_timer_elapsed (self->timer, NULL) >= WAIT_FOR_RESULTS_MS ||
g_list_model_get_n_items (self->model) >= MIN_RESULTS)
{
g_task_return_boolean (task, TRUE);
goto stop_idle;
}
return G_SOURCE_CONTINUE;
stop_idle:
g_clear_pointer (&self->timer, g_timer_destroy);
self->idle_id = 0;
return G_SOURCE_REMOVE;
}
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 gboolean
search_hits_equals_cb (gconstpointer a,
gconstpointer b)
{
GcalSearchHit *search_hit_a = GCAL_SEARCH_HIT ((gpointer) a);
GcalSearchHit *search_hit_b = GCAL_SEARCH_HIT ((gpointer) b);
return gcal_search_hit_compare (search_hit_a, search_hit_b) == 0;
}
static void
on_search_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);
}
/*
* GcalTimelineSubscriber interface
*/
static GcalRange*
gcal_search_model_get_range (GcalTimelineSubscriber *subscriber)
{
GcalSearchModel *self = GCAL_SEARCH_MODEL (subscriber);
return gcal_range_new (self->range_start, self->range_end, GCAL_RANGE_DEFAULT);
}
static void
gcal_search_model_add_event (GcalTimelineSubscriber *subscriber,
GcalEvent *event)
{
g_autoptr (GcalSearchHitEvent) search_hit = NULL;
GcalSearchModel *self;
gboolean found;
self = GCAL_SEARCH_MODEL (subscriber);
GCAL_TRACE_MSG ("Adding search hit '%s'", gcal_event_get_summary (event));
search_hit = gcal_search_hit_event_new (event);
found = g_list_store_find_with_equal_func (G_LIST_STORE (self->model),
search_hit,
search_hits_equals_cb,
NULL);
if (found)
return;
g_list_store_insert_sorted (G_LIST_STORE (self->model),
search_hit,
compare_search_hits_cb,
self);
}
static void
gcal_search_model_update_event (GcalTimelineSubscriber *subscriber,
GcalEvent *old_event,
GcalEvent *event)
{
}
static void
gcal_search_model_remove_event (GcalTimelineSubscriber *subscriber,
GcalEvent *event)
{
}
static void
gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface)
{
iface->get_range = gcal_search_model_get_range;
iface->add_event = gcal_search_model_add_event;
iface->update_event = gcal_search_model_update_event;
iface->remove_event = gcal_search_model_remove_event;
}
/*
* GListModel interface
*/
static GType
gcal_search_model_get_item_type (GListModel *model)
{
return GCAL_TYPE_SEARCH_HIT;
}
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_assert (self->timer == NULL);
g_assert (self->idle_id == 0);
g_cancellable_cancel (self->cancellable);
gcal_clear_date_time (&self->range_start);
gcal_clear_date_time (&self->range_end);
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 (GCAL_TYPE_SEARCH_HIT);
g_signal_connect (self->model, "items-changed", G_CALLBACK (on_search_model_items_changed_cb), self);
}
GcalSearchModel *
gcal_search_model_new (GCancellable *cancellable,
GDateTime *range_start,
GDateTime *range_end)
{
GcalSearchModel *model;
model = g_object_new (GCAL_TYPE_SEARCH_MODEL, NULL);
model->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
model->range_start = g_date_time_ref (range_start);
model->range_end = g_date_time_ref (range_end);
return model;
}
void
gcal_search_model_wait_for_hits (GcalSearchModel *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr (GTask) task = NULL;
GCAL_ENTRY;
g_assert (self->timer == NULL);
g_assert (self->idle_id == 0);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gcal_search_model_wait_for_hits);
g_task_set_priority (task, G_PRIORITY_LOW);
self->timer = g_timer_new ();
self->idle_id = g_idle_add_full (G_PRIORITY_LOW,
check_for_search_hits_cb,
g_object_ref (task),
g_object_unref);
GCAL_EXIT;
}
gboolean
gcal_search_model_wait_for_hits_finish (GcalSearchModel *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GCAL_IS_SEARCH_MODEL (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}