aboutsummaryrefslogtreecommitdiffstats
path: root/modules/calendar/e-cal-shell-view-private.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/calendar/e-cal-shell-view-private.c')
-rw-r--r--modules/calendar/e-cal-shell-view-private.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/modules/calendar/e-cal-shell-view-private.c b/modules/calendar/e-cal-shell-view-private.c
index 818ee3047e..fc94a83a21 100644
--- a/modules/calendar/e-cal-shell-view-private.c
+++ b/modules/calendar/e-cal-shell-view-private.c
@@ -687,6 +687,8 @@ e_cal_shell_view_private_dispose (ECalShellView *cal_shell_view)
ECalShellViewPrivate *priv = cal_shell_view->priv;
gint i;
+ e_cal_shell_view_search_stop (cal_shell_view);
+
/* Calling calendar's save state from here,
* because it is too late in its dispose. */
if (priv->cal_shell_content != NULL)
@@ -1159,3 +1161,463 @@ e_cal_shell_view_update_timezone (ECalShellView *cal_shell_view)
g_list_free (clients);
}
+
+static gint
+cal_searching_get_search_range_years (ECalShellView *cal_shell_view)
+{
+ EShellBackend *backend;
+ EShellSettings *shell_settings;
+ gint value;
+
+ backend = e_shell_view_get_shell_backend (E_SHELL_VIEW (cal_shell_view));
+ shell_settings = e_shell_get_shell_settings (e_shell_backend_get_shell (backend));
+
+ value = e_shell_settings_get_int (shell_settings, "cal-search-range-years");
+ if (value <= 0)
+ value = 10;
+
+ return value;
+}
+
+static gint
+cal_time_t_ptr_compare (gconstpointer a,
+ gconstpointer b)
+{
+ const time_t *ta = a, *tb = b;
+
+ return (ta ? *ta : 0) - (tb ? *tb : 0);
+}
+
+static void cal_iterate_searching (ECalShellView *cal_shell_view);
+
+struct GenerateInstancesData
+{
+ ECalClient *client;
+ ECalShellView *cal_shell_view;
+ GCancellable *cancellable;
+};
+
+static void
+cal_searching_instances_done_cb (gpointer user_data)
+{
+ struct GenerateInstancesData *gid = user_data;
+
+ g_return_if_fail (gid != NULL);
+ g_return_if_fail (gid->cal_shell_view != NULL);
+
+ if (!g_cancellable_is_cancelled (gid->cancellable)) {
+ gid->cal_shell_view->priv->search_pending_count--;
+ if (!gid->cal_shell_view->priv->search_pending_count) {
+ gid->cal_shell_view->priv->search_hit_cache =
+ g_slist_sort (gid->cal_shell_view->priv->search_hit_cache, cal_time_t_ptr_compare);
+ cal_iterate_searching (gid->cal_shell_view);
+ }
+ }
+
+ g_object_unref (gid->cancellable);
+ g_free (gid);
+}
+
+static gboolean
+cal_searching_got_instance_cb (ECalComponent *comp,
+ time_t instance_start,
+ time_t instance_end,
+ gpointer user_data)
+{
+ struct GenerateInstancesData *gid = user_data;
+ ECalShellViewPrivate *priv;
+ ECalComponentDateTime dt;
+ time_t *value;
+
+ g_return_val_if_fail (gid != NULL, FALSE);
+
+ if (g_cancellable_is_cancelled (gid->cancellable))
+ return FALSE;
+
+ g_return_val_if_fail (gid->cal_shell_view != NULL, FALSE);
+ g_return_val_if_fail (gid->cal_shell_view->priv != NULL, FALSE);
+
+ e_cal_component_get_dtstart (comp, &dt);
+
+ if (dt.tzid && dt.value) {
+ icaltimezone *zone = NULL;
+ if (!e_cal_client_get_timezone_sync (gid->client, dt.tzid, &zone, gid->cancellable, NULL)) {
+ zone = NULL;
+ }
+
+ if (g_cancellable_is_cancelled (gid->cancellable))
+ return FALSE;
+
+ if (zone)
+ instance_start = icaltime_as_timet_with_zone (*dt.value, zone);
+ }
+
+ e_cal_component_free_datetime (&dt);
+
+ priv = gid->cal_shell_view->priv;
+ value = g_new (time_t, 1);
+ *value = instance_start;
+ if (!g_slist_find_custom (priv->search_hit_cache, value, cal_time_t_ptr_compare))
+ priv->search_hit_cache = g_slist_append (priv->search_hit_cache, value);
+ else
+ g_free (value);
+
+ return TRUE;
+}
+
+static void
+cal_search_get_object_list_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECalClient *client = E_CAL_CLIENT (source);
+ ECalShellView *cal_shell_view = user_data;
+ GSList *icalcomps = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (result != NULL);
+ g_return_if_fail (cal_shell_view != NULL);
+
+ if (!e_cal_client_get_object_list_finish (client, result, &icalcomps, &error) || !icalcomps) {
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_clear_error (&error);
+ return;
+ }
+
+ g_clear_error (&error);
+ cal_shell_view->priv->search_pending_count--;
+ if (!cal_shell_view->priv->search_pending_count) {
+ cal_shell_view->priv->search_hit_cache =
+ g_slist_sort (cal_shell_view->priv->search_hit_cache, cal_time_t_ptr_compare);
+ cal_iterate_searching (cal_shell_view);
+ }
+ } else {
+ GSList *iter;
+ GCancellable *cancellable;
+ time_t start, end;
+
+ cancellable = e_activity_get_cancellable (cal_shell_view->priv->searching_activity);
+ start = time_add_day (cal_shell_view->priv->search_time, (-1) * cal_shell_view->priv->search_direction);
+ end = cal_shell_view->priv->search_time;
+ if (start > end) {
+ time_t tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ for (iter = icalcomps; iter; iter = iter->next) {
+ icalcomponent *icalcomp = iter->data;
+ struct GenerateInstancesData *gid = g_new0 (struct GenerateInstancesData, 1);
+
+ gid->client = client;
+ gid->cal_shell_view = cal_shell_view;
+ gid->cancellable = g_object_ref (cancellable);
+
+ e_cal_client_generate_instances_for_object (client, icalcomp, start, end, cancellable,
+ cal_searching_got_instance_cb, gid, cal_searching_instances_done_cb);
+ }
+
+ e_cal_client_free_icalcomp_slist (icalcomps);
+ }
+}
+
+static gboolean
+cal_searching_check_candidates (ECalShellView *cal_shell_view)
+{
+ ECalShellContent *cal_shell_content;
+ GnomeCalendarViewType view_type;
+ ECalendarView *calendar_view;
+ GnomeCalendar *calendar;
+ GSList *iter;
+ time_t value, candidate = -1;
+
+ g_return_val_if_fail (cal_shell_view != NULL, FALSE);
+ g_return_val_if_fail (cal_shell_view->priv != NULL, FALSE);
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ view_type = gnome_calendar_get_view (calendar);
+ calendar_view = gnome_calendar_get_calendar_view (calendar, view_type);
+
+ if (!e_calendar_view_get_selected_time_range (calendar_view, &value, NULL))
+ return FALSE;
+
+ if (cal_shell_view->priv->search_direction > 0 && (view_type == GNOME_CAL_WEEK_VIEW || view_type == GNOME_CAL_MONTH_VIEW))
+ value = time_add_day (value, 1);
+
+ for (iter = cal_shell_view->priv->search_hit_cache; iter; iter = iter->next) {
+ time_t cache = *((time_t *) iter->data);
+
+ /* list is sorted, once the search iteration is complete */
+ if (cache > value) {
+ if (cal_shell_view->priv->search_direction > 0)
+ candidate = cache;
+ break;
+ } else if (cal_shell_view->priv->search_direction < 0 && cache != value)
+ candidate = cache;
+ }
+
+ if (candidate > 0) {
+ gnome_calendar_goto (calendar, candidate);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+cal_searching_update_alert (ECalShellView *cal_shell_view,
+ const gchar *message)
+{
+ ECalShellViewPrivate *priv;
+ EShellContent *shell_content;
+ EAlert *alert;
+
+ g_return_if_fail (cal_shell_view != NULL);
+ g_return_if_fail (cal_shell_view->priv != NULL);
+
+ priv = cal_shell_view->priv;
+
+ if (priv->search_alert) {
+ e_alert_response (priv->search_alert, e_alert_get_default_response (priv->search_alert));
+ priv->search_alert = NULL;
+ }
+
+ if (!message)
+ return;
+
+ alert = e_alert_new ("calendar:search-error-generic", message, NULL);
+ g_return_if_fail (alert != NULL);
+
+ priv->search_alert = alert;
+ g_object_add_weak_pointer (G_OBJECT (alert), &priv->search_alert);
+ e_alert_start_timer (priv->search_alert, 5);
+
+ shell_content = e_shell_view_get_shell_content (E_SHELL_VIEW (cal_shell_view));
+ e_alert_sink_submit_alert (E_ALERT_SINK (shell_content), priv->search_alert);
+ g_object_unref (priv->search_alert);
+}
+
+static void
+cal_iterate_searching (ECalShellView *cal_shell_view)
+{
+ ECalShellViewPrivate *priv;
+ GList *clients, *iter;
+ ECalModel *model;
+ time_t new_time, range1, range2;
+ icaltimezone *timezone;
+ const gchar *default_tzloc = NULL;
+ GCancellable *cancellable;
+ gchar *sexp, *start, *end;
+
+ g_return_if_fail (cal_shell_view != NULL);
+ g_return_if_fail (cal_shell_view->priv != NULL);
+
+ priv = cal_shell_view->priv;
+ g_return_if_fail (priv->search_direction != 0);
+ g_return_if_fail (priv->search_pending_count == 0);
+
+ cal_searching_update_alert (cal_shell_view, NULL);
+
+ if (cal_searching_check_candidates (cal_shell_view)) {
+ if (priv->searching_activity) {
+ e_activity_set_state (priv->searching_activity, E_ACTIVITY_COMPLETED);
+ g_object_unref (priv->searching_activity);
+ priv->searching_activity = NULL;
+ }
+
+ return;
+ }
+
+ if (!priv->searching_activity) {
+ EShellBackend *shell_backend = e_shell_view_get_shell_backend (E_SHELL_VIEW (cal_shell_view));
+
+ cancellable = g_cancellable_new ();
+ priv->searching_activity = e_activity_new ();
+ e_activity_set_cancellable (priv->searching_activity, cancellable);
+ e_activity_set_state (priv->searching_activity, E_ACTIVITY_RUNNING);
+ e_activity_set_text (priv->searching_activity,
+ priv->search_direction > 0 ?
+ _("Searching next matching event") :
+ _("Searching previous matching event"));
+
+ e_shell_backend_add_activity (shell_backend, priv->searching_activity);
+ }
+
+ new_time = time_add_day (priv->search_time, priv->search_direction);
+ if (new_time > priv->search_max_time || new_time < priv->search_min_time) {
+ gchar *alert_msg;
+ gint range_years;
+
+ /* would get out of bounds, stop searching */
+ e_activity_set_state (priv->searching_activity, E_ACTIVITY_COMPLETED);
+ g_object_unref (priv->searching_activity);
+ priv->searching_activity = NULL;
+
+ range_years = cal_searching_get_search_range_years (cal_shell_view);
+ alert_msg = g_strdup_printf (
+ priv->search_direction > 0 ?
+ ngettext ("Cannot find matching event in the next %d year",
+ "Cannot find matching event in the next %d years",
+ range_years) :
+ ngettext ("Cannot find matching event in the previous %d year",
+ "Cannot find matching event in the previous %d years",
+ range_years),
+ range_years);
+ cal_searching_update_alert (cal_shell_view, alert_msg);
+ g_free (alert_msg);
+
+ e_shell_view_update_actions (E_SHELL_VIEW (cal_shell_view));
+
+ return;
+ }
+
+ model = gnome_calendar_get_model (
+ e_cal_shell_content_get_calendar (cal_shell_view->priv->cal_shell_content));
+ clients = e_cal_model_get_client_list (model);
+
+ if (!clients) {
+ e_activity_set_state (priv->searching_activity, E_ACTIVITY_COMPLETED);
+ g_object_unref (priv->searching_activity);
+ priv->searching_activity = NULL;
+
+ cal_searching_update_alert (cal_shell_view, _("Cannot search with no active calendar"));
+
+ e_shell_view_update_actions (E_SHELL_VIEW (cal_shell_view));
+
+ return;
+ }
+
+ timezone = e_cal_model_get_timezone (model);
+ range1 = priv->search_time;
+ range2 = time_add_day (range1, priv->search_direction);
+ if (range1 < range2) {
+ start = isodate_from_time_t (time_day_begin (range1));
+ end = isodate_from_time_t (time_day_end (range2));
+ } else {
+ start = isodate_from_time_t (time_day_begin (range2));
+ end = isodate_from_time_t (time_day_end (range1));
+ }
+
+ if (timezone && timezone != icaltimezone_get_utc_timezone ())
+ default_tzloc = icaltimezone_get_location (timezone);
+ if (!default_tzloc)
+ default_tzloc = "";
+
+ sexp = g_strdup_printf (
+ "(and %s (occur-in-time-range? "
+ "(make-time \"%s\") "
+ "(make-time \"%s\") \"%s\"))",
+ e_cal_model_get_search_query (model), start, end, default_tzloc);
+
+ g_free (start);
+ g_free (end);
+
+ cancellable = e_activity_get_cancellable (priv->searching_activity);
+ g_list_foreach (clients, (GFunc) g_object_ref, NULL);
+ priv->search_pending_count = g_list_length (clients);
+ priv->search_time = new_time;
+
+ for (iter = clients; iter; iter = iter->next) {
+ ECalClient *client = iter->data;
+
+ e_cal_client_get_object_list (client, sexp, cancellable, cal_search_get_object_list_cb, cal_shell_view);
+ }
+
+ g_list_free_full (clients, g_object_unref);
+ g_free (sexp);
+}
+
+void
+e_cal_shell_view_search_events (ECalShellView *cal_shell_view,
+ gboolean search_forward)
+{
+ ECalShellViewPrivate *priv = cal_shell_view->priv;
+ ECalShellContent *cal_shell_content;
+ GnomeCalendarViewType view_type;
+ ECalendarView *calendar_view;
+ GnomeCalendar *calendar;
+ time_t start_time = 0;
+ gint range_years;
+
+ if (priv->searching_activity || !priv->search_direction)
+ e_cal_shell_view_search_stop (cal_shell_view);
+
+ cal_shell_content = cal_shell_view->priv->cal_shell_content;
+ calendar = e_cal_shell_content_get_calendar (cal_shell_content);
+ view_type = gnome_calendar_get_view (calendar);
+ calendar_view = gnome_calendar_get_calendar_view (calendar, view_type);
+
+ if (!e_calendar_view_get_selected_time_range (calendar_view, &start_time, NULL)) {
+ e_shell_view_update_actions (E_SHELL_VIEW (cal_shell_view));
+ return;
+ }
+
+ start_time = time_day_begin (start_time);
+ if (priv->search_direction) {
+ time_t cached_start, cached_end, tmp;
+
+ cached_start = priv->search_time;
+ cached_end = time_add_day (cached_start, (-1) * priv->search_direction);
+
+ if (priv->search_direction > 0) {
+ tmp = cached_start;
+ cached_start = cached_end;
+ cached_end = tmp;
+ }
+
+ /* clear cached results if searching out of cached bounds */
+ if (start_time < cached_start || start_time > cached_end)
+ e_cal_shell_view_search_stop (cal_shell_view);
+ }
+
+ priv->search_direction = search_forward ? +30 : -30;
+
+ if (cal_searching_check_candidates (cal_shell_view))
+ return;
+
+ range_years = cal_searching_get_search_range_years (cal_shell_view);
+
+ priv->search_pending_count = 0;
+ priv->search_time = start_time;
+ priv->search_min_time = start_time - (range_years * 365 * 24 * 60 * 60);
+ priv->search_max_time = start_time + (range_years * 365 * 24 * 60 * 60);
+
+ if (priv->search_min_time < 0)
+ priv->search_min_time = 0;
+ if (priv->search_hit_cache) {
+ g_slist_free_full (priv->search_hit_cache, g_free);
+ priv->search_hit_cache = NULL;
+ }
+
+ cal_iterate_searching (cal_shell_view);
+}
+
+void
+e_cal_shell_view_search_stop (ECalShellView *cal_shell_view)
+{
+ ECalShellViewPrivate *priv;
+
+ g_return_if_fail (cal_shell_view != NULL);
+ g_return_if_fail (cal_shell_view->priv != NULL);
+
+ priv = cal_shell_view->priv;
+
+ cal_searching_update_alert (cal_shell_view, NULL);
+
+ if (priv->searching_activity) {
+ g_cancellable_cancel (e_activity_get_cancellable (priv->searching_activity));
+ e_activity_set_state (priv->searching_activity, E_ACTIVITY_CANCELLED);
+ g_object_unref (priv->searching_activity);
+ priv->searching_activity = NULL;
+ }
+
+ if (priv->search_hit_cache) {
+ g_slist_free_full (priv->search_hit_cache, g_free);
+ priv->search_hit_cache = NULL;
+ }
+
+ priv->search_direction = 0;
+}