Files
evolution/modules/calendar/e-cal-shell-view-private.c
T
Milan Crha a676422ad7 GSettings Bindings rely on quarks
There is a fake change notify on timezone when either a corresponding
file changes or the setting for "use system timezone" changes,
to propagate the change in the code properly. This notify requires
proper key identification for GSettings bindings, because without it
the binding crashes on a runtime check.
2014-11-13 11:46:13 +01:00

1003 lines
29 KiB
C

/*
* e-cal-shell-view-private.c
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* 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 Lesser General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "e-util/e-util-private.h"
#include <calendar/gui/e-cal-ops.h>
#include "e-cal-shell-view-private.h"
#define CHECK_NB 5
/* be compatible with older e-d-s for MeeGo */
#ifndef ETC_TIMEZONE
# define ETC_TIMEZONE "/etc/timezone"
# define ETC_TIMEZONE_MAJ "/etc/TIMEZONE"
# define ETC_RC_CONF "/etc/rc.conf"
# define ETC_SYSCONFIG_CLOCK "/etc/sysconfig/clock"
# define ETC_CONF_D_CLOCK "/etc/conf.d/clock"
# define ETC_LOCALTIME "/etc/localtime"
#endif
static const gchar * files_to_check[CHECK_NB] = {
ETC_TIMEZONE,
ETC_TIMEZONE_MAJ,
ETC_SYSCONFIG_CLOCK,
ETC_CONF_D_CLOCK,
ETC_LOCALTIME
};
static struct tm
cal_shell_view_get_current_time (ECalendarItem *calitem,
ECalShellView *cal_shell_view)
{
ECalShellContent *cal_shell_content;
struct icaltimetype tt;
icaltimezone *timezone;
ECalModel *model;
cal_shell_content = cal_shell_view->priv->cal_shell_content;
model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
timezone = e_cal_model_get_timezone (model);
tt = icaltime_from_timet_with_zone (time (NULL), FALSE, timezone);
return icaltimetype_to_tm (&tt);
}
static void
cal_shell_view_popup_event_cb (EShellView *shell_view,
GdkEvent *button_event)
{
GList *list;
ECalendarView *view;
ECalShellViewPrivate *priv;
const gchar *widget_path;
gint n_selected;
priv = E_CAL_SHELL_VIEW_GET_PRIVATE (shell_view);
view = e_cal_shell_content_get_current_calendar_view (priv->cal_shell_content);
list = e_calendar_view_get_selected_events (view);
n_selected = g_list_length (list);
g_list_free (list);
if (n_selected <= 0)
widget_path = "/calendar-empty-popup";
else
widget_path = "/calendar-event-popup";
e_shell_view_show_popup_menu (shell_view, widget_path, button_event);
}
static gboolean
cal_shell_view_selector_popup_event_cb (EShellView *shell_view,
ESource *primary_source,
GdkEvent *button_event)
{
const gchar *widget_path;
widget_path = "/calendar-popup";
e_shell_view_show_popup_menu (shell_view, widget_path, button_event);
return TRUE;
}
static void
cal_shell_view_memopad_popup_event_cb (EShellView *shell_view,
GdkEvent *button_event)
{
const gchar *widget_path;
widget_path = "/calendar-memopad-popup";
e_shell_view_show_popup_menu (shell_view, widget_path, button_event);
}
static void
cal_shell_view_taskpad_popup_event_cb (EShellView *shell_view,
GdkEvent *button_event)
{
const gchar *widget_path;
widget_path = "/calendar-taskpad-popup";
e_shell_view_show_popup_menu (shell_view, widget_path, button_event);
}
static void
cal_shell_view_backend_error_cb (EClientCache *client_cache,
EClient *client,
EAlert *alert,
ECalShellView *cal_shell_view)
{
ECalShellContent *cal_shell_content;
ESource *source;
const gchar *extension_name;
cal_shell_content = cal_shell_view->priv->cal_shell_content;
source = e_client_get_source (client);
extension_name = E_SOURCE_EXTENSION_CALENDAR;
/* Only submit alerts from calendar backends. */
if (e_source_has_extension (source, extension_name)) {
EAlertSink *alert_sink;
alert_sink = E_ALERT_SINK (cal_shell_content);
e_alert_sink_submit_alert (alert_sink, alert);
}
}
static void
cal_shell_view_notify_view_id_cb (EShellView *shell_view)
{
GalViewInstance *view_instance;
const gchar *view_id;
view_id = e_shell_view_get_view_id (shell_view);
view_instance = e_shell_view_get_view_instance (shell_view);
/* A NULL view ID implies we're in a custom view. But you can
* only get to a custom view via the "Define Views" dialog, which
* would have already modified the view instance appropriately.
* Furthermore, there's no way to refer to a custom view by ID
* anyway, since custom views have no IDs. */
if (view_id == NULL)
return;
gal_view_instance_set_current_view_id (view_instance, view_id);
}
void
e_cal_shell_view_private_init (ECalShellView *cal_shell_view)
{
e_signal_connect_notify (
cal_shell_view, "notify::view-id",
G_CALLBACK (cal_shell_view_notify_view_id_cb), NULL);
}
static void
system_timezone_monitor_changed (GFileMonitor *handle,
GFile *file,
GFile *other_file,
GFileMonitorEvent event,
gpointer user_data)
{
GSettings *settings;
if (event != G_FILE_MONITOR_EVENT_CHANGED &&
event != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT &&
event != G_FILE_MONITOR_EVENT_DELETED &&
event != G_FILE_MONITOR_EVENT_CREATED)
return;
settings = e_util_ref_settings ("org.gnome.evolution.calendar");
/* GSettings Bindings rely on quarks */
g_signal_emit_by_name (settings, "changed::timezone",
g_quark_to_string (g_quark_from_string ("timezone")));
g_object_unref (settings);
}
static void
init_timezone_monitors (ECalShellView *view)
{
ECalShellViewPrivate *priv = view->priv;
gint i;
for (i = 0; i < CHECK_NB; i++) {
GFile *file;
file = g_file_new_for_path (files_to_check[i]);
priv->monitors[i] = g_file_monitor_file (
file, G_FILE_MONITOR_NONE, NULL, NULL);
g_object_unref (file);
if (priv->monitors[i])
g_signal_connect (
priv->monitors[i], "changed",
G_CALLBACK (system_timezone_monitor_changed),
NULL);
}
}
void
e_cal_shell_view_private_constructed (ECalShellView *cal_shell_view)
{
ECalShellViewPrivate *priv = cal_shell_view->priv;
EShellBackend *shell_backend;
EShellContent *shell_content;
EShellSidebar *shell_sidebar;
EShellWindow *shell_window;
EShellView *shell_view;
EShell *shell;
ECalendar *calendar;
gulong handler_id;
gint ii;
shell_view = E_SHELL_VIEW (cal_shell_view);
shell_backend = e_shell_view_get_shell_backend (shell_view);
shell_content = e_shell_view_get_shell_content (shell_view);
shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
shell_window = e_shell_view_get_shell_window (shell_view);
shell = e_shell_window_get_shell (shell_window);
e_shell_window_add_action_group (shell_window, "calendar");
e_shell_window_add_action_group (shell_window, "calendar-filter");
/* Cache these to avoid lots of awkward casting. */
priv->cal_shell_backend = g_object_ref (shell_backend);
priv->cal_shell_content = g_object_ref (shell_content);
priv->cal_shell_sidebar = g_object_ref (shell_sidebar);
calendar = e_cal_base_shell_sidebar_get_date_navigator (priv->cal_shell_sidebar);
/* Keep our own reference to this so we can
* disconnect our signal handlers in dispose(). */
priv->client_cache = e_shell_get_client_cache (shell);
g_object_ref (priv->client_cache);
handler_id = g_signal_connect (
priv->client_cache, "backend-error",
G_CALLBACK (cal_shell_view_backend_error_cb),
cal_shell_view);
priv->backend_error_handler_id = handler_id;
g_signal_connect_swapped (
e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (priv->cal_shell_content)),
"time-range-changed", G_CALLBACK (e_cal_shell_view_update_sidebar), cal_shell_view);
for (ii = E_CAL_VIEW_KIND_DAY; ii < E_CAL_VIEW_KIND_LAST; ii++) {
ECalendarView *calendar_view;
/* Keep our own reference to this so we can
* disconnect our signal handlers in dispose(). */
calendar_view = e_cal_shell_content_get_calendar_view (priv->cal_shell_content, ii);
priv->views[ii].calendar_view = g_object_ref (calendar_view);
handler_id = g_signal_connect_swapped (
calendar_view, "popup-event",
G_CALLBACK (cal_shell_view_popup_event_cb),
cal_shell_view);
priv->views[ii].popup_event_handler_id = handler_id;
handler_id = g_signal_connect_swapped (
calendar_view, "selection-changed",
G_CALLBACK (e_shell_view_update_actions),
cal_shell_view);
priv->views[ii].selection_changed_handler_id = handler_id;
}
/* Keep our own reference to this so we can
* disconnect our signal handlers in dispose(). */
priv->model = e_cal_base_shell_content_get_model (
E_CAL_BASE_SHELL_CONTENT (shell_content));
g_object_ref (priv->model);
/* Keep our own reference to this so we can
* disconnect our signal handlers in dispose(). */
priv->selector = e_cal_base_shell_sidebar_get_selector (
E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
g_object_ref (priv->selector);
handler_id = g_signal_connect_swapped (
priv->selector, "popup-event",
G_CALLBACK (cal_shell_view_selector_popup_event_cb),
cal_shell_view);
priv->selector_popup_event_handler_id = handler_id;
/* Keep our own reference to this so we can
* disconnect our signal handlers in dispose(). */
priv->memo_table = e_cal_shell_content_get_memo_table (
E_CAL_SHELL_CONTENT (shell_content));
g_object_ref (priv->memo_table);
handler_id = g_signal_connect_swapped (
priv->memo_table, "popup-event",
G_CALLBACK (cal_shell_view_memopad_popup_event_cb),
cal_shell_view);
priv->memo_table_popup_event_handler_id = handler_id;
handler_id = g_signal_connect_swapped (
priv->memo_table, "selection-change",
G_CALLBACK (e_cal_shell_view_memopad_actions_update),
cal_shell_view);
priv->memo_table_selection_change_handler_id = handler_id;
/* Keep our own reference to this so we can
* disconnect our signal handlers in dispose(). */
priv->task_table = e_cal_shell_content_get_task_table (
E_CAL_SHELL_CONTENT (shell_content));
g_object_ref (priv->task_table);
handler_id = g_signal_connect_swapped (
priv->task_table, "popup-event",
G_CALLBACK (cal_shell_view_taskpad_popup_event_cb),
cal_shell_view);
priv->task_table_popup_event_handler_id = handler_id;
handler_id = g_signal_connect_swapped (
priv->task_table, "selection-change",
G_CALLBACK (e_cal_shell_view_taskpad_actions_update),
cal_shell_view);
priv->task_table_selection_change_handler_id = handler_id;
e_categories_add_change_hook (
(GHookFunc) e_cal_shell_view_update_search_filter,
cal_shell_view);
e_calendar_item_set_get_time_callback (
calendar->calitem, (ECalendarItemGetTimeCallback)
cal_shell_view_get_current_time, cal_shell_view, NULL);
init_timezone_monitors (cal_shell_view);
e_cal_shell_view_actions_init (cal_shell_view);
e_cal_shell_view_update_sidebar (cal_shell_view);
e_cal_shell_view_update_search_filter (cal_shell_view);
}
void
e_cal_shell_view_private_dispose (ECalShellView *cal_shell_view)
{
ECalShellViewPrivate *priv = cal_shell_view->priv;
gint ii;
e_cal_shell_view_search_stop (cal_shell_view);
/* Calling ECalShellContent's save state from here,
* because it is too late in its own dispose(). */
if (priv->cal_shell_content != NULL)
e_cal_shell_content_save_state (priv->cal_shell_content);
if (priv->prepare_for_quit_handler_id > 0) {
g_signal_handler_disconnect (
priv->shell,
priv->prepare_for_quit_handler_id);
priv->prepare_for_quit_handler_id = 0;
}
if (priv->backend_error_handler_id > 0) {
g_signal_handler_disconnect (
priv->client_cache,
priv->backend_error_handler_id);
priv->backend_error_handler_id = 0;
}
if (priv->selector_popup_event_handler_id > 0) {
g_signal_handler_disconnect (
priv->selector,
priv->selector_popup_event_handler_id);
priv->selector_popup_event_handler_id = 0;
}
if (priv->memo_table_popup_event_handler_id > 0) {
g_signal_handler_disconnect (
priv->memo_table,
priv->memo_table_popup_event_handler_id);
priv->memo_table_popup_event_handler_id = 0;
}
if (priv->memo_table_selection_change_handler_id > 0) {
g_signal_handler_disconnect (
priv->memo_table,
priv->memo_table_selection_change_handler_id);
priv->memo_table_selection_change_handler_id = 0;
}
if (priv->task_table_popup_event_handler_id > 0) {
g_signal_handler_disconnect (
priv->task_table,
priv->task_table_popup_event_handler_id);
priv->task_table_popup_event_handler_id = 0;
}
if (priv->task_table_selection_change_handler_id > 0) {
g_signal_handler_disconnect (
priv->task_table,
priv->task_table_selection_change_handler_id);
priv->task_table_selection_change_handler_id = 0;
}
for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) {
if (priv->views[ii].popup_event_handler_id > 0) {
g_signal_handler_disconnect (
priv->views[ii].calendar_view,
priv->views[ii].popup_event_handler_id);
priv->views[ii].popup_event_handler_id = 0;
}
if (priv->views[ii].selection_changed_handler_id > 0) {
g_signal_handler_disconnect (
priv->views[ii].calendar_view,
priv->views[ii].selection_changed_handler_id);
priv->views[ii].selection_changed_handler_id = 0;
}
g_clear_object (&priv->views[ii].calendar_view);
}
g_clear_object (&priv->cal_shell_backend);
g_clear_object (&priv->cal_shell_content);
g_clear_object (&priv->cal_shell_sidebar);
g_clear_object (&priv->shell);
g_clear_object (&priv->client_cache);
g_clear_object (&priv->model);
g_clear_object (&priv->selector);
g_clear_object (&priv->memo_table);
g_clear_object (&priv->task_table);
for (ii = 0; ii < CHECK_NB; ii++)
g_clear_object (&priv->monitors[ii]);
}
void
e_cal_shell_view_private_finalize (ECalShellView *cal_shell_view)
{
/* XXX Nothing to do? */
}
void
e_cal_shell_view_update_sidebar (ECalShellView *cal_shell_view)
{
EShellView *shell_view;
EShellSidebar *shell_sidebar;
ECalShellContent *cal_shell_content;
ECalendarView *calendar_view;
gchar *description;
g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view));
shell_view = E_SHELL_VIEW (cal_shell_view);
shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
cal_shell_content = cal_shell_view->priv->cal_shell_content;
calendar_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
description = e_calendar_view_get_description_text (calendar_view);
e_shell_sidebar_set_secondary_text (shell_sidebar, description ? description : "");
g_free (description);
}
static gint
cal_searching_get_search_range_years (ECalShellView *cal_shell_view)
{
GSettings *settings;
gint search_range_years;
settings = e_util_ref_settings ("org.gnome.evolution.calendar");
search_range_years =
g_settings_get_int (settings, "search-range-years");
if (search_range_years <= 0)
search_range_years = 10;
g_object_unref (settings);
return search_range_years;
}
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)
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;
e_cal_client_get_timezone_sync (
gid->client, dt.tzid, &zone, gid->cancellable, 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);
e_cal_client_get_object_list_finish (
client, result, &icalcomps, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_warn_if_fail (icalcomps == NULL);
g_error_free (error);
} else if (error != NULL || !icalcomps) {
g_warn_if_fail (icalcomps == NULL);
g_clear_error (&error);
cal_shell_view->priv->search_pending_count--;
if (!cal_shell_view->priv->search_pending_count) {
cal_iterate_searching (cal_shell_view);
}
} else if (cal_shell_view->priv->searching_activity) {
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;
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);
} else {
e_cal_client_free_icalcomp_slist (icalcomps);
}
}
static gboolean
cal_searching_check_candidates (ECalShellView *cal_shell_view)
{
ECalShellContent *cal_shell_content;
ECalendarView *calendar_view;
ECalViewKind view_kind;
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_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
view_kind = e_cal_shell_content_get_current_view_id (cal_shell_content);
if (!e_calendar_view_get_selected_time_range (calendar_view, &value, NULL))
return FALSE;
if (cal_shell_view->priv->search_direction > 0 &&
(view_kind == E_CAL_VIEW_KIND_WEEK ||
view_kind == E_CAL_VIEW_KIND_MONTH))
value = time_add_day (value, 1);
cal_shell_view->priv->search_hit_cache =
g_slist_sort (
cal_shell_view->priv->search_hit_cache,
cal_time_t_ptr_compare);
for (iter = cal_shell_view->priv->search_hit_cache; iter; iter = iter->next) {
time_t cache = *((time_t *) iter->data);
/* list is sorted before traversing it */
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) {
struct icaltimetype tt;
icaltimezone *zone;
ECalDataModel *data_model;
ECalendar *calendar;
calendar = e_cal_base_shell_sidebar_get_date_navigator (cal_shell_view->priv->cal_shell_sidebar);
data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_view->priv->cal_shell_content));
zone = e_cal_data_model_get_timezone (data_model);
if (zone)
tt = icaltime_from_timet_with_zone (candidate, FALSE, zone);
else
tt = icaltime_from_timet (candidate, FALSE);
if (icaltime_is_valid_time (tt) && !icaltime_is_null_time (tt)) {
ECalendarView *cal_view;
GDate *dt;
dt = g_date_new_dmy (tt.day, tt.month, tt.year);
e_calendar_item_set_selection (calendar->calitem, dt, dt);
g_signal_emit_by_name (calendar->calitem, "selection-changed", 0);
g_date_free (dt);
cal_view = e_cal_shell_content_get_current_calendar_view (cal_shell_view->priv->cal_shell_content);
e_calendar_view_set_selected_time_range (cal_view, candidate, 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 *list, *link;
ECalDataModel *data_model;
time_t new_time, range1, range2;
icaltimezone *timezone;
const gchar *default_tzloc = NULL;
GCancellable *cancellable;
gchar *sexp, *start, *end, *data_filter;
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;
}
e_shell_view_update_actions (E_SHELL_VIEW (cal_shell_view));
return;
}
if (!priv->searching_activity) {
EShellBackend *shell_backend;
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;
}
data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_view->priv->cal_shell_content));
list = e_cal_data_model_get_clients (data_model);
if (list == NULL) {
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_data_model_get_timezone (data_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 = "";
data_filter = e_cal_data_model_dup_filter (data_model);
sexp = g_strdup_printf (
"(and %s (occur-in-time-range? "
"(make-time \"%s\") "
"(make-time \"%s\") \"%s\"))",
data_filter, start, end, default_tzloc);
g_free (data_filter);
g_free (start);
g_free (end);
cancellable = e_activity_get_cancellable (priv->searching_activity);
priv->search_pending_count = g_list_length (list);
priv->search_time = new_time;
for (link = list; link != NULL; link = g_list_next (link)) {
ECalClient *client = E_CAL_CLIENT (link->data);
e_cal_client_get_object_list (
client, sexp, cancellable,
cal_search_get_object_list_cb, cal_shell_view);
}
g_list_free_full (list, g_object_unref);
g_free (sexp);
e_shell_view_update_actions (E_SHELL_VIEW (cal_shell_view));
}
void
e_cal_shell_view_search_events (ECalShellView *cal_shell_view,
gboolean search_forward)
{
ECalShellViewPrivate *priv = cal_shell_view->priv;
ECalShellContent *cal_shell_content;
ECalendarView *calendar_view;
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_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
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)) {
e_shell_view_update_actions (E_SHELL_VIEW (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;
}