[evolution] Bug #584143 - Global Search Function withing Calendar



commit 49787d04feef8ff699326e54d5991a36d8ef74bb
Author: Milan Crha <mcrha redhat com>
Date:   Wed Apr 11 16:58:05 2012 +0200

    Bug #584143 - Global Search Function withing Calendar

 calendar/calendar.error.xml                      |    4 +
 calendar/gui/e-day-view.c                        |    3 +
 data/org.gnome.evolution.calendar.gschema.xml.in |    5 +
 modules/calendar/e-cal-shell-settings.c          |    5 +
 modules/calendar/e-cal-shell-view-actions.c      |   42 ++
 modules/calendar/e-cal-shell-view-actions.h      |    6 +
 modules/calendar/e-cal-shell-view-private.c      |  462 ++++++++++++++++++++++
 modules/calendar/e-cal-shell-view-private.h      |   14 +
 modules/calendar/e-cal-shell-view.c              |   24 ++-
 ui/evolution-calendars.ui                        |    9 +
 ui/evolution-shell.ui                            |    1 +
 11 files changed, 574 insertions(+), 1 deletions(-)
---
diff --git a/calendar/calendar.error.xml b/calendar/calendar.error.xml
index 236f247..9301d8b 100644
--- a/calendar/calendar.error.xml
+++ b/calendar/calendar.error.xml
@@ -314,4 +314,8 @@
  <_secondary>You must be working online to complete this operation.</_secondary>
  </error>
 
+ <error id="search-error-generic" type="error">
+  <secondary>{0}</secondary>
+ </error>
+
 </error-list>
diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c
index 8969450..7b4fecf 100644
--- a/calendar/gui/e-day-view.c
+++ b/calendar/gui/e-day-view.c
@@ -2515,6 +2515,9 @@ e_day_view_set_selected_time_range (ECalendarView *cal_view,
 	work_day_start_hour = e_cal_model_get_work_day_start_hour (model);
 	work_day_start_minute = e_cal_model_get_work_day_start_minute (model);
 
+	if (start_time == end_time)
+		end_time += e_calendar_view_get_time_divisions (cal_view) * 60;
+
 	/* Set the selection. */
 	start_in_grid = e_day_view_convert_time_to_grid_position (day_view,
 								  start_time,
diff --git a/data/org.gnome.evolution.calendar.gschema.xml.in b/data/org.gnome.evolution.calendar.gschema.xml.in
index cc66fbb..638a528 100644
--- a/data/org.gnome.evolution.calendar.gschema.xml.in
+++ b/data/org.gnome.evolution.calendar.gschema.xml.in
@@ -215,6 +215,11 @@
       <_summary>Recurrent Events in Italic</_summary>
       <_description>Show days with recurrent events in italic font in bottom left calendar</_description>
     </key>
+    <key name="search-range-years" type="i">
+      <default>10</default>
+      <_summary>Search range for time-based searching in years</_summary>
+      <_description>How many years can the time-based search go forward or backward from currently selected day when searching for another occurrence; default is ten years</_description>
+    </key>
     <key name="selected-calendars" type="as">
       <default>[]</default>
       <_summary>List of selected calendars</_summary>
diff --git a/modules/calendar/e-cal-shell-settings.c b/modules/calendar/e-cal-shell-settings.c
index 392bb84..045ca28 100644
--- a/modules/calendar/e-cal-shell-settings.c
+++ b/modules/calendar/e-cal-shell-settings.c
@@ -624,6 +624,11 @@ e_cal_shell_backend_init_settings (EShell *shell)
 		"recur-events-italic");
 
 	e_shell_settings_install_property_for_key (
+		"cal-search-range-years",
+		CALENDAR_SCHEMA,
+		"search-range-years");
+
+	e_shell_settings_install_property_for_key (
 		"cal-show-event-end-times",
 		CALENDAR_SCHEMA,
 		"show-event-end");
diff --git a/modules/calendar/e-cal-shell-view-actions.c b/modules/calendar/e-cal-shell-view-actions.c
index 9567665..d73feef 100644
--- a/modules/calendar/e-cal-shell-view-actions.c
+++ b/modules/calendar/e-cal-shell-view-actions.c
@@ -421,6 +421,27 @@ action_calendar_rename_cb (GtkAction *action,
 }
 
 static void
+action_calendar_search_next_cb (GtkAction *action,
+				ECalShellView *cal_shell_view)
+{
+	e_cal_shell_view_search_events (cal_shell_view, TRUE);
+}
+
+static void
+action_calendar_search_prev_cb (GtkAction *action,
+				ECalShellView *cal_shell_view)
+{
+	e_cal_shell_view_search_events (cal_shell_view, FALSE);
+}
+
+static void
+action_calendar_search_stop_cb (GtkAction *action,
+				ECalShellView *cal_shell_view)
+{
+	e_cal_shell_view_search_stop (cal_shell_view);
+}
+
+static void
 action_calendar_select_one_cb (GtkAction *action,
                                ECalShellView *cal_shell_view)
 {
@@ -1407,6 +1428,27 @@ static GtkActionEntry calendar_entries[] = {
 	  N_("Rename the selected calendar"),
 	  G_CALLBACK (action_calendar_rename_cb) },
 
+	{ "calendar-search-next",
+	  GTK_STOCK_GO_FORWARD,
+	  N_("Find _next"),
+	  "<Control><Shift>n",
+	  N_("Find next occurrence of the current search string"),
+	  G_CALLBACK (action_calendar_search_next_cb) },
+
+	{ "calendar-search-prev",
+	  GTK_STOCK_GO_BACK,
+	  N_("Find _previous"),
+	  "<Control><Shift>p",
+	  N_("Find previous occurrence of the current search string"),
+	  G_CALLBACK (action_calendar_search_prev_cb) },
+
+	{ "calendar-search-stop",
+	  GTK_STOCK_STOP,
+	  N_("Stop _running search"),
+	  NULL,
+	  N_("Stop currently running search"),
+	  G_CALLBACK (action_calendar_search_stop_cb) },
+
 	{ "calendar-select-one",
 	  "stock_check-filled",
 	  N_("Show _Only This Calendar"),
diff --git a/modules/calendar/e-cal-shell-view-actions.h b/modules/calendar/e-cal-shell-view-actions.h
index fcc77ea..ef2c520 100644
--- a/modules/calendar/e-cal-shell-view-actions.h
+++ b/modules/calendar/e-cal-shell-view-actions.h
@@ -51,6 +51,12 @@
 	E_SHELL_WINDOW_ACTION ((window), "calendar-refresh")
 #define E_SHELL_WINDOW_ACTION_CALENDAR_RENAME(window) \
 	E_SHELL_WINDOW_ACTION ((window), "calendar-rename")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_SEARCH_PREV(window) \
+	E_SHELL_WINDOW_ACTION ((window), "calendar-search-prev")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_SEARCH_NEXT(window) \
+	E_SHELL_WINDOW_ACTION ((window), "calendar-search-next")
+#define E_SHELL_WINDOW_ACTION_CALENDAR_SEARCH_STOP(window) \
+	E_SHELL_WINDOW_ACTION ((window), "calendar-search-stop")
 #define E_SHELL_WINDOW_ACTION_CALENDAR_SELECT_ONE(window) \
 	E_SHELL_WINDOW_ACTION ((window), "calendar-select-one")
 #define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_DAY(window) \
diff --git a/modules/calendar/e-cal-shell-view-private.c b/modules/calendar/e-cal-shell-view-private.c
index 818ee30..fc94a83 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;
+}
diff --git a/modules/calendar/e-cal-shell-view-private.h b/modules/calendar/e-cal-shell-view-private.h
index ad4984a..4fc5e24 100644
--- a/modules/calendar/e-cal-shell-view-private.h
+++ b/modules/calendar/e-cal-shell-view-private.h
@@ -122,6 +122,15 @@ struct _ECalShellViewPrivate {
 	EActivity *memopad_activity;
 	EActivity *taskpad_activity;
 
+	/* Time-range searching */
+	EActivity *searching_activity;
+	gpointer search_alert; /* weak pointer to EAlert * */
+	gint search_pending_count; /* how many clients are pending */
+	time_t search_time; /* current search time from */
+	time_t search_min_time, search_max_time; /* time boundary for searching */
+	gint search_direction; /* negative value is backward, positive is forward, zero is error; in days */
+	GSList *search_hit_cache; /* pointers on time_t for matched events */
+
         GFileMonitor *monitors[CHECK_NB];
 };
 
@@ -159,6 +168,11 @@ void		e_cal_shell_view_update_search_filter
 					(ECalShellView *cal_shell_view);
 void		e_cal_shell_view_update_timezone
 					(ECalShellView *cal_shell_view);
+void		e_cal_shell_view_search_events
+					(ECalShellView *cal_shell_view,
+					 gboolean search_forward);
+void		e_cal_shell_view_search_stop
+					(ECalShellView *cal_shell_view);
 
 /* Memo Pad Utilities */
 
diff --git a/modules/calendar/e-cal-shell-view.c b/modules/calendar/e-cal-shell-view.c
index ad3477c..9554f74 100644
--- a/modules/calendar/e-cal-shell-view.c
+++ b/modules/calendar/e-cal-shell-view.c
@@ -81,6 +81,8 @@ cal_shell_view_execute_search (EShellView *shell_view)
 	gchar *temp;
 	gint value;
 
+	e_cal_shell_view_search_stop (E_CAL_SHELL_VIEW (shell_view));
+
 	shell_window = e_shell_view_get_shell_window (shell_view);
 	shell_content = e_shell_view_get_shell_content (shell_view);
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
@@ -239,6 +241,9 @@ cal_shell_view_execute_search (EShellView *shell_view)
 	gnome_calendar_set_search_query (
 		calendar, query, range_search, start_range, end_range);
 	g_free (query);
+
+	/* update also actions, thus Find Prev/Next/Stop will be sensitive as expected */
+	e_shell_view_update_actions (shell_view);
 }
 
 static icalproperty *
@@ -318,9 +323,12 @@ cal_shell_view_update_actions (EShellView *shell_view)
 	EShell *shell;
 	GnomeCalendarViewType view_type;
 	GnomeCalendar *calendar;
+	ECalModel *model;
 	ECalendarView *view;
 	GtkAction *action;
 	GList *list, *iter;
+	const gchar *model_sexp;
+	gboolean is_searching;
 	gboolean sensitive;
 	gboolean visible;
 	guint32 state;
@@ -364,6 +372,11 @@ cal_shell_view_update_actions (EShellView *shell_view)
 	calendar = e_cal_shell_content_get_calendar (cal_shell_content);
 	view_type = gnome_calendar_get_view (calendar);
 	view = gnome_calendar_get_calendar_view (calendar, view_type);
+	model = gnome_calendar_get_model (calendar);
+	model_sexp = e_cal_model_get_search_query (model);
+	is_searching = model_sexp && *model_sexp &&
+		g_strcmp0 (model_sexp, "#t") != 0 &&
+		g_strcmp0 (model_sexp, "(contains? \"summary\"  \"\")") != 0;
 
 	list = e_calendar_view_get_selected_events (view);
 	n_selected = g_list_length (list);
@@ -452,6 +465,15 @@ cal_shell_view_update_actions (EShellView *shell_view)
 	sensitive = can_delete_primary_source;
 	gtk_action_set_sensitive (action, sensitive);
 
+	action = ACTION (CALENDAR_SEARCH_PREV);
+	gtk_action_set_sensitive (action, is_searching);
+
+	action = ACTION (CALENDAR_SEARCH_NEXT);
+	gtk_action_set_sensitive (action, is_searching);
+
+	action = ACTION (CALENDAR_SEARCH_STOP);
+	gtk_action_set_sensitive (action, is_searching && priv->searching_activity != NULL);
+
 	action = ACTION (EVENT_DELEGATE);
 	sensitive =
 		(n_selected == 1) && editable &&
@@ -511,7 +533,7 @@ cal_shell_view_update_actions (EShellView *shell_view)
 
 static void
 cal_shell_view_class_init (ECalShellViewClass *class,
-                            GTypeModule *type_module)
+                           GTypeModule *type_module)
 {
 	GObjectClass *object_class;
 	EShellViewClass *shell_view_class;
diff --git a/ui/evolution-calendars.ui b/ui/evolution-calendars.ui
index 496971b..70cb095 100644
--- a/ui/evolution-calendars.ui
+++ b/ui/evolution-calendars.ui
@@ -31,6 +31,15 @@
         <menuitem action='calendar-purge'/>
       </menu>
     </placeholder>
+    <menu action='search-menu'>
+      <placeholder name='search-extra-actions'>
+        <separator/>
+        <menuitem action='calendar-search-prev'/>
+        <menuitem action='calendar-search-next'/>
+        <menuitem action='calendar-search-stop'/>
+        <separator/>
+      </placeholder>
+    </menu>
   </menubar>
   <toolbar name='main-toolbar'>
 #if !EXPRESS
diff --git a/ui/evolution-shell.ui b/ui/evolution-shell.ui
index fd90ba8..ae7ce84 100644
--- a/ui/evolution-shell.ui
+++ b/ui/evolution-shell.ui
@@ -70,6 +70,7 @@
     <menu action='search-menu'>
       <menuitem action='search-quick'/>
       <menuitem action='search-clear'/>
+      <placeholder name='search-extra-actions'/>
       <menuitem action='search-advanced'/>
       <separator/>
       <menuitem action='search-save'/>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]