diff options
-rw-r--r-- | calendar/ChangeLog | 46 | ||||
-rw-r--r-- | calendar/cal-client/cal-query.c | 8 | ||||
-rw-r--r-- | calendar/cal-util/timeutil.c | 75 | ||||
-rw-r--r-- | calendar/cal-util/timeutil.h | 1 | ||||
-rw-r--r-- | calendar/gui/calendar-commands.c | 10 | ||||
-rw-r--r-- | calendar/gui/e-day-view.c | 283 | ||||
-rw-r--r-- | calendar/gui/e-day-view.h | 20 | ||||
-rw-r--r-- | calendar/gui/e-week-view.c | 282 | ||||
-rw-r--r-- | calendar/gui/e-week-view.h | 10 | ||||
-rw-r--r-- | calendar/pcs/cal.c | 17 | ||||
-rw-r--r-- | calendar/pcs/query.c | 194 |
11 files changed, 662 insertions, 284 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 74b9069362..8498890735 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,49 @@ +2001-04-12 Federico Mena Quintero <federico@ximian.com> + + * gui/e-day-view.c (update_query): New function to restart a query + for the day view. + (query_obj_updated_cb): Renamed from obj_updated_cb(); updated for + queries instead of calendar clients. + (query_obj_removed_cb): Likewise. + (cal_opened_cb): Just update_query() instead of queueing reloading + all the events. + (e_day_view_set_cal_client): Likewise. + (e_day_view_set_query): Likewise. + (e_day_view_set_selected_time_range): Likewise. + (e_day_view_set_days_shown): Likewise. + (e_day_view_recalc_work_week): Likewise. + (e_day_view_queue_reload_events): Removed function now that events + are updated entirely by the query. + (e_day_view_reload_events_idle_cb): Likewise. + (e_day_view_reload_events): Likewise. + (e_day_view_init): Use a pretty arrow instead of GDK_TOP_LEFT_ARROW. + + * gui/e-week-view.c: Analogous changes to the ones in e-day-view.c. + (e_week_view_init): Use a pretty arrow instead of GDK_TOP_LEFT_ARROW. + + * cal-util/timeutil.c (isodate_from_time_t): Return a g_strdup()ed + version of the string instead of a pointer to a static buffer. + (time_from_isodate): Resurrected function. Polished up to our + current standards of paranoia. + + * pcs/query.c (func_time_now): New function (time-now). + (func_make_time): New function (make-time ISODATE). + (func_time_add_day): New function (time-add-day TIME N). + (func_time_day_begin): New function (time-day-begin TIME). + (func_time_day_end): New function (time-day-end TIME). + (func_occur_in_time_range): Use time_t values instead of ints. + (match_component): Free the stringized component. Free the ESexp + result value. + + * gui/e-day-view.h: Removed a couple of unused prototypes. + + * pcs/query.c (query_destroy): Oops, disconnect from the backend. + + * pcs/cal.c (Cal_get_query): Duplicate the query reference before + we return it. + + * gui/calendar-commands.c (pixmaps): Fixed paths to image files. + 2001-04-11 JP Rosevear <jpr@ximian.com> * pcs/cal-backend-file.c (cal_backend_file_compute_changes): diff --git a/calendar/cal-client/cal-query.c b/calendar/cal-client/cal-query.c index 1a3fd241f9..727a2dcb3b 100644 --- a/calendar/cal-client/cal-query.c +++ b/calendar/cal-client/cal-query.c @@ -207,7 +207,7 @@ cal_query_destroy (GtkObject *object) /* Marshalers */ -typedef void (* ObjUpdatedFunc) (QueryListener *ql, const char *uid, +typedef void (* ObjUpdatedFunc) (CalQuery *query, const char *uid, gboolean query_in_progress, int n_scanned, int total, gpointer data); @@ -218,12 +218,12 @@ marshal_obj_updated (GtkObject *object, GtkSignalFunc func, gpointer func_data, f = (ObjUpdatedFunc) func; - (* f) (QUERY_LISTENER (object), GTK_VALUE_STRING (args[0]), + (* f) (CAL_QUERY (object), GTK_VALUE_STRING (args[0]), GTK_VALUE_BOOL (args[1]), GTK_VALUE_INT (args[2]), GTK_VALUE_INT (args[3]), func_data); } -typedef void (* QueryDoneFunc) (QueryListener *ql, CalQueryDoneStatus status, const char *error_str, +typedef void (* QueryDoneFunc) (CalQuery *query, CalQueryDoneStatus status, const char *error_str, gpointer data); static void @@ -233,7 +233,7 @@ marshal_query_done (GtkObject *object, GtkSignalFunc func, gpointer func_data, G f = (QueryDoneFunc) func; - (* f) (QUERY_LISTENER (object), GTK_VALUE_ENUM (args[0]), GTK_VALUE_STRING (args[1]), + (* f) (CAL_QUERY (object), GTK_VALUE_ENUM (args[0]), GTK_VALUE_STRING (args[1]), func_data); } diff --git a/calendar/cal-util/timeutil.c b/calendar/cal-util/timeutil.c index b48a02b5fa..2d769623aa 100644 --- a/calendar/cal-util/timeutil.c +++ b/calendar/cal-util/timeutil.c @@ -9,6 +9,7 @@ */ #include <string.h> +#include <ctype.h> #include <glib.h> #include "timeutil.h" @@ -24,15 +25,85 @@ print_time_t (time_t t) tm->tm_hour, tm->tm_min, tm->tm_sec); } +/** + * isodate_from_time_t: + * @t: A time value. + * + * Creates an ISO 8601 local time representation from a time value. + * + * Return value: String with the ISO 8601 representation of the local time. + **/ char * isodate_from_time_t (time_t t) { struct tm *tm; - static char isotime [40]; + char isotime[40]; tm = localtime (&t); strftime (isotime, sizeof (isotime)-1, "%Y%m%dT%H%M%S", tm); - return isotime; + return g_strdup (isotime); +} + +/** + * time_from_isodate: + * @str: Date/time value in ISO 8601 format. + * + * Converts an ISO 8601 time string into a time_t value. + * + * Return value: Time_t corresponding to the specified ISO string. + **/ +time_t +time_from_isodate (const char *str) +{ + int len; + struct tm my_tm; + time_t t; + int i; + + g_return_val_if_fail (str != NULL, -1); + + /* yyyymmdd[Thhmmss[Z]] */ + + len = strlen (str); + + if (!(len == 8 || len == 15 || len == 16)) + return -1; + + for (i = 0; i < len; i++) + if (!((i != 8 && i != 15 && isdigit (str[i])) + || (i == 8 && str[i] == 'T') + || (i == 15 && str[i] == 'Z'))) + return -1; + + memset (&my_tm, 0, sizeof (my_tm)); + +#define digit_at(x,y) (x[y] - '0') + + my_tm.tm_year = (digit_at (str, 0) * 1000 + digit_at (str, 1) * 100 + + digit_at (str, 2) * 10 + digit_at (str, 3)) - 1900; + + my_tm.tm_mon = digit_at (str, 4) * 10 + digit_at (str, 5) - 1; + my_tm.tm_mday = digit_at (str, 6) * 10 + digit_at (str, 7); + + if (len > 8) { + my_tm.tm_hour = digit_at (str, 9) * 10 + digit_at (str, 10); + my_tm.tm_min = digit_at (str, 11) * 10 + digit_at (str, 12); + my_tm.tm_sec = digit_at (str, 13) * 10 + digit_at (str, 14); + } + + my_tm.tm_isdst = -1; + + t = mktime (&my_tm); + + if (len == 16) { +#if defined(HAVE_TM_GMTOFF) + t -= my_tm.tm_gmtoff; +#elsif defined(HAVE_TIMEZONE) + t -= timezone; +#endif + } + + return t; } time_t diff --git a/calendar/cal-util/timeutil.h b/calendar/cal-util/timeutil.h index 730c7dc089..04f1ca4867 100644 --- a/calendar/cal-util/timeutil.h +++ b/calendar/cal-util/timeutil.h @@ -17,6 +17,7 @@ char *isodate_from_time_t (time_t t); +time_t time_from_isodate (const char *str); time_t time_add_minutes (time_t time, int minutes); time_t time_add_day (time_t time, int days); diff --git a/calendar/gui/calendar-commands.c b/calendar/gui/calendar-commands.c index d31d252dfd..3556554d82 100644 --- a/calendar/gui/calendar-commands.c +++ b/calendar/gui/calendar-commands.c @@ -387,12 +387,12 @@ static EPixmap pixmaps [] = E_PIXMAP ("/menu/File/Print/Print Preview", "print-preview.xpm"), E_PIXMAP ("/menu/Actions/Component/CalendarNew", "new_appointment.xpm"), E_PIXMAP ("/menu/Tools/Component/CalendarPreferences", "configure_16_calendar.xpm"), - E_PIXMAP ("/Toolbar/New", "buttons/new_appointment.png"), - E_PIXMAP ("/Toolbar/DayView", "buttons/dayview.xpm"), - E_PIXMAP ("/Toolbar/WorkWeekView", "buttons/workweekview.xpm"), - E_PIXMAP ("/Toolbar/WeekView", "buttons/weekview.xpm"), - E_PIXMAP ("/Toolbar/MonthView", "buttons/monthview.xpm"), + E_PIXMAP ("/Toolbar/New", "buttons/new_appointment.png"), + E_PIXMAP ("/Toolbar/DayView", "buttons/dayview.xpm"), + E_PIXMAP ("/Toolbar/WorkWeekView", "buttons/workweekview.xpm"), + E_PIXMAP ("/Toolbar/WeekView", "buttons/weekview.xpm"), + E_PIXMAP ("/Toolbar/MonthView", "buttons/monthview.xpm"), E_PIXMAP_END }; diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c index 828eff3419..b10382a3ea 100644 --- a/calendar/gui/e-day-view.c +++ b/calendar/gui/e-day-view.c @@ -253,9 +253,6 @@ static void e_day_view_foreach_event_with_uid (EDayView *day_view, EDayViewForeachEventCallback callback, gpointer data); -static void e_day_view_queue_reload_events (EDayView *day_view); -static gboolean e_day_view_reload_events_idle_cb (gpointer data); -static void e_day_view_reload_events (EDayView *day_view); static void e_day_view_free_events (EDayView *day_view); static void e_day_view_free_event_array (EDayView *day_view, GArray *array); @@ -488,13 +485,14 @@ e_day_view_init (EDayView *day_view) day_view->calendar = NULL; day_view->client = NULL; + day_view->sexp = g_strdup ("#t"); /* match all by default */ + day_view->query = NULL; day_view->long_events = g_array_new (FALSE, FALSE, sizeof (EDayViewEvent)); day_view->long_events_sorted = TRUE; day_view->long_events_need_layout = FALSE; day_view->long_events_need_reshape = FALSE; - day_view->reload_events_idle_id = 0; for (day = 0; day < E_DAY_VIEW_MAX_DAYS; day++) { day_view->events[day] = g_array_new (FALSE, FALSE, @@ -794,7 +792,7 @@ e_day_view_init (EDayView *day_view) /* Create the cursors. */ - day_view->normal_cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW); + day_view->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR); day_view->move_cursor = gdk_cursor_new (GDK_FLEUR); day_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); day_view->resize_height_cursor = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW); @@ -851,17 +849,23 @@ e_day_view_destroy (GtkObject *object) e_day_view_stop_auto_scroll (day_view); - if (day_view->reload_events_idle_id != 0) { - g_source_remove (day_view->reload_events_idle_id); - day_view->reload_events_idle_id = 0; - } - if (day_view->client) { gtk_signal_disconnect_by_data (GTK_OBJECT (day_view->client), day_view); gtk_object_unref (GTK_OBJECT (day_view->client)); day_view->client = NULL; } + if (day_view->sexp) { + g_free (day_view->sexp); + day_view->sexp = NULL; + } + + if (day_view->query) { + gtk_signal_disconnect_by_data (GTK_OBJECT (day_view->query), day_view); + gtk_object_unref (GTK_OBJECT (day_view->query)); + day_view->query = NULL; + } + if (day_view->large_font) gdk_font_unref (day_view->large_font); @@ -1372,23 +1376,11 @@ e_day_view_set_calendar (EDayView *day_view, } -/* Callback used when the calendar client finishes opening */ -static void -cal_opened_cb (CalClient *client, CalClientOpenStatus status, gpointer data) -{ - EDayView *day_view; - - day_view = E_DAY_VIEW (data); - - if (status != CAL_CLIENT_OPEN_SUCCESS) - return; - - e_day_view_queue_reload_events (day_view); -} - -/* Callback used when the calendar client tells us that an object changed */ +/* Callback used when a component is updated in the live query */ static void -obj_updated_cb (CalClient *client, const char *uid, gpointer data) +query_obj_updated_cb (CalQuery *query, const char *uid, + gboolean query_in_progress, int n_scanned, int total, + gpointer data) { EDayView *day_view; EDayViewEvent *event; @@ -1396,13 +1388,8 @@ obj_updated_cb (CalClient *client, const char *uid, gpointer data) CalClientGetStatus status; gint day, event_num; - g_return_if_fail (E_IS_DAY_VIEW (data)); - day_view = E_DAY_VIEW (data); - /* Sanity check. */ - g_return_if_fail (client == day_view->client); - /* If our time hasn't been set yet, just return. */ if (day_view->lower == 0 && day_view->upper == 0) return; @@ -1416,7 +1403,7 @@ obj_updated_cb (CalClient *client, const char *uid, gpointer data) break; case CAL_CLIENT_GET_SYNTAX_ERROR: - g_message ("obj_updated_cb(): Syntax error when getting object `%s'", uid); + g_message ("query_obj_updated_cb(): Syntax error when getting object `%s'", uid); return; case CAL_CLIENT_GET_NOT_FOUND: @@ -1428,12 +1415,6 @@ obj_updated_cb (CalClient *client, const char *uid, gpointer data) return; } - /* We only care about events. */ - if (cal_component_get_vtype (comp) != CAL_COMPONENT_EVENT) { - gtk_object_unref (GTK_OBJECT (comp)); - return; - } - /* If the event already exists and the dates didn't change, we can update the event fairly easily without changing the events arrays or computing a new layout. */ @@ -1481,10 +1462,9 @@ obj_updated_cb (CalClient *client, const char *uid, gpointer data) gtk_widget_queue_draw (day_view->main_canvas); } - -/* Callback used when the calendar client tells us that an object was removed */ +/* Callback used when a component is removed from the live query */ static void -obj_removed_cb (CalClient *client, const char *uid, gpointer data) +query_obj_removed_cb (CalQuery *query, const char *uid, gpointer data) { EDayView *day_view; @@ -1498,6 +1478,117 @@ obj_removed_cb (CalClient *client, const char *uid, gpointer data) gtk_widget_queue_draw (day_view->main_canvas); } +/* Callback used when a query ends */ +static void +query_query_done_cb (CalQuery *query, CalQueryDoneStatus status, const char *error_str, gpointer data) +{ + EDayView *day_view; + + day_view = E_DAY_VIEW (data); + + /* FIXME */ + + if (status != CAL_QUERY_DONE_SUCCESS) + fprintf (stderr, "query done: %s\n", error_str); +} + +/* Callback used when an evaluation error occurs when running a query */ +static void +query_eval_error_cb (CalQuery *query, const char *error_str, gpointer data) +{ + EDayView *day_view; + + day_view = E_DAY_VIEW (data); + + /* FIXME */ + + fprintf (stderr, "eval error: %s\n", error_str); +} + + +/* Builds a complete query sexp for the day view by adding the predicates to + * filter only for VEVENTS that fit in the day view's time range. + */ +static char * +adjust_query_sexp (EDayView *day_view, const char *sexp) +{ + char *start, *end; + char *new_sexp; + + /* If the dates have not been set yet, we just want an empty query. */ + if (day_view->lower == 0 || day_view->upper == 0) + return g_strdup ("#f"); + + start = isodate_from_time_t (day_view->lower); + end = isodate_from_time_t (day_view->upper); + + new_sexp = g_strdup_printf ("(and (= (get-vtype) \"VEVENT\")" + " (occur-in-time-range? (make-time \"%s\")" + " (make-time \"%s\"))" + " %s)", + start, end, + sexp); + + g_free (start); + g_free (end); + + return new_sexp; +} + + +/* Restarts a query for the day view */ +static void +update_query (EDayView *day_view) +{ + char *real_sexp; + + e_day_view_free_events (day_view); + gtk_widget_queue_draw (day_view->top_canvas); + gtk_widget_queue_draw (day_view->main_canvas); + + if (!(day_view->client + && cal_client_get_load_state (day_view->client) == CAL_CLIENT_LOAD_LOADED)) + return; + + if (day_view->query) { + gtk_signal_disconnect_by_data (GTK_OBJECT (day_view->query), day_view); + gtk_object_unref (GTK_OBJECT (day_view->query)); + } + + g_assert (day_view->sexp != NULL); + real_sexp = adjust_query_sexp (day_view, day_view->sexp); + + day_view->query = cal_client_get_query (day_view->client, real_sexp); + g_free (real_sexp); + + if (!day_view->query) { + g_message ("update_query(): Could not create the query"); + return; + } + + gtk_signal_connect (GTK_OBJECT (day_view->query), "obj_updated", + GTK_SIGNAL_FUNC (query_obj_updated_cb), day_view); + gtk_signal_connect (GTK_OBJECT (day_view->query), "obj_removed", + GTK_SIGNAL_FUNC (query_obj_removed_cb), day_view); + gtk_signal_connect (GTK_OBJECT (day_view->query), "query_done", + GTK_SIGNAL_FUNC (query_query_done_cb), day_view); + gtk_signal_connect (GTK_OBJECT (day_view->query), "eval_error", + GTK_SIGNAL_FUNC (query_eval_error_cb), day_view); +} + +/* Callback used when the calendar client finishes opening */ +static void +cal_opened_cb (CalClient *client, CalClientOpenStatus status, gpointer data) +{ + EDayView *day_view; + + day_view = E_DAY_VIEW (data); + + if (status != CAL_CLIENT_OPEN_SUCCESS) + return; + + update_query (day_view); +} /** * e_day_view_set_cal_client: @@ -1530,17 +1621,35 @@ e_day_view_set_cal_client (EDayView *day_view, day_view->client = client; if (day_view->client) { - if (cal_client_get_load_state (day_view->client) != CAL_CLIENT_LOAD_LOADED) + if (cal_client_get_load_state (day_view->client) == CAL_CLIENT_LOAD_LOADED) + update_query (day_view); + else gtk_signal_connect (GTK_OBJECT (day_view->client), "cal_opened", GTK_SIGNAL_FUNC (cal_opened_cb), day_view); - - gtk_signal_connect (GTK_OBJECT (day_view->client), "obj_updated", - GTK_SIGNAL_FUNC (obj_updated_cb), day_view); - gtk_signal_connect (GTK_OBJECT (day_view->client), "obj_removed", - GTK_SIGNAL_FUNC (obj_removed_cb), day_view); } +} - e_day_view_queue_reload_events (day_view); +/** + * e_day_view_set_query: + * @day_view: A day view. + * @sexp: S-expression that defines the query. + * + * Sets the query sexp that the day view will use for filtering the displayed + * events. + **/ +void +e_day_view_set_query (EDayView *day_view, const char *sexp) +{ + g_return_if_fail (day_view != NULL); + g_return_if_fail (E_IS_DAY_VIEW (day_view)); + g_return_if_fail (sexp != NULL); + + if (day_view->sexp) + g_free (day_view->sexp); + + day_view->sexp = g_strdup (sexp); + + update_query (day_view); } @@ -1943,7 +2052,7 @@ e_day_view_set_selected_time_range (EDayView *day_view, /* See if we need to change the days shown. */ if (lower != day_view->lower) { e_day_view_recalc_day_starts (day_view, lower); - e_day_view_queue_reload_events (day_view); + update_query (day_view); } /* Set the selection. */ @@ -2140,7 +2249,8 @@ e_day_view_set_days_shown (EDayView *day_view, e_day_view_recalc_day_starts (day_view, day_view->lower); e_day_view_recalc_cell_sizes (day_view); - e_day_view_queue_reload_events (day_view); + + update_query (day_view); } @@ -2419,7 +2529,7 @@ e_day_view_recalc_work_week (EDayView *day_view) day_view->selection_start_day = -1; e_day_view_recalc_day_starts (day_view, lower); - e_day_view_queue_reload_events (day_view); + update_query (day_view); /* This updates the date navigator. */ e_day_view_update_calendar_selection_time (day_view); @@ -3927,77 +4037,6 @@ e_day_view_abort_resize (EDayView *day_view, } -/* This frees any events currently loaded, and queues a reload. */ -static void -e_day_view_queue_reload_events (EDayView *day_view) -{ - e_day_view_free_events (day_view); - - if (day_view->reload_events_idle_id == 0) { - /* We'll use a high idle priority here, so the events are - reloaded before the canvas is updated. */ - day_view->reload_events_idle_id = g_idle_add_full - (G_PRIORITY_HIGH_IDLE, - e_day_view_reload_events_idle_cb, day_view, NULL); - } -} - - -static gboolean -e_day_view_reload_events_idle_cb (gpointer data) -{ - EDayView *day_view; - - g_return_val_if_fail (E_IS_DAY_VIEW (data), FALSE); - - GDK_THREADS_ENTER (); - - day_view = E_DAY_VIEW (data); - - day_view->reload_events_idle_id = 0; - - e_day_view_reload_events (day_view); - - GDK_THREADS_LEAVE (); - return FALSE; -} - - -static void -e_day_view_reload_events (EDayView *day_view) -{ - e_day_view_free_events (day_view); - - if (!(day_view->client - && cal_client_get_load_state (day_view->client) == CAL_CLIENT_LOAD_LOADED)) - return; - - /* If both lower & upper are 0, then the time range hasn't been set, - so we don't try to load any events. */ - if (day_view->lower != 0 || day_view->upper != 0) { -#if 0 - g_print ("EDayView (%s) generating instances\n", - day_view->work_week_view ? "Work Week" : "1 Day View"); -#endif - cal_client_generate_instances (day_view->client, - CALOBJ_TYPE_EVENT, - day_view->lower, - day_view->upper, - e_day_view_add_event, - day_view); - } - - /* We need to do this to make sure the top canvas is resized. */ - day_view->long_events_need_layout = TRUE; - - e_day_view_check_layout (day_view); - e_day_view_reshape_main_canvas_resize_bars (day_view); - - gtk_widget_queue_draw (day_view->top_canvas); - gtk_widget_queue_draw (day_view->main_canvas); -} - - static void e_day_view_free_events (EDayView *day_view) { diff --git a/calendar/gui/e-day-view.h b/calendar/gui/e-day-view.h index 2379a76b87..18f8187951 100644 --- a/calendar/gui/e-day-view.h +++ b/calendar/gui/e-day-view.h @@ -235,6 +235,10 @@ struct _EDayView /* Calendar client object we are monitoring */ CalClient *client; + /* S-expression for query and the query object */ + char *sexp; + CalQuery *query; + /* The start and end of the days shown. */ time_t lower; time_t upper; @@ -270,9 +274,6 @@ struct _EDayView gboolean long_events_need_reshape; gboolean need_reshape[E_DAY_VIEW_MAX_DAYS]; - /* The id of our idle function to reload all events. */ - gint reload_events_idle_id; - /* The number of minutes per row. 5, 10, 15, 30 or 60. */ gint mins_per_row; @@ -488,6 +489,9 @@ void e_day_view_set_calendar (EDayView *day_view, void e_day_view_set_cal_client (EDayView *day_view, CalClient *client); +void e_day_view_set_query (EDayView *day_view, + const char *sexp); + /* This sets the selected time range. The EDayView will show the day or week corresponding to the start time. If the start_time & end_time are not equal and are both visible in the view, then the selection is set to those times, @@ -501,16 +505,6 @@ void e_day_view_get_selected_time_range (EDayView *day_view, time_t *start_time, time_t *end_time); -/* This is called when one event has been added or updated. */ -void e_day_view_update_event (EDayView *day_view, - const gchar *uid); - -/* This removes all the events associated with the given uid. Note that for - recurring events there may be more than one. If any events are found and - removed we need to layout the events again. */ -void e_day_view_remove_event (EDayView *day_view, - const gchar *uid); - /* Whether we are displaying a work-week, in which case the display always starts on the first day of the working week. */ gboolean e_day_view_get_work_week_view (EDayView *day_view); diff --git a/calendar/gui/e-week-view.c b/calendar/gui/e-week-view.c index 90a7b2c695..c06b568d9d 100644 --- a/calendar/gui/e-week-view.c +++ b/calendar/gui/e-week-view.c @@ -109,9 +109,6 @@ static gint e_week_view_convert_position_to_day (EWeekView *week_view, static void e_week_view_update_selection (EWeekView *week_view, gint day); -static void e_week_view_queue_reload_events (EWeekView *week_view); -static gboolean e_week_view_reload_events_idle_cb (gpointer data); -static void e_week_view_reload_events (EWeekView *week_view); static void e_week_view_free_events (EWeekView *week_view); static gboolean e_week_view_add_event (CalComponent *comp, time_t start, @@ -257,13 +254,14 @@ e_week_view_init (EWeekView *week_view) week_view->calendar = NULL; week_view->client = NULL; + week_view->sexp = g_strdup ("#t"); /* match all by default */ + week_view->query = NULL; week_view->events = g_array_new (FALSE, FALSE, sizeof (EWeekViewEvent)); week_view->events_sorted = TRUE; week_view->events_need_layout = FALSE; week_view->events_need_reshape = FALSE; - week_view->reload_events_idle_id = 0; week_view->spans = NULL; @@ -383,7 +381,7 @@ e_week_view_init (EWeekView *week_view) /* Create the cursors. */ - week_view->normal_cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW); + week_view->normal_cursor = gdk_cursor_new (GDK_LEFT_PTR); week_view->move_cursor = gdk_cursor_new (GDK_FLEUR); week_view->resize_width_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); week_view->last_cursor_set = NULL; @@ -416,17 +414,23 @@ e_week_view_destroy (GtkObject *object) e_week_view_free_events (week_view); - if (week_view->reload_events_idle_id != 0) { - g_source_remove (week_view->reload_events_idle_id); - week_view->reload_events_idle_id = 0; - } - if (week_view->client) { gtk_signal_disconnect_by_data (GTK_OBJECT (week_view->client), week_view); gtk_object_unref (GTK_OBJECT (week_view->client)); week_view->client = NULL; } + if (week_view->sexp) { + g_free (week_view->sexp); + week_view->sexp = NULL; + } + + if (week_view->query) { + gtk_signal_disconnect_by_data (GTK_OBJECT (week_view->query), week_view); + gtk_object_unref (GTK_OBJECT (week_view->query)); + week_view->query = NULL; + } + if (week_view->small_font) gdk_font_unref (week_view->small_font); @@ -880,23 +884,11 @@ e_week_view_set_calendar (EWeekView *week_view, } -/* Callback used when the calendar client finishes opening */ -static void -cal_opened_cb (CalClient *client, CalClientOpenStatus status, gpointer data) -{ - EWeekView *week_view; - - week_view = E_WEEK_VIEW (data); - - if (status != CAL_CLIENT_OPEN_SUCCESS) - return; - - e_week_view_queue_reload_events (week_view); -} - -/* Callback used when the calendar client tells us that an object changed */ +/* Callback used when a component is updated in the live query */ static void -obj_updated_cb (CalClient *client, const char *uid, gpointer data) +query_obj_updated_cb (CalQuery *query, const char *uid, + gboolean query_in_progress, int n_scanned, int total, + gpointer data) { EWeekView *week_view; EWeekViewEvent *event; @@ -906,9 +898,6 @@ obj_updated_cb (CalClient *client, const char *uid, gpointer data) week_view = E_WEEK_VIEW (data); - /* Sanity check. */ - g_return_if_fail (client == week_view->client); - /* If we don't have a valid date set yet, just return. */ if (!g_date_valid (&week_view->first_day_shown)) return; @@ -930,12 +919,6 @@ obj_updated_cb (CalClient *client, const char *uid, gpointer data) return; } - /* We only care about events. */ - if (cal_component_get_vtype (comp) != CAL_COMPONENT_EVENT) { - gtk_object_unref (GTK_OBJECT (comp)); - return; - } - /* If the event already exists and the dates didn't change, we can update the event fairly easily without changing the events arrays or computing a new layout. */ @@ -981,9 +964,9 @@ obj_updated_cb (CalClient *client, const char *uid, gpointer data) gtk_widget_queue_draw (week_view->main_canvas); } -/* Callback used when the calendar client tells us that an object was removed */ +/* Callback used when a component is removed from the live query */ static void -obj_removed_cb (CalClient *client, const char *uid, gpointer data) +query_obj_removed_cb (CalClient *client, const char *uid, gpointer data) { EWeekView *week_view; @@ -996,6 +979,117 @@ obj_removed_cb (CalClient *client, const char *uid, gpointer data) gtk_widget_queue_draw (week_view->main_canvas); } +/* Callback used when a query ends */ +static void +query_query_done_cb (CalQuery *query, CalQueryDoneStatus status, const char *error_str, gpointer data) +{ + EWeekView *week_view; + + week_view = E_WEEK_VIEW (data); + + /* FIXME */ + + if (status != CAL_QUERY_DONE_SUCCESS) + fprintf (stderr, "query done: %s\n", error_str); +} + +/* Callback used when an evaluation error occurs when running a query */ +static void +query_eval_error_cb (CalQuery *query, const char *error_str, gpointer data) +{ + EWeekView *week_view; + + week_view = E_WEEK_VIEW (data); + + /* FIXME */ + + fprintf (stderr, "eval error: %s\n", error_str); +} + +/* Builds a complete query sexp for the week view by adding the predicates to + * filter only for VEVENTS that fit in the week view's time range. + */ +static char * +adjust_query_sexp (EWeekView *week_view, const char *sexp) +{ + int num_days; + char *start, *end; + char *new_sexp; + + /* If the dates have not been set yet, we just want an empty query. */ + if (!g_date_valid (&week_view->first_day_shown)) + return g_strdup ("#f"); + + num_days = week_view->multi_week_view ? week_view->weeks_shown * 7 : 7; + + start = isodate_from_time_t (week_view->day_starts[0]); + end = isodate_from_time_t (week_view->day_starts[num_days]); + + new_sexp = g_strdup_printf ("(and (= (get-vtype) \"VEVENT\")" + " (occur-in-time-range? (make-time \"%s\")" + " (make-time \"%s\"))" + " %s)", + start, end, + sexp); + + g_free (start); + g_free (end); + + return new_sexp; +} + +/* Restarts a query for the week view */ +static void +update_query (EWeekView *week_view) +{ + char *real_sexp; + + e_week_view_free_events (week_view); + gtk_widget_queue_draw (week_view->main_canvas); + + if (!(week_view->client + && cal_client_get_load_state (week_view->client) == CAL_CLIENT_LOAD_LOADED)) + return; + + if (week_view->query) { + gtk_signal_disconnect_by_data (GTK_OBJECT (week_view->query), week_view); + gtk_object_unref (GTK_OBJECT (week_view->query)); + } + + g_assert (week_view->sexp != NULL); + real_sexp = adjust_query_sexp (week_view, week_view->sexp); + + week_view->query = cal_client_get_query (week_view->client, real_sexp); + g_free (real_sexp); + + if (!week_view->query) { + g_message ("update_query(): Could not create the query"); + return; + } + + gtk_signal_connect (GTK_OBJECT (week_view->query), "obj_updated", + GTK_SIGNAL_FUNC (query_obj_updated_cb), week_view); + gtk_signal_connect (GTK_OBJECT (week_view->query), "obj_removed", + GTK_SIGNAL_FUNC (query_obj_removed_cb), week_view); + gtk_signal_connect (GTK_OBJECT (week_view->query), "query_done", + GTK_SIGNAL_FUNC (query_query_done_cb), week_view); + gtk_signal_connect (GTK_OBJECT (week_view->query), "eval_error", + GTK_SIGNAL_FUNC (query_eval_error_cb), week_view); +} + +/* Callback used when the calendar client finishes opening */ +static void +cal_opened_cb (CalClient *client, CalClientOpenStatus status, gpointer data) +{ + EWeekView *week_view; + + week_view = E_WEEK_VIEW (data); + + if (status != CAL_CLIENT_OPEN_SUCCESS) + return; + + update_query (week_view); +} /** * e_week_view_set_cal_client: @@ -1028,17 +1122,35 @@ e_week_view_set_cal_client (EWeekView *week_view, week_view->client = client; if (week_view->client) { - if (cal_client_get_load_state (week_view->client) != CAL_CLIENT_LOAD_LOADED) + if (cal_client_get_load_state (week_view->client) == CAL_CLIENT_LOAD_LOADED) + update_query (week_view); + else gtk_signal_connect (GTK_OBJECT (week_view->client), "cal_opened", GTK_SIGNAL_FUNC (cal_opened_cb), week_view); - - gtk_signal_connect (GTK_OBJECT (week_view->client), "obj_updated", - GTK_SIGNAL_FUNC (obj_updated_cb), week_view); - gtk_signal_connect (GTK_OBJECT (week_view->client), "obj_removed", - GTK_SIGNAL_FUNC (obj_removed_cb), week_view); } +} + +/** + * e_week_view_set_query: + * @week_view: A week view. + * @sexp: S-expression that defines the query. + * + * Sets the query sexp that the week view will use for filtering the displayed + * events. + **/ +void +e_week_view_set_query (EWeekView *week_view, const char *sexp) +{ + g_return_if_fail (week_view != NULL); + g_return_if_fail (E_IS_WEEK_VIEW (week_view)); + g_return_if_fail (sexp != NULL); + + if (week_view->sexp) + g_free (week_view->sexp); - e_week_view_queue_reload_events (week_view); + week_view->sexp = g_strdup (sexp); + + update_query (week_view); } @@ -1108,7 +1220,7 @@ e_week_view_set_selected_time_range (EWeekView *week_view, start_time = time_add_day (start_time, -day_offset); start_time = time_day_begin (start_time); e_week_view_recalc_day_starts (week_view, start_time); - e_week_view_queue_reload_events (week_view); + update_query (week_view); } /* Set the selection to the given days. */ @@ -1139,7 +1251,6 @@ e_week_view_set_selected_time_range (EWeekView *week_view, if (update_adjustment_value) gtk_adjustment_set_value (GTK_RANGE (week_view->vscrollbar)->adjustment, 0); - gtk_widget_queue_draw (week_view->main_canvas); } @@ -1227,7 +1338,7 @@ e_week_view_set_first_day_shown (EWeekView *week_view, g_date_to_struct_tm (&base_date, &start_tm); start_time = mktime (&start_tm); e_week_view_recalc_day_starts (week_view, start_time); - e_week_view_queue_reload_events (week_view); + update_query (week_view); } /* Try to keep the previous selection, but if it is no longer shown @@ -1256,7 +1367,6 @@ e_week_view_set_first_day_shown (EWeekView *week_view, if (update_adjustment_value) gtk_adjustment_set_value (GTK_RANGE (week_view->vscrollbar)->adjustment, 0); - gtk_widget_queue_draw (week_view->main_canvas); } @@ -1364,8 +1474,7 @@ e_week_view_set_weeks_shown (EWeekView *week_view, if (g_date_valid (&week_view->first_day_shown)) e_week_view_set_first_day_shown (week_view, &week_view->first_day_shown); - /* Make sure the events are reloaded. */ - e_week_view_queue_reload_events (week_view); + update_query (week_view); } } @@ -2019,77 +2128,6 @@ e_week_view_update_selection (EWeekView *week_view, } -/* This frees any events currently loaded, and queues a reload. */ -static void -e_week_view_queue_reload_events (EWeekView *week_view) -{ - e_week_view_free_events (week_view); - - if (week_view->reload_events_idle_id == 0) { - /* We'll use a low priority here, so the user can scroll - the view quickly. */ - week_view->reload_events_idle_id = g_idle_add_full - (G_PRIORITY_LOW, - e_week_view_reload_events_idle_cb, week_view, NULL); - } -} - - -static gboolean -e_week_view_reload_events_idle_cb (gpointer data) -{ - EWeekView *week_view; - - g_return_val_if_fail (E_IS_WEEK_VIEW (data), FALSE); - - GDK_THREADS_ENTER (); - - week_view = E_WEEK_VIEW (data); - - week_view->reload_events_idle_id = 0; - - e_week_view_reload_events (week_view); - - GDK_THREADS_LEAVE (); - return FALSE; -} - - -static void -e_week_view_reload_events (EWeekView *week_view) -{ - gint num_days; - - e_week_view_free_events (week_view); - - if (!(week_view->client - && cal_client_get_load_state (week_view->client) == CAL_CLIENT_LOAD_LOADED)) - return; - - /* Only load events if the date range has been set. */ - if (g_date_valid (&week_view->first_day_shown)) { - num_days = week_view->multi_week_view - ? week_view->weeks_shown * 7 : 7; - -#if 0 - g_print ("EWeekView (%s) generating instances\n", - week_view->multi_week_view ? "Month View" : "Week View"); -#endif - cal_client_generate_instances (week_view->client, - CALOBJ_TYPE_EVENT, - week_view->day_starts[0], - week_view->day_starts[num_days], - e_week_view_add_event, - week_view); - } - - week_view->events_need_reshape = TRUE; - e_week_view_check_layout (week_view); - - gtk_widget_queue_draw (week_view->main_canvas); -} - - static void e_week_view_free_events (EWeekView *week_view) { @@ -2777,7 +2815,7 @@ e_week_view_on_adjustment_changed (GtkAdjustment *adjustment, lower = time_day_begin (lower); e_week_view_recalc_day_starts (week_view, lower); - e_week_view_queue_reload_events (week_view); + update_query (week_view); /* Update the selection, if needed. */ if (week_view->selection_start_day != -1) { diff --git a/calendar/gui/e-week-view.h b/calendar/gui/e-week-view.h index 47362d0ee6..6717da9ea3 100644 --- a/calendar/gui/e-week-view.h +++ b/calendar/gui/e-week-view.h @@ -186,15 +186,16 @@ struct _EWeekView /* Calendar client object we are monitoring */ CalClient *client; + /* S-expression for query and the query object */ + char *sexp; + CalQuery *query; + /* The array of EWeekViewEvent elements. */ GArray *events; gboolean events_sorted; gboolean events_need_layout; gboolean events_need_reshape; - /* The id of our idle function to reload all events. */ - gint reload_events_idle_id; - /* An array of EWeekViewEventSpan elements. Each event has its own space within this array, and uses the spans_index and num_spans fields of the EWeekViewEvent struct to access it. */ @@ -363,6 +364,9 @@ void e_week_view_set_first_day_shown (EWeekView *week_view, void e_week_view_set_cal_client (EWeekView *week_view, CalClient *client); +void e_week_view_set_query (EWeekView *week_view, + const char *sexp); + /* The selected time range. The EWeekView will show the corresponding month and the days between start_time and end_time will be selected. To select a single day, use the same value for start_time & end_time. */ diff --git a/calendar/pcs/cal.c b/calendar/pcs/cal.c index 3129665466..e723c077cb 100644 --- a/calendar/pcs/cal.c +++ b/calendar/pcs/cal.c @@ -456,6 +456,8 @@ Cal_get_query (PortableServer_Servant servant, Cal *cal; CalPrivate *priv; Query *query; + CORBA_Environment ev2; + GNOME_Evolution_Calendar_Query query_copy; cal = CAL (bonobo_object_from_servant (servant)); priv = cal->priv; @@ -468,7 +470,20 @@ Cal_get_query (PortableServer_Servant servant, return CORBA_OBJECT_NIL; } - return BONOBO_OBJREF (query); + CORBA_exception_init (&ev2); + query_copy = CORBA_Object_duplicate (BONOBO_OBJREF (query), &ev2); + if (ev2._major != CORBA_NO_EXCEPTION) { + CORBA_exception_free (&ev2); + g_message ("Cal_get_query(): Could not duplicate the query reference"); + CORBA_exception_set (ev, CORBA_USER_EXCEPTION, + ex_GNOME_Evolution_Calendar_Cal_CouldNotCreate, + NULL); + return CORBA_OBJECT_NIL; + } + + CORBA_exception_free (&ev2); + + return query_copy; } /** diff --git a/calendar/pcs/query.c b/calendar/pcs/query.c index 708a160794..474a2eb971 100644 --- a/calendar/pcs/query.c +++ b/calendar/pcs/query.c @@ -30,6 +30,7 @@ #include <gtk/gtksignal.h> #include <e-util/e-sexp.h> #include <cal-util/cal-recur.h> +#include <cal-util/timeutil.h> #include "query.h" @@ -138,6 +139,7 @@ query_destroy (GtkObject *object) priv = query->priv; if (priv->backend) { + gtk_signal_disconnect_by_data (GTK_OBJECT (priv->backend), query); gtk_object_unref (GTK_OBJECT (priv->backend)); priv->backend = NULL; } @@ -200,7 +202,164 @@ query_destroy (GtkObject *object) -/* Search engine functions */ +/* E-Sexp functions */ + +/* (time-now) + * + * Returns a time_t of time (NULL). + */ +static ESExpResult * +func_time_now (ESExp *esexp, int argc, ESExpResult **argv, void *data) +{ + ESExpResult *result; + + if (argc != 0) { + e_sexp_fatal_error (esexp, _("time-now expects 0 arguments")); + return NULL; + } + + result = e_sexp_result_new (esexp, ESEXP_RES_TIME); + result->value.time = time (NULL); + + return result; +} + +/* (make-time ISODATE) + * + * ISODATE - string, ISO 8601 date/time representation + * + * Constructs a time_t value for the specified date. + */ +static ESExpResult * +func_make_time (ESExp *esexp, int argc, ESExpResult **argv, void *data) +{ + const char *str; + time_t t; + ESExpResult *result; + + if (argc != 1) { + e_sexp_fatal_error (esexp, _("make-time expects 1 argument")); + return NULL; + } + + if (argv[0]->type != ESEXP_RES_STRING) { + e_sexp_fatal_error (esexp, _("make-time expects argument 1 " + "to be a string")); + return NULL; + } + str = argv[0]->value.string; + + t = time_from_isodate (str); + if (t == -1) { + e_sexp_fatal_error (esexp, _("make-time argument 1 must be an " + "ISO 8601 date/time string")); + return NULL; + } + + result = e_sexp_result_new (esexp, ESEXP_RES_TIME); + result->value.time = t; + + return result; +} + +/* (time-add-day TIME N) + * + * TIME - time_t, base time + * N - int, number of days to add + * + * Adds the specified number of days to a time value. + */ +static ESExpResult * +func_time_add_day (ESExp *esexp, int argc, ESExpResult **argv, void *data) +{ + ESExpResult *result; + time_t t; + int n; + + if (argc != 2) { + e_sexp_fatal_error (esexp, _("time-add-day expects 2 arguments")); + return NULL; + } + + if (argv[0]->type != ESEXP_RES_TIME) { + e_sexp_fatal_error (esexp, _("time-add-day expects argument 1 " + "to be a time_t")); + return NULL; + } + t = argv[0]->value.time; + + if (argv[1]->type != ESEXP_RES_INT) { + e_sexp_fatal_error (esexp, _("time-add-day expects argument 2 " + "to be an integer")); + return NULL; + } + n = argv[1]->value.number; + + result = e_sexp_result_new (esexp, ESEXP_RES_TIME); + result->value.time = time_add_day (t, n); + + return result; +} + +/* (time-day-begin TIME) + * + * TIME - time_t, base time + * + * Returns the start of the day, according to the local time. + */ +static ESExpResult * +func_time_day_begin (ESExp *esexp, int argc, ESExpResult **argv, void *data) +{ + time_t t; + ESExpResult *result; + + if (argc != 1) { + e_sexp_fatal_error (esexp, _("time-day-begin expects 1 argument")); + return NULL; + } + + if (argv[0]->type != ESEXP_RES_TIME) { + e_sexp_fatal_error (esexp, _("time-day-begin expects argument 1 " + "to be a time_t")); + return NULL; + } + t = argv[0]->value.time; + + result = e_sexp_result_new (esexp, ESEXP_RES_TIME); + result->value.time = time_day_begin (t); + + return result; +} + +/* (time-day-end TIME) + * + * TIME - time_t, base time + * + * Returns the end of the day, according to the local time. + */ +static ESExpResult * +func_time_day_end (ESExp *esexp, int argc, ESExpResult **argv, void *data) +{ + time_t t; + ESExpResult *result; + + if (argc != 1) { + e_sexp_fatal_error (esexp, _("time-day-end expects 1 argument")); + return NULL; + } + + if (argv[0]->type != ESEXP_RES_TIME) { + e_sexp_fatal_error (esexp, _("time-day-end expects argument 1 " + "to be a time_t")); + return NULL; + } + t = argv[0]->value.time; + + result = e_sexp_result_new (esexp, ESEXP_RES_TIME); + result->value.time = time_day_end (t); + + return result; +} /* (get-vtype) * @@ -284,14 +443,12 @@ instance_occur_cb (CalComponent *comp, time_t start, time_t end, gpointer data) /* (occur-in-time-range? START END) * - * START - int, time_t start of the time range - * END - int, time_t end of the time range + * START - time_t, start of the time range + * END - time_t, end of the time range * * Returns a boolean indicating whether the component has any occurrences in the * specified time range. - * - * We may prefer to switch this to a string representation of times (ISO 8601, - * perhaps). */ + */ static ESExpResult * func_occur_in_time_range (ESExp *esexp, int argc, ESExpResult **argv, void *data) { @@ -315,19 +472,19 @@ func_occur_in_time_range (ESExp *esexp, int argc, ESExpResult **argv, void *data return NULL; } - if (argv[0]->type != ESEXP_RES_INT) { + if (argv[0]->type != ESEXP_RES_TIME) { e_sexp_fatal_error (esexp, _("occur-in-time-range? expects argument 1 " - "to be an integer")); + "to be a time_t")); return NULL; } - start = argv[0]->value.number; + start = argv[0]->value.time; - if (argv[1]->type != ESEXP_RES_INT) { + if (argv[1]->type != ESEXP_RES_TIME) { e_sexp_fatal_error (esexp, _("occur-in-time-range? expects argument 2 " - "to be an integer")); + "to be a time_t")); return NULL; } - end = argv[1]->value.number; + end = argv[1]->value.time; /* See if there is at least one instance in that range */ @@ -446,6 +603,14 @@ static struct { char *name; ESExpFunc *func; } functions[] = { + /* Time-related functions */ + { "time-now", func_time_now }, + { "make-time", func_make_time }, + { "time-add-day", func_time_add_day }, + { "time-day-begin", func_time_day_begin }, + { "time-day-end", func_time_day_end }, + + /* Component-related functions */ { "get-vtype", func_get_vtype }, { "occur-in-time-range?", func_occur_in_time_range } }; @@ -487,6 +652,8 @@ match_component (Query *query, const char *uid, icalcomp = icalparser_parse_string (comp_str); g_assert (icalcomp != NULL); + g_free (comp_str); + comp = cal_component_new (); set_succeeded = cal_component_set_icalcomponent (comp, icalcomp); g_assert (set_succeeded); @@ -518,6 +685,7 @@ match_component (Query *query, const char *uid, "an evaluation error"); CORBA_exception_free (&ev); + return; } else if (result->type != ESEXP_RES_BOOL) { CORBA_Environment ev; @@ -541,6 +709,8 @@ match_component (Query *query, const char *uid, else remove_component (query, uid); } + + e_sexp_result_free (priv->esexp, result); } /* Processes a single component that is queued in the list */ |