diff options
Diffstat (limited to 'calendar')
-rw-r--r-- | calendar/ChangeLog | 22 | ||||
-rw-r--r-- | calendar/gui/alarm-notify/notify-main.c | 35 | ||||
-rw-r--r-- | calendar/gui/gnome-cal.c | 163 | ||||
-rw-r--r-- | calendar/gui/main.c | 1 | ||||
-rw-r--r-- | calendar/pcs/query.c | 279 |
5 files changed, 482 insertions, 18 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 75ff2c9228..ef4cc6c29a 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,25 @@ +2001-04-15 Federico Mena Quintero <federico@ximian.com> + + * gui/gnome-cal.c (setup_widgets): Create the ESearchBar thingy. + (search_bar_query_changed_cb): Build the different queries based + on the type and string. + + * pcs/query.c (backend_obj_updated_cb): Ref the query while we are + notifying the listener so that it will not disappear from under us. + (backend_obj_removed_cb): Likewise. + (process_component_cb): Likewise. + (func_contains): New function to match text fields. + (matches_comment): New function to match comment lists. + (matches_description): New function to match description lists. + (matches_summary): New function to match summaries. + (matches_any): New function to match any text field. + (func_has_categories): New function to match categories. + +2001-04-14 Federico Mena Quintero <federico@ximian.com> + + * gui/alarm-notify/notify-main.c (main): Initialize the trigger + and queue systems. + 2001-04-13 Dan Winship <danw@ximian.com> * cal-util/timeutil.c (time_from_isodate): Fix the syntactic bogon diff --git a/calendar/gui/alarm-notify/notify-main.c b/calendar/gui/alarm-notify/notify-main.c index ec43738e96..9c1d04b739 100644 --- a/calendar/gui/alarm-notify/notify-main.c +++ b/calendar/gui/alarm-notify/notify-main.c @@ -31,6 +31,8 @@ #include <bonobo/bonobo-main.h> #include <bonobo/bonobo-generic-factory.h> #include <liboaf/liboaf.h> +#include "alarm.h" +#include "alarm-queue.h" #include "alarm-notify.h" @@ -40,6 +42,31 @@ static BonoboGenericFactory *factory; static AlarmNotify *alarm_notify_service; +/* La de da */ +static void +funny_trigger_cb (gpointer alarm_id, time_t trigger, gpointer data) +{ + char *msg; + char str[256]; + struct tm *tm; + + tm = localtime (&trigger); + strftime (str, sizeof (str), "%Y/%m/%d %H:%M:%S", tm); + + msg = g_strdup_printf (_("It is %s. The Unix time is %ld right now. We just thought " + "you may like to know."), str, (long) trigger); + gnome_ok_dialog (msg); + g_free (msg); +} + +/* Dum de dum */ +static void +funny_times_init (void) +{ + alarm_add ((time_t) 987654321L, funny_trigger_cb, NULL, NULL); /* Apr 19 04:25:21 2001 UTC */ + alarm_add ((time_t) 999999999L, funny_trigger_cb, NULL, NULL); /* Sep 9 01:46:39 2001 UTC */ +} + /* Factory function for the alarm notify service; just creates and references a * singleton service object. */ @@ -71,6 +98,11 @@ main (int argc, char **argv) if (bonobo_init (CORBA_OBJECT_NIL, CORBA_OBJECT_NIL, CORBA_OBJECT_NIL) == FALSE) g_error (_("Could not initialize Bonobo")); + alarm_init (); + alarm_queue_init (); + + funny_times_init (); + factory = bonobo_generic_factory_new ("OAFID:GNOME_Evolution_Calendar_AlarmNotify_Factory", alarm_notify_factory_fn, NULL); if (!factory) @@ -81,5 +113,8 @@ main (int argc, char **argv) bonobo_object_unref (BONOBO_OBJECT (factory)); factory = NULL; + alarm_queue_done (); + alarm_done (); + return 0; } diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c index 917489c043..138e5cb280 100644 --- a/calendar/gui/gnome-cal.c +++ b/calendar/gui/gnome-cal.c @@ -37,6 +37,7 @@ #include <gal/e-paned/e-hpaned.h> #include <gal/e-paned/e-vpaned.h> #include <cal-util/timeutil.h> +#include "widgets/misc/e-search-bar.h" #include "dialogs/alarm-notify-dialog.h" #include "e-calendar-table.h" #include "e-day-view.h" @@ -91,6 +92,8 @@ struct _GnomeCalendarPrivate { /* Widgets */ + GtkWidget *search_bar; + GtkWidget *hpane; GtkWidget *notebook; GtkWidget *vpane; @@ -202,6 +205,147 @@ gnome_calendar_class_init (GnomeCalendarClass *class) object_class->destroy = gnome_calendar_destroy; } +static GtkWidget * +get_current_page (GnomeCalendar *gcal) +{ + GnomeCalendarPrivate *priv; + + priv = gcal->priv; + + return GTK_NOTEBOOK (priv->notebook)->cur_page->child; +} + +enum { + SEARCH_SHOW_ALL +}; + +static ESearchBarItem search_menu_items[] = { + { N_("Show all "), SEARCH_SHOW_ALL }, + { NULL, -1 } +}; + +enum { + SEARCH_ANY_FIELD_CONTAINS, + SEARCH_SUMMARY_CONTAINS, + SEARCH_DESCRIPTION_CONTAINS, + SEARCH_COMMENT_CONTAINS, + SEARCH_HAS_CATEGORY +}; + +static ESearchBarItem search_option_items[] = { + { N_("Any field contains"), SEARCH_ANY_FIELD_CONTAINS }, + { N_("Summary contains"), SEARCH_SUMMARY_CONTAINS }, + { N_("Description contains"), SEARCH_DESCRIPTION_CONTAINS }, + { N_("Comment contains"), SEARCH_COMMENT_CONTAINS }, + { N_("Has category"), SEARCH_HAS_CATEGORY }, + { NULL, -1 } +}; + +/* Sets the query sexp for the current view in the calendar */ +static void +set_query (GnomeCalendar *gcal, char *sexp) +{ + GnomeCalendarPrivate *priv; + GtkWidget *page; + + g_assert (sexp != NULL); + + priv = gcal->priv; + + page = get_current_page (gcal); + + if (page == priv->day_view || page == priv->work_week_view) + e_day_view_set_query (E_DAY_VIEW (page), sexp); + else if (page == priv->week_view || page == priv->month_view) + e_week_view_set_query (E_WEEK_VIEW (page), sexp); + else { + g_warning ("A penguin bit my hand!"); + g_assert_not_reached (); + } +} + +/* Sets the query string to be (contains? "field" "text") */ +static void +set_query_contains (GnomeCalendar *gcal, const char *field, const char *text) +{ + char *sexp; + + sexp = g_strdup_printf ("(contains? \"%s\" \"%s\")", field, text); + set_query (gcal, sexp); + g_free (sexp); +} + +/* Callback used when the query string is changed in the search bar */ +static void +search_bar_query_changed_cb (ESearchBar *search_bar, gpointer data) +{ + GnomeCalendar *gcal; + int item; + char *text; + + gcal = GNOME_CALENDAR (data); + + item = e_search_bar_get_option_choice (search_bar); + text = e_search_bar_get_text (search_bar); + + if (!text) + return; /* This is an error in the UTF8 conversion, not an empty string! */ + + switch (item) { + case SEARCH_ANY_FIELD_CONTAINS: + set_query_contains (gcal, "any", text); + break; + + case SEARCH_SUMMARY_CONTAINS: + set_query_contains (gcal, "summary", text); + break; + + case SEARCH_DESCRIPTION_CONTAINS: + set_query_contains (gcal, "description", text); + break; + + case SEARCH_COMMENT_CONTAINS: + set_query_contains (gcal, "comment", text); + break; + + case SEARCH_HAS_CATEGORY: { + char *sexp; + + sexp = g_strdup_printf ("(has-categories? \"%s\")", text); + set_query (gcal, sexp); + g_free (sexp); + break; + } + + default: + g_assert_not_reached (); + } + + g_free (text); +} + +/* Callback used when a menu item is activated in the search bar */ +static void +search_bar_menu_activated_cb (ESearchBar *search_bar, int item, gpointer data) +{ + GnomeCalendar *gcal; + + gcal = GNOME_CALENDAR (data); + + switch (item) { + case SEARCH_SHOW_ALL: + set_query (gcal, "#t"); /* match all */ + /* FIXME: should we change the rest of the search bar so that + * the user sees that he selected "show all" instead of some + * type/text search combination? + */ + break; + + default: + g_assert_not_reached (); + } +} + static void setup_widgets (GnomeCalendar *gcal) { @@ -212,6 +356,15 @@ setup_widgets (GnomeCalendar *gcal) priv = gcal->priv; + priv->search_bar = e_search_bar_new (search_menu_items, search_option_items); + gtk_signal_connect (GTK_OBJECT (priv->search_bar), "query_changed", + GTK_SIGNAL_FUNC (search_bar_query_changed_cb), gcal); + gtk_signal_connect (GTK_OBJECT (priv->search_bar), "menu_activated", + GTK_SIGNAL_FUNC (search_bar_menu_activated_cb), gcal); + + gtk_widget_show (priv->search_bar); + gtk_box_pack_start (GTK_BOX (gcal), priv->search_bar, FALSE, FALSE, 0); + /* The main HPaned, with the notebook of calendar views on the left and the ECalendar and ToDo list on the right. */ priv->hpane = e_hpaned_new (); @@ -378,16 +531,6 @@ gnome_calendar_destroy (GtkObject *object) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } -static GtkWidget * -get_current_page (GnomeCalendar *gcal) -{ - GnomeCalendarPrivate *priv; - - priv = gcal->priv; - - return GTK_NOTEBOOK (priv->notebook)->cur_page->child; -} - char * gnome_calendar_get_current_view_name (GnomeCalendar *gcal) { diff --git a/calendar/gui/main.c b/calendar/gui/main.c index 674a55078c..e75819f0f0 100644 --- a/calendar/gui/main.c +++ b/calendar/gui/main.c @@ -57,7 +57,6 @@ init_bonobo (int argc, char **argv) g_error (_("Could not initialize Bonobo")); } - int main (int argc, char **argv) { diff --git a/calendar/pcs/query.c b/calendar/pcs/query.c index 474a2eb971..48e77d216a 100644 --- a/calendar/pcs/query.c +++ b/calendar/pcs/query.c @@ -28,6 +28,7 @@ #include <libgnome/gnome-defs.h> #include <libgnome/gnome-i18n.h> #include <gtk/gtksignal.h> +#include <gal/widgets/e-unicode.h> #include <e-util/e-sexp.h> #include <cal-util/cal-recur.h> #include <cal-util/timeutil.h> @@ -497,6 +498,240 @@ func_occur_in_time_range (ESExp *esexp, int argc, ESExpResult **argv, void *data return result; } +/* Returns whether a list of CalComponentText items matches the specified string */ +static gboolean +matches_text_list (GSList *text_list, const char *str) +{ + GSList *l; + gboolean matches; + + matches = FALSE; + + for (l = text_list; l; l = l->next) { + CalComponentText *text; + + text = l->data; + g_assert (text->value != NULL); + + if (e_utf8_strstrcase (text->value, str) != NULL) { + matches = TRUE; + break; + } + } + + return matches; +} + +/* Returns whether the comments in a component matches the specified string */ +static gboolean +matches_comment (CalComponent *comp, const char *str) +{ + GSList *list; + gboolean matches; + + cal_component_get_comment_list (comp, &list); + matches = matches_text_list (list, str); + cal_component_free_text_list (list); + + return matches; +} + +/* Returns whether the description in a component matches the specified string */ +static gboolean +matches_description (CalComponent *comp, const char *str) +{ + GSList *list; + gboolean matches; + + cal_component_get_description_list (comp, &list); + matches = matches_text_list (list, str); + cal_component_free_text_list (list); + + return matches; +} + +/* Returns whether the summary in a component matches the specified string */ +static gboolean +matches_summary (CalComponent *comp, const char *str) +{ + CalComponentText text; + + cal_component_get_summary (comp, &text); + + if (!text.value) + return FALSE; + + return e_utf8_strstrcase (text.value, str) != NULL; +} + +/* Returns whether any text field in a component matches the specified string */ +static gboolean +matches_any (CalComponent *comp, const char *str) +{ + /* As an optimization, and to make life easier for the individual + * predicate functions, see if we are looking for the empty string right + * away. + */ + if (strlen (str) == 0) + return TRUE; + + return (matches_comment (comp, str) + || matches_description (comp, str) + || matches_summary (comp, str)); +} + +/* (contains? FIELD STR) + * + * FIELD - string, name of field to match (any, comment, description, summary) + * STR - string, match string + * + * Returns a boolean indicating whether the specified field contains the + * specified string. + */ +static ESExpResult * +func_contains (ESExp *esexp, int argc, ESExpResult **argv, void *data) +{ + Query *query; + QueryPrivate *priv; + CalComponent *comp; + const char *field; + const char *str; + gboolean matches; + ESExpResult *result; + + query = QUERY (data); + priv = query->priv; + + g_assert (priv->next_comp != NULL); + comp = priv->next_comp; + + /* Check argument types */ + + if (argc != 2) { + e_sexp_fatal_error (esexp, _("contains? expects 2 arguments")); + return NULL; + } + + if (argv[0]->type != ESEXP_RES_STRING) { + e_sexp_fatal_error (esexp, _("contains? expects argument 1 " + "to be a string")); + return NULL; + } + field = argv[0]->value.string; + + if (argv[1]->type != ESEXP_RES_STRING) { + e_sexp_fatal_error (esexp, _("contains? expects argument 2 " + "to be a string")); + return NULL; + } + str = argv[1]->value.string; + + /* See if it matches */ + + if (strcmp (field, "any") == 0) + matches = matches_any (comp, str); + else if (strcmp (field, "comment") == 0) + matches = matches_comment (comp, str); + else if (strcmp (field, "description") == 0) + matches = matches_description (comp, str); + else if (strcmp (field, "summary") == 0) + matches = matches_summary (comp, str); + else { + e_sexp_fatal_error (esexp, _("contains? expects argument 1 to " + "be one of \"any\", \"summary\", \"description\"")); + return NULL; + } + + result = e_sexp_result_new (esexp, ESEXP_RES_BOOL); + result->value.bool = matches; + + return result; +} + +/* (has-categories? STR+) + * + * STR - At least one string specifying a category + * + * Returns a boolean indicating whether the component has all the specified + * categories. + */ +static ESExpResult * +func_has_categories (ESExp *esexp, int argc, ESExpResult **argv, void *data) +{ + Query *query; + QueryPrivate *priv; + CalComponent *comp; + int i; + GSList *categories; + gboolean matches; + ESExpResult *result; + + query = QUERY (data); + priv = query->priv; + + g_assert (priv->next_comp != NULL); + comp = priv->next_comp; + + /* Check argument types */ + + if (argc < 1) { + e_sexp_fatal_error (esexp, _("has-categories? expects at least 1 argument")); + return NULL; + } + + for (i = 0; i < argc; i++) + if (argv[i]->type != ESEXP_RES_STRING) { + e_sexp_fatal_error (esexp, _("has-categories? expects all arguments " + "to be strings")); + return NULL; + } + + /* Search categories */ + + cal_component_get_categories_list (comp, &categories); + if (!categories) { + result = e_sexp_result_new (esexp, ESEXP_RES_BOOL); + result->value.bool = FALSE; + + return result; + } + + matches = TRUE; + + for (i = 0; i < argc; i++) { + const char *sought; + GSList *l; + gboolean has_category; + + sought = argv[i]->value.string; + + has_category = FALSE; + + for (l = categories; l; l = l->next) { + const char *category; + + category = l->data; + + if (strcmp (category, sought) == 0) { + has_category = TRUE; + break; + } + } + + if (!has_category) { + matches = FALSE; + break; + } + } + + cal_component_free_categories_list (categories); + + result = e_sexp_result_new (esexp, ESEXP_RES_BOOL); + result->value.bool = matches; + + return result; +} + /* Adds a component to our the UIDs hash table and notifies the client */ @@ -612,7 +847,9 @@ static struct { /* Component-related functions */ { "get-vtype", func_get_vtype }, - { "occur-in-time-range?", func_occur_in_time_range } + { "occur-in-time-range?", func_occur_in_time_range }, + { "contains?", func_contains }, + { "has-categories?", func_has_categories } }; /* Initializes a sexp by interning our own symbols */ @@ -681,7 +918,7 @@ match_component (Query *query, const char *uid, &ev); if (ev._major != CORBA_NO_EXCEPTION) - g_message ("process_component_cb(): Could not notify the listener of " + g_message ("match_component(): Could not notify the listener of " "an evaluation error"); CORBA_exception_free (&ev); @@ -696,7 +933,7 @@ match_component (Query *query, const char *uid, &ev); if (ev._major != CORBA_NO_EXCEPTION) - g_message ("process_component_cb(): Could not notify the listener of " + g_message ("match_component(): Could not notify the listener of " "an unexpected result value type when evaluating the " "search expression"); @@ -730,7 +967,6 @@ process_component_cb (gpointer data) if (!priv->pending_uids) { g_assert (priv->n_pending == 0); - g_source_remove (priv->idle_id); priv->idle_id = 0; return FALSE; } @@ -751,11 +987,15 @@ process_component_cb (gpointer data) g_list_free_1 (l); + bonobo_object_ref (BONOBO_OBJECT (query)); + match_component (query, uid, TRUE, priv->pending_total - priv->n_pending, priv->pending_total); + bonobo_object_unref (BONOBO_OBJECT (query)); + g_free (uid); return TRUE; @@ -841,6 +1081,7 @@ backend_opened_cb (CalBackend *backend, CalBackendOpenStatus status, gpointer da if (status == CAL_BACKEND_OPEN_SUCCESS) { g_assert (cal_backend_is_loaded (backend)); + g_assert (priv->idle_id == 0); priv->idle_id = g_idle_add (start_query_cb, query); } @@ -854,8 +1095,12 @@ backend_obj_updated_cb (CalBackend *backend, const char *uid, gpointer data) query = QUERY (data); + bonobo_object_ref (BONOBO_OBJECT (query)); + match_component (query, uid, FALSE, 0, 0); remove_from_pending (query, uid); + + bonobo_object_unref (BONOBO_OBJECT (query)); } /* Callback used when a component is removed from the backend */ @@ -868,8 +1113,12 @@ backend_obj_removed_cb (CalBackend *backend, const char *uid, gpointer data) query = QUERY (data); priv = query->priv; + bonobo_object_ref (BONOBO_OBJECT (query)); + remove_component (query, uid); remove_from_pending (query, uid); + + bonobo_object_unref (BONOBO_OBJECT (query)); } /** @@ -928,9 +1177,10 @@ query_construct (Query *query, /* Queue the query to be started asynchronously */ - if (cal_backend_is_loaded (priv->backend)) + if (cal_backend_is_loaded (priv->backend)) { + g_assert (priv->idle_id == 0); priv->idle_id = g_idle_add (start_query_cb, query); - else + } else gtk_signal_connect (GTK_OBJECT (priv->backend), "opened", GTK_SIGNAL_FUNC (backend_opened_cb), query); @@ -938,6 +1188,16 @@ query_construct (Query *query, return query; } +/** + * query_new: + * @backend: Calendar backend that the query object will monitor. + * @ql: Listener for query results. + * @sexp: Sexp that defines the query. + * + * Creates a new query engine object that monitors a calendar backend. + * + * Return value: A newly-created query object, or NULL on failure. + **/ Query * query_new (CalBackend *backend, GNOME_Evolution_Calendar_QueryListener ql, @@ -946,5 +1206,10 @@ query_new (CalBackend *backend, Query *query; query = QUERY (gtk_type_new (QUERY_TYPE)); - return query_construct (query, backend, ql, sexp); + if (!query_construct (query, backend, ql, sexp)) { + bonobo_object_unref (BONOBO_OBJECT (query)); + return NULL; + } + + return query; } |