aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--calendar/ChangeLog22
-rw-r--r--calendar/gui/alarm-notify/notify-main.c35
-rw-r--r--calendar/gui/gnome-cal.c163
-rw-r--r--calendar/gui/main.c1
-rw-r--r--calendar/pcs/query.c279
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;
}