From f6ee20d9097f3ba7bce189f3015e65721d328122 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Wed, 29 Aug 2001 03:15:54 +0000 Subject: Fixes bug #7879, a query may receive an update notification from the 2001-08-28 Federico Mena Quintero Fixes bug #7879, a query may receive an update notification from the backend before the query itself gets populated. * pcs/query.c (ensure_sexp): New function; ensures that the esexp is created and notifies of parse errors. It is the bulk of start_query_cb() but put in a separate function so that we can share it elsewhere. (start_query_cb): Use ensure_sexp(). (process_component_cb): Oops, notify of a successfully finished query. (match_component): Call ensure_sexp(). This function can be called by the backend notification callbacks before the query is populated, so we need to make sure the esexp exists here. svn path=/trunk/; revision=12509 --- calendar/ChangeLog | 21 ++++++ calendar/gui/cal-search-bar.c | 2 + calendar/gui/comp-editor-factory.c | 96 +++++++++++++++++++++++---- calendar/idl/evolution-calendar.idl | 3 +- calendar/pcs/query.c | 126 ++++++++++++++++++++++++++++-------- 5 files changed, 207 insertions(+), 41 deletions(-) diff --git a/calendar/ChangeLog b/calendar/ChangeLog index bc8b72e02f..8887a33474 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,24 @@ +2001-08-28 Federico Mena Quintero + + Fixes bug #7879, a query may receive an update notification from + the backend before the query itself gets populated. + + * pcs/query.c (ensure_sexp): New function; ensures that the esexp + is created and notifies of parse errors. It is the bulk of + start_query_cb() but put in a separate function so that we can + share it elsewhere. + (start_query_cb): Use ensure_sexp(). + (process_component_cb): Oops, notify of a successfully finished + query. + (match_component): Call ensure_sexp(). This function can be + called by the backend notification callbacks before the query is + populated, so we need to make sure the esexp exists here. + +2001-08-22 Federico Mena Quintero + + * gui/cal-search-bar.c (cal_search_bar_construct): Set the + "category is" criterion as the default for the calendar and tasks. + 2001-08-22 Federico Mena Quintero * gui/dialogs/recurrence-page.c (recurrence_page_fill_widgets): diff --git a/calendar/gui/cal-search-bar.c b/calendar/gui/cal-search-bar.c index e35ba63181..cca017ee8d 100644 --- a/calendar/gui/cal-search-bar.c +++ b/calendar/gui/cal-search-bar.c @@ -467,6 +467,8 @@ cal_search_bar_construct (CalSearchBar *cal_search) e_search_bar_construct (E_SEARCH_BAR (cal_search), search_menu_items, search_option_items); make_suboptions (cal_search); + e_search_bar_set_ids (E_SEARCH_BAR (cal_search), SEARCH_CATEGORY_IS, CATEGORIES_ALL); + return cal_search; } diff --git a/calendar/gui/comp-editor-factory.c b/calendar/gui/comp-editor-factory.c index 8c32bb3059..b235fcbdd0 100644 --- a/calendar/gui/comp-editor-factory.c +++ b/calendar/gui/comp-editor-factory.c @@ -28,7 +28,28 @@ -/* An client we have open */ +/* A pending request */ + +typedef enum { + REQUEST_EXISTING, + REQUEST_NEW +} RequestType; + +typedef struct { + RequestType type; + + union { + struct { + char *uid; + } existing; + + struct { + CalObjType type; + } new; + } u; +} Request; + +/* A client we have open */ typedef struct { /* Uri of the calendar, used as key in the clients hash table */ GnomeVFSURI *uri; @@ -39,8 +60,8 @@ typedef struct { /* Hash table of components that belong to this client */ GHashTable *uid_comp_hash; - /* Number of times this client has been opened */ - int refcount; + /* Pending requests; they are pending if the client is still being opened */ + GSList *pending; } OpenClient; /* A component that is being edited */ @@ -112,17 +133,56 @@ comp_editor_factory_init (CompEditorFactory *factory) priv->uri_client_hash = g_hash_table_new (gnome_vfs_uri_hash, gnome_vfs_uri_hequal); } +/* Used from g_hash_table_foreach(); frees a component structure */ +static void +free_component_cb (gpointer key, gpointer value, gpointer data) +{ + Component *c; + + c = value; + + c->parent = NULL; + c->uid = NULL; + + cal_component_unref (c->comp); + c->comp = NULL; + + g_free (c); +} + /* Used from g_hash_table_foreach(); frees a client structure */ static void free_client_cb (gpointer key, gpointer value, gpointer data) { OpenClient *oc; + GSList *l; oc = value; gnome_vfs_uri_unref (oc->uri); + oc->uri = NULL; + cal_client_unref (oc->client); + oc->client = NULL; + + g_hash_table_foreach (oc->uid_comp_hash, free_component_cb, NULL); g_hash_table_destroy (oc->uid_comp_hash); + oc->uid_comp_hash = NULL; + + for (l = oc->pending; l; l = l->next) { + Request *r; + + r = l->data; + + if (r->type == REQUEST_EXISTING) { + g_assert (r->u.existing.uid != NULL); + g_free (r->u.existing.uid); + } + + g_free (r); + } + g_slist_free (oc->pending); + oc->pending = NULL; g_free (oc); } @@ -153,11 +213,12 @@ comp_editor_factory_destroy (GtkObject *object) -/* Creates a new OpenClient structure by synchronously (!) opening a calendar - * client. Returns NULL if it could not open it. +/* Creates a new OpenClient structure and queues the component editing/creation + * process until the client is open. Returns NULL if it could not issue the + * open request. */ static OpenClient * -open_client (GnomeVFSURI *uri) +open_client (GnomeVFSURI *uri, gboolean only_if_exists) { CalClient *client; @@ -165,15 +226,28 @@ open_client (GnomeVFSURI *uri) if (!client) return NULL; - gtk_signal_connect (GTK_OBJECT (client), "cal_opened", - GTK_SIGNAL_FUNC (cal_opened_cb), NULL); - oc = g_new (OpenClient, 1); + + gnome_vfs_uri_ref (uri); oc->uri = uri; + oc->client = client; oc->uid_comp_hash = g_hash_table_new (g_str_hash, g_str_equal); - oc->refcount = 1; - + oc->pending = NULL; + + gtk_signal_connect (GTK_OBJECT (oc->client), "cal_opened", + GTK_SIGNAL_FUNC (cal_opened_cb), oc); + + if (!cal_client_open_calendar (oc->client, uri, only_if_exists)) { + gnome_vfs_uri_unref (oc->uri); + gtk_object_unref (GTK_OBJECT (oc->client)); + g_hash_table_destroy (oc->uid_comp_hash); + g_free (oc); + + return NULL; + } + + return oc; } static void diff --git a/calendar/idl/evolution-calendar.idl b/calendar/idl/evolution-calendar.idl index 7f4a850ecb..c236f26649 100644 --- a/calendar/idl/evolution-calendar.idl +++ b/calendar/idl/evolution-calendar.idl @@ -288,12 +288,11 @@ module Calendar { interface CompEditorFactory : Bonobo::Unknown { exception InvalidURI {}; exception BackendContactError {}; - exception NotFound {}; exception UnsupportedType {}; /* Loads a calendar and opens an editor for the specified object */ void editExisting (in string uri, in CalObjUID uid) - raises (InvalidURI, BackendContactError, NotFound, UnsupportedType); + raises (InvalidURI, BackendContactError); /* Loads a calendar and creates a new component of the specified type */ void editNew (in string uri, in CalObjType type) diff --git a/calendar/pcs/query.c b/calendar/pcs/query.c index ae76ec9795..1b991a85ea 100644 --- a/calendar/pcs/query.c +++ b/calendar/pcs/query.c @@ -36,6 +36,14 @@ +/* States of a query */ +typedef enum { + QUERY_START_PENDING, /* the query is not populated yet */ + QUERY_IN_PROGRESS, /* the query is populated; components are still being processed */ + QUERY_DONE, /* the query is done, but still accepts object changes */ + QUERY_PARSE_ERROR /* a parse error occurred when initially creating the ESexp */ +} QueryState; + /* Private part of the Query structure */ struct _QueryPrivate { /* The backend we are monitoring */ @@ -48,8 +56,9 @@ struct _QueryPrivate { char *sexp; ESExp *esexp; - /* Idle handler ID for asynchronous queries */ + /* Idle handler ID for asynchronous queries and the current state */ guint idle_id; + QueryState state; /* List of UIDs that we still have to process */ GList *pending_uids; @@ -110,6 +119,9 @@ query_init (Query *query) priv->ql = CORBA_OBJECT_NIL; priv->sexp = NULL; + priv->idle_id = 0; + priv->state = QUERY_START_PENDING; + priv->pending_uids = NULL; priv->uids = g_hash_table_new (g_str_hash, g_str_equal); @@ -954,6 +966,66 @@ create_sexp (Query *query) return esexp; } +/* Ensures that the sexp has been parsed and the ESexp has been created. If a + * parse error occurs, it sets the query state to QUERY_PARSE_ERROR and returns + * FALSE. + */ +static gboolean +ensure_sexp (Query *query) +{ + QueryPrivate *priv; + + priv = query->priv; + + if (priv->state == QUERY_PARSE_ERROR) + g_assert_not_reached (); /* we should already have terminated everything */ + + if (priv->esexp) + return TRUE; + + /* Compile the query string */ + + priv->esexp = create_sexp (query); + + g_assert (priv->sexp != NULL); + e_sexp_input_text (priv->esexp, priv->sexp, strlen (priv->sexp)); + + if (e_sexp_parse (priv->esexp) == -1) { + const char *error_str; + CORBA_Environment ev; + + /* Change the state and disconnect from any notifications */ + + priv->state = QUERY_PARSE_ERROR; + gtk_signal_disconnect_by_data (GTK_OBJECT (priv->backend), query); + + /* Report the error to the listener */ + + error_str = e_sexp_error (priv->esexp); + g_assert (error_str != NULL); + + CORBA_exception_init (&ev); + GNOME_Evolution_Calendar_QueryListener_notifyQueryDone ( + priv->ql, + GNOME_Evolution_Calendar_QueryListener_PARSE_ERROR, + error_str, + &ev); + + if (ev._major != CORBA_NO_EXCEPTION) + g_message ("ensure_sexp(): Could not notify the listener of " + "a parse error"); + + CORBA_exception_free (&ev); + + e_sexp_unref (priv->esexp); + priv->esexp = NULL; + + return FALSE; + } + + return TRUE; +} + /* Evaluates the query sexp on the specified component and notifies the listener * as appropriate. */ @@ -967,6 +1039,11 @@ match_component (Query *query, const char *uid, priv = query->priv; + g_assert (priv->state != QUERY_PARSE_ERROR); + + if (!ensure_sexp (query)) + return; + comp = cal_backend_get_object_component (priv->backend, uid); g_assert (comp != NULL); gtk_object_ref (GTK_OBJECT (comp)); @@ -1041,9 +1118,26 @@ process_component_cb (gpointer data) /* No more components? */ if (!priv->pending_uids) { + CORBA_Environment ev; + g_assert (priv->n_pending == 0); priv->idle_id = 0; + priv->state = QUERY_DONE; + + CORBA_exception_init (&ev); + GNOME_Evolution_Calendar_QueryListener_notifyQueryDone ( + priv->ql, + GNOME_Evolution_Calendar_QueryListener_SUCCESS, + "", + &ev); + + if (ev._major != CORBA_NO_EXCEPTION) + g_message ("process_component_cb(): Could not notify the listener of " + "a finished query"); + + CORBA_exception_free (&ev); + return FALSE; } @@ -1087,12 +1181,14 @@ populate_query (Query *query) priv = query->priv; g_assert (priv->idle_id == 0); + g_assert (priv->state == QUERY_START_PENDING); priv->pending_uids = cal_backend_get_uids (priv->backend, CALOBJ_TYPE_ANY); priv->pending_total = g_list_length (priv->pending_uids); priv->n_pending = priv->pending_total; priv->idle_id = g_idle_add (process_component_cb, query); + priv->state = QUERY_IN_PROGRESS; } /* Idle handler for starting a query */ @@ -1101,40 +1197,14 @@ start_query_cb (gpointer data) { Query *query; QueryPrivate *priv; - CORBA_Environment ev; query = QUERY (data); priv = query->priv; priv->idle_id = 0; - priv->esexp = create_sexp (query); - - /* Compile the query string */ - - g_assert (priv->sexp != NULL); - e_sexp_input_text (priv->esexp, priv->sexp, strlen (priv->sexp)); - - if (e_sexp_parse (priv->esexp) == -1) { - const char *error_str; - - error_str = e_sexp_error (priv->esexp); - g_assert (error_str != NULL); - - CORBA_exception_init (&ev); - GNOME_Evolution_Calendar_QueryListener_notifyQueryDone ( - priv->ql, - GNOME_Evolution_Calendar_QueryListener_PARSE_ERROR, - error_str, - &ev); - - if (ev._major != CORBA_NO_EXCEPTION) - g_message ("start_query_cb(): Could not notify the listener of " - "a parse error"); - - CORBA_exception_free (&ev); + if (!ensure_sexp (query)) return FALSE; - } /* Populate the query with UIDs so that we can process them asynchronously */ -- cgit v1.2.3