diff options
Diffstat (limited to 'calendar')
-rw-r--r-- | calendar/ChangeLog | 66 | ||||
-rw-r--r-- | calendar/gui/cal-search-bar.c | 280 | ||||
-rw-r--r-- | calendar/gui/cal-search-bar.h | 10 | ||||
-rw-r--r-- | calendar/gui/calendar-model.c | 87 | ||||
-rw-r--r-- | calendar/gui/calendar-model.h | 6 | ||||
-rw-r--r-- | calendar/gui/e-tasks.c | 334 | ||||
-rw-r--r-- | calendar/gui/e-tasks.h | 5 | ||||
-rw-r--r-- | calendar/gui/gnome-cal.c | 21 | ||||
-rw-r--r-- | calendar/gui/tasks-control.c | 8 | ||||
-rw-r--r-- | calendar/pcs/cal-backend-file.c | 104 | ||||
-rw-r--r-- | calendar/pcs/query.c | 37 |
11 files changed, 561 insertions, 397 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index ef7b73a460..05fb471217 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,69 @@ +2001-08-01 Federico Mena Quintero <federico@ximian.com> + + The calendar search bar widget now includes a drop-down menu of + available categories. + + * pcs/query.c (func_has_categories): Handle one and only one #f + value as meaning "unfiled", for components that have no categories + at all. + + * pcs/cal-backend-file.c (open_cal): Duh, do not notify here about + changed categories since at this point we don't have any clients + bound to us yet. + (create_cal): Likewise. + (cal_backend_file_add_cal): Notify here. + + * gui/cal-search-bar.h (CalSearchBarClass): New signal + "category_changed". + + * gui/cal-search-bar.c (cal_search_bar_construct): Add a drop-down + menu for the list of categories. + (search_option_items): Removed the "Has category" option, since we + now have the drop-down menu instad and it would be confusing to + have both options. + (regen_query): Likewise. Also, this function is now the old + cal_search_bar_query_changed() and is shared by that very function + and by the callback from the drop-down menu. + (notify_query_contains): Include the sub-sexp for the categories. + (cal_search_bar_set_categories): New function. + (cal_search_bar_get_category): New function. + (categories_selection_done_cb): Emit the "category_changed" signal. + + * gui/e-tasks.c (obj_updated_cb): Removed function since it did + not do anything; all updates are handled by the CalendarModel. + (obj_removed_cb): Likewise. + (ETasksPrivate): Removed the fields for the categories option + menu, since now it is in the ESearchBar. + (search_bar_sexp_changed_cb): Use calendar_model_set_query() + directly here, as we do not need to frob the sexp anymore. + (update_query): Removed. + (client_categories_changed_cb): New callback. + (search_bar_category_changed_cb): New callback. + (e_tasks_new_task): Set the default category on the component to + the one that is selected in the search bar. + (e_tasks_on_filter_selected): Removed. + (e_tasks_on_categories_changed): Removed. + (e_tasks_rebuild_categories_menu): Removed. + (e_tasks_add_menu_item): Removed. + (e_tasks_setup_view_menus): Sanitized not to sink objects wildly. + (e_tasks_discard_view_menus): New function. + + * gui/calendar-model.h (CalendarModelClass): Removed the + "categories_changed" signal since this is handled in the Wombat + now. + + * gui/calendar-model.c (calendar_model_get_categories): Removed. + (calendar_model_set_value_at): Do not collect the categories. + (query_obj_updated_cb): Likewise. + (calendar_model_collect_categories): Removed. + (calendar_model_set_default_category): Constify. + + * gui/tasks-control.c (tasks_control_deactivate): Call + e_tasks_discard_view_menus(). + + * gui/gnome-cal.c (search_bar_category_changed_cb): Set the + default category for the task pad's model. + 2001-07-31 Federico Mena Quintero <federico@ximian.com> The Wombat now keeps track of which categories are present in the diff --git a/calendar/gui/cal-search-bar.c b/calendar/gui/cal-search-bar.c index 351f65c5e6..e5cfcf95e9 100644 --- a/calendar/gui/cal-search-bar.c +++ b/calendar/gui/cal-search-bar.c @@ -24,9 +24,13 @@ #endif #include <glib.h> +#include <gtk/gtkmenu.h> +#include <gtk/gtkmenuitem.h> +#include <gtk/gtkoptionmenu.h> #include <gtk/gtksignal.h> #include <libgnome/gnome-defs.h> #include <libgnome/gnome-i18n.h> +#include <gal/widgets/e-unicode.h> #include "cal-search-bar.h" @@ -42,8 +46,7 @@ enum { SEARCH_ANY_FIELD_CONTAINS, SEARCH_SUMMARY_CONTAINS, SEARCH_DESCRIPTION_CONTAINS, - SEARCH_COMMENT_CONTAINS, - SEARCH_HAS_CATEGORY + SEARCH_COMMENT_CONTAINS }; static ESearchBarItem search_option_items[] = { @@ -51,13 +54,19 @@ static ESearchBarItem search_option_items[] = { { 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 } }; +/* Private part of the CalSearchBar structure */ +struct CalSearchBarPrivate { + /* Option menu for the categories drop-down */ + GtkOptionMenu *categories_omenu; +}; + static void cal_search_bar_class_init (CalSearchBarClass *class); +static void cal_search_bar_init (CalSearchBar *cal_search); static void cal_search_bar_query_changed (ESearchBar *search); static void cal_search_bar_menu_activated (ESearchBar *search, int item); @@ -65,6 +74,7 @@ static void cal_search_bar_menu_activated (ESearchBar *search, int item); /* Signal IDs */ enum { SEXP_CHANGED, + CATEGORY_CHANGED, LAST_SIGNAL }; @@ -91,7 +101,7 @@ cal_search_bar_get_type (void) sizeof (CalSearchBar), sizeof (CalSearchBarClass), (GtkClassInitFunc) cal_search_bar_class_init, - (GtkObjectInitFunc) NULL, + (GtkObjectInitFunc) cal_search_bar_init, NULL, /* reserved_1 */ NULL, /* reserved_2 */ (GtkClassInitFunc) NULL @@ -122,14 +132,36 @@ cal_search_bar_class_init (CalSearchBarClass *class) GTK_TYPE_NONE, 1, GTK_TYPE_STRING); + cal_search_bar_signals[CATEGORY_CHANGED] = + gtk_signal_new ("category_changed", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (CalSearchBarClass, category_changed), + gtk_marshal_NONE__STRING, + GTK_TYPE_NONE, 1, + GTK_TYPE_STRING); + gtk_object_class_add_signals (object_class, cal_search_bar_signals, LAST_SIGNAL); class->sexp_changed = NULL; + class->category_changed = NULL; e_search_bar_class->query_changed = cal_search_bar_query_changed; e_search_bar_class->menu_activated = cal_search_bar_menu_activated; } +/* Object initialization function for the calendar search bar */ +static void +cal_search_bar_init (CalSearchBar *cal_search) +{ + CalSearchBarPrivate *priv; + + priv = g_new (CalSearchBarPrivate, 1); + cal_search->priv = priv; + + priv->categories_omenu = NULL; +} + /* Emits the "sexp_changed" signal for the calendar search bar */ @@ -140,33 +172,93 @@ notify_sexp_changed (CalSearchBar *cal_search, const char *sexp) sexp); } +/* Returns the string of the currently selected category, NULL for "Unmatched", + * or (const char *) 1 for "All". + */ +static const char * +get_current_category (CalSearchBar *cal_search) +{ + CalSearchBarPrivate *priv; + GtkMenu *menu; + GtkWidget *active; + const char *category; + + priv = cal_search->priv; + + menu = GTK_MENU (gtk_option_menu_get_menu (priv->categories_omenu)); + + active = gtk_menu_get_active (menu); + g_assert (active != NULL); + + category = gtk_object_get_user_data (GTK_OBJECT (active)); + return category; +} + +/* Returns a sexp for the selected category in the drop-down menu. The "All" + * option is returned as (const char *) 1, and the "Unfiled" option is returned + * as NULL. + */ +static char * +get_category_sexp (CalSearchBar *cal_search) +{ + const char *category; + + category = get_current_category (cal_search); + + if (category == NULL) + return g_strdup ("(has-categories? #f)"); /* Unfiled items */ + else if (category == (const char *) 1) + return NULL; /* All items */ + else + return g_strdup_printf ("(has-categories? \"%s\")", category); /* Specific category */ +} + /* Sets the query string to be (contains? "field" "text") */ static void notify_query_contains (CalSearchBar *cal_search, const char *field, const char *text) { char *sexp; + char *category_sexp; + + category_sexp = get_category_sexp (cal_search); + + if (category_sexp) + /* "Contains" sexp plus a sexp for a category or for unfiled items */ + sexp = g_strdup_printf ("(and (contains? \"%s\" \"%s\")" + " %s)", + field, text, category_sexp); + else + /* "Contains" sexp; matches any category */ + sexp = g_strdup_printf ("(contains? \"%s\" \"%s\")", field, text); - sexp = g_strdup_printf ("(contains? \"%s\" \"%s\")", field, text); notify_sexp_changed (cal_search, sexp); + + if (category_sexp) + g_free (category_sexp); + g_free (sexp); } -/* query_changed handler for the calendar search bar */ +/* Creates a new query from the values in the widgets and notifies upstream */ static void -cal_search_bar_query_changed (ESearchBar *search) +regen_query (CalSearchBar *cal_search) { - CalSearchBar *cal_search; + CalSearchBarPrivate *priv; int item; char *text; - cal_search = CAL_SEARCH_BAR (search); + priv = cal_search->priv; - item = e_search_bar_get_option_choice (search); - text = e_search_bar_get_text (search); + /* Fetch the data from the ESearchBar's entry widgets */ + + item = e_search_bar_get_option_choice (E_SEARCH_BAR (cal_search)); + text = e_search_bar_get_text (E_SEARCH_BAR (cal_search)); if (!text) return; /* This is an error in the UTF8 conversion, not an empty string! */ + /* Generate the different types of queries */ + switch (item) { case SEARCH_ANY_FIELD_CONTAINS: notify_query_contains (cal_search, "any", text); @@ -184,15 +276,6 @@ cal_search_bar_query_changed (ESearchBar *search) notify_query_contains (cal_search, "comment", text); break; - case SEARCH_HAS_CATEGORY: { - char *sexp; - - sexp = g_strdup_printf ("(has-categories? \"%s\")", text); - notify_sexp_changed (cal_search, sexp); - g_free (sexp); - break; - } - default: g_assert_not_reached (); } @@ -200,6 +283,16 @@ cal_search_bar_query_changed (ESearchBar *search) g_free (text); } +/* query_changed handler for the calendar search bar */ +static void +cal_search_bar_query_changed (ESearchBar *search) +{ + CalSearchBar *cal_search; + + cal_search = CAL_SEARCH_BAR (search); + regen_query (cal_search); +} + /* menu_activated handler for the calendar search bar */ static void cal_search_bar_menu_activated (ESearchBar *search, int item) @@ -224,6 +317,39 @@ cal_search_bar_menu_activated (ESearchBar *search, int item) +/* Callback used when an item is selected in the categories option menu */ +static void +categories_selection_done_cb (GtkMenuShell *menu_shell, gpointer data) +{ + CalSearchBar *cal_search; + const char *category; + + cal_search = CAL_SEARCH_BAR (data); + regen_query (cal_search); + + category = cal_search_bar_get_category (cal_search); + gtk_signal_emit (GTK_OBJECT (cal_search), cal_search_bar_signals[CATEGORY_CHANGED], + category); +} + +/* Creates the option menu of categories */ +static void +setup_categories_omenu (CalSearchBar *cal_search) +{ + CalSearchBarPrivate *priv; + GtkWidget *label; + + priv = cal_search->priv; + + priv->categories_omenu = GTK_OPTION_MENU (gtk_option_menu_new ()); + gtk_box_pack_end (GTK_BOX (cal_search), GTK_WIDGET (priv->categories_omenu), FALSE, FALSE, 0); + gtk_widget_show (GTK_WIDGET (priv->categories_omenu)); + + label = gtk_label_new (_("Category:")); + gtk_box_pack_end (GTK_BOX (cal_search), label, FALSE, FALSE, 4); + gtk_widget_show (label); +} + /** * cal_search_bar_construct: * @cal_search: A calendar search bar. @@ -239,6 +365,8 @@ cal_search_bar_construct (CalSearchBar *cal_search) g_return_val_if_fail (IS_CAL_SEARCH_BAR (cal_search), NULL); e_search_bar_construct (E_SEARCH_BAR (cal_search), search_menu_items, search_option_items); + setup_categories_omenu (cal_search); + return cal_search; } @@ -258,3 +386,115 @@ cal_search_bar_new (void) cal_search = gtk_type_new (TYPE_CAL_SEARCH_BAR); return GTK_WIDGET (cal_search_bar_construct (cal_search)); } + +/* Callback used when a categories menu item is destroyed. We free its user + * data, which is the category string. + */ +static void +item_destroyed_cb (GtkObject *object, gpointer data) +{ + char *category; + + category = gtk_object_get_user_data (object); + g_assert (category != NULL); + + g_free (category); +} + +/** + * cal_search_bar_set_categories: + * @cal_search: A calendar search bar. + * @categories: Array of pointers to strings for the category names. + * + * Sets the list of categories that are to be shown in the drop-down list + * of a calendar search bar. The search bar will automatically add an item + * for "unfiled" components, that is, those that have no categories assigned + * to them. + **/ +void +cal_search_bar_set_categories (CalSearchBar *cal_search, GPtrArray *categories) +{ + CalSearchBarPrivate *priv; + GtkMenu *menu; + GtkWidget *item; + int i; + + g_return_if_fail (cal_search != NULL); + g_return_if_fail (IS_CAL_SEARCH_BAR (cal_search)); + g_return_if_fail (categories != NULL); + + priv = cal_search->priv; + + menu = GTK_MENU (gtk_menu_new ()); + gtk_signal_connect (GTK_OBJECT (menu), "selection_done", + GTK_SIGNAL_FUNC (categories_selection_done_cb), cal_search); + + /* All, Unmatched, separator items */ + + item = gtk_menu_item_new_with_label (_("All")); + gtk_object_set_user_data (GTK_OBJECT (item), (char *) 1); + gtk_menu_append (menu, item); + gtk_widget_show (item); + + item = gtk_menu_item_new_with_label (_("Unfiled")); + gtk_object_set_user_data (GTK_OBJECT (item), NULL); + gtk_menu_append (menu, item); + gtk_widget_show (item); + + if (categories->len > 0) { + item = gtk_menu_item_new (); + gtk_widget_set_sensitive (item, FALSE); + gtk_menu_append (menu, item); + gtk_widget_show (item); + } + + /* Categories items */ + + for (i = 0; i < categories->len; i++) { + char *str; + + /* FIXME: Put the category icons here */ + + str = e_utf8_to_gtk_string (GTK_WIDGET (menu), categories->pdata[i]); + if (!str) + continue; + + item = gtk_menu_item_new_with_label (str); + g_free (str); + + gtk_object_set_user_data (GTK_OBJECT (item), g_strdup (categories->pdata[i])); + gtk_signal_connect (GTK_OBJECT (item), "destroy", + GTK_SIGNAL_FUNC (item_destroyed_cb), + NULL); + + gtk_menu_append (menu, item); + gtk_widget_show (item); + } + + /* Set the new menu; the old one will be destroyed automatically */ + + gtk_option_menu_set_menu (priv->categories_omenu, GTK_WIDGET (menu)); +} + +/** + * cal_search_bar_get_category: + * @cal_search: A calendar search bar. + * + * Queries the currently selected category name in a calendar search bar. + * If "All" or "Unfiled" are selected, this function will return NULL. + * + * Return value: Name of the selected category, or NULL if there is no + * selected category. + **/ +const char * +cal_search_bar_get_category (CalSearchBar *cal_search) +{ + const char *category; + + category = get_current_category (cal_search); + + if (!category || category == (const char *) 1) + return NULL; + else + return category; +} diff --git a/calendar/gui/cal-search-bar.h b/calendar/gui/cal-search-bar.h index 1317369371..3142952482 100644 --- a/calendar/gui/cal-search-bar.h +++ b/calendar/gui/cal-search-bar.h @@ -37,8 +37,13 @@ BEGIN_GNOME_DECLS #define IS_CAL_SEARCH_BAR(obj) (GTK_CHECK_TYPE ((obj), TYPE_CAL_SEARCH_BAR)) #define IS_CAL_SEARCH_BAR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), TYPE_CAL_SEARCH_BAR)) +typedef struct CalSearchBarPrivate CalSearchBarPrivate; + typedef struct { ESearchBar search_bar; + + /* Private data */ + CalSearchBarPrivate *priv; } CalSearchBar; typedef struct { @@ -47,6 +52,7 @@ typedef struct { /* Notification signals */ void (* sexp_changed) (CalSearchBar *cal_search, const char *sexp); + void (* category_changed) (CalSearchBar *cal_search, const char *category); } CalSearchBarClass; GtkType cal_search_bar_get_type (void); @@ -55,6 +61,10 @@ CalSearchBar *cal_search_bar_construct (CalSearchBar *cal_search); GtkWidget *cal_search_bar_new (void); +void cal_search_bar_set_categories (CalSearchBar *cal_search, GPtrArray *categories); + +const char *cal_search_bar_get_category (CalSearchBar *cal_search); + END_GNOME_DECLS diff --git a/calendar/gui/calendar-model.c b/calendar/gui/calendar-model.c index 0cdd97b7d6..b41c0d2519 100644 --- a/calendar/gui/calendar-model.c +++ b/calendar/gui/calendar-model.c @@ -69,20 +69,10 @@ struct _CalendarModelPrivate { creating a new task. */ gchar *default_category; - /* A balanced tree of the categories used by all the tasks/events. */ - GTree *categories; - /* The current timezone. */ icaltimezone *zone; }; -enum { - CATEGORIES_CHANGED, - LAST_SIGNAL -}; - -static gint calendar_model_signals [LAST_SIGNAL] = { 0 }; - static void calendar_model_class_init (CalendarModelClass *class); @@ -104,8 +94,6 @@ static int remove_object (CalendarModel *model, const char *uid); static void ensure_task_complete (CalComponent *comp, time_t completed_date); static void ensure_task_not_complete (CalComponent *comp); -static gboolean calendar_model_collect_categories (CalendarModel *model, - CalComponent *comp); static ETableModelClass *parent_class; @@ -155,17 +143,6 @@ calendar_model_class_init (CalendarModelClass *class) parent_class = gtk_type_class (E_TABLE_MODEL_TYPE); - calendar_model_signals [CATEGORIES_CHANGED] = - gtk_signal_new ("categories-changed", - GTK_RUN_LAST, object_class->type, - GTK_SIGNAL_OFFSET (CalendarModelClass, - categories_changed), - gtk_signal_default_marshaller, - GTK_TYPE_NONE, 0); - - gtk_object_class_add_signals (object_class, calendar_model_signals, - LAST_SIGNAL); - object_class->destroy = calendar_model_destroy; etm_class->column_count = calendar_model_column_count; @@ -179,8 +156,6 @@ calendar_model_class_init (CalendarModelClass *class) etm_class->initialize_value = calendar_model_initialize_value; etm_class->value_is_empty = calendar_model_value_is_empty; etm_class->value_to_string = calendar_model_value_to_string; - - class->categories_changed = NULL; } /* Object initialization function for the calendar table model */ @@ -200,8 +175,6 @@ calendar_model_init (CalendarModel *model) priv->new_comp_vtype = CAL_COMPONENT_EVENT; priv->use_24_hour_format = TRUE; - priv->categories = g_tree_new ((GCompareFunc)strcmp); - priv->zone = NULL; } @@ -285,12 +258,6 @@ calendar_model_destroy (GtkObject *object) g_free (priv->default_category); - /* We only need to free the first argument, the key, so g_free will do. - */ - g_tree_traverse (priv->categories, (GTraverseFunc) g_free, - G_PRE_ORDER, NULL); - g_tree_destroy (priv->categories); - /* Free the private structure */ g_free (priv); @@ -1338,10 +1305,6 @@ calendar_model_set_value_at (ETableModel *etm, int col, int row, const void *val switch (col) { case CAL_COMPONENT_FIELD_CATEGORIES: set_categories (comp, value); - if (calendar_model_collect_categories (model, comp)) { - gtk_signal_emit (GTK_OBJECT (model), - calendar_model_signals [CATEGORIES_CHANGED]); - } break; case CAL_COMPONENT_FIELD_CLASSIFICATION: @@ -1815,14 +1778,6 @@ query_obj_updated_cb (CalQuery *query, const char *uid, e_table_model_row_changed (E_TABLE_MODEL (model), *new_idx); } - /* See if we need to add any categories. Note that old - categories won't be removed, but I don't think that matters - too much here. */ - if (calendar_model_collect_categories (model, new_comp)) { - gtk_signal_emit (GTK_OBJECT (model), - calendar_model_signals [CATEGORIES_CHANGED]); - } - break; case CAL_CLIENT_GET_NOT_FOUND: @@ -2330,7 +2285,7 @@ calendar_model_set_use_24_hour_format (CalendarModel *model, void calendar_model_set_default_category (CalendarModel *model, - gchar *default_category) + const char *default_category) { g_return_if_fail (IS_CALENDAR_MODEL (model)); @@ -2340,46 +2295,6 @@ calendar_model_set_default_category (CalendarModel *model, -static gboolean -calendar_model_collect_categories (CalendarModel *model, - CalComponent *comp) -{ - CalendarModelPrivate *priv; - GSList *categories_list, *elem; - gboolean changed = FALSE; - - priv = model->priv; - - cal_component_get_categories_list (comp, &categories_list); - - for (elem = categories_list; elem; elem = elem->next) { - if (!g_tree_lookup (priv->categories, elem->data)) { - /* We store a '1' as the data, just so we can use - g_tree_lookup() on it. Note that we don't free - the string since it is now part of the tree. */ - g_tree_insert (priv->categories, elem->data, - GINT_TO_POINTER (1)); - changed = TRUE; - } else { - g_free (elem->data); - } - } - - g_slist_free (categories_list); - - return changed; -} - - -GTree* -calendar_model_get_categories (CalendarModel *model) -{ - g_return_val_if_fail (IS_CALENDAR_MODEL (model), NULL); - - return model->priv->categories; -} - - /* The current timezone. */ icaltimezone* calendar_model_get_timezone (CalendarModel *model) diff --git a/calendar/gui/calendar-model.h b/calendar/gui/calendar-model.h index c06b1f3f48..5e1350100d 100644 --- a/calendar/gui/calendar-model.h +++ b/calendar/gui/calendar-model.h @@ -52,8 +52,6 @@ struct _CalendarModel { struct _CalendarModelClass { ETableModelClass parent_class; - - void (* categories_changed) (CalendarModel *model); }; GtkType calendar_model_get_type (void); @@ -88,10 +86,8 @@ icaltimezone* calendar_model_get_timezone (CalendarModel *model); void calendar_model_set_timezone (CalendarModel *model, icaltimezone *zone); -GTree* calendar_model_get_categories (CalendarModel *model); - void calendar_model_set_default_category (CalendarModel *model, - gchar *default_category); + const char *default_category); diff --git a/calendar/gui/e-tasks.c b/calendar/gui/e-tasks.c index e0a0b8ef8f..9ea9bc6e2d 100644 --- a/calendar/gui/e-tasks.c +++ b/calendar/gui/e-tasks.c @@ -51,16 +51,8 @@ struct _ETasksPrivate { /* The ECalendarTable showing the tasks. */ GtkWidget *tasks_view; - /* Search bar for tasks and the current sexp */ + /* Calendar search bar for tasks */ GtkWidget *search_bar; - char *sexp; - - /* The option menu showing the categories, and the popup menu. */ - GtkWidget *categories_option_menu; - GtkWidget *categories_menu; - - /* The category that is currently selected, used to filter out items */ - char *category; /* View collection and the view menus handler */ GalViewCollection *view_collection; @@ -74,20 +66,9 @@ static void setup_widgets (ETasks *tasks); static void e_tasks_destroy (GtkObject *object); static void cal_opened_cb (CalClient *client, CalClientOpenStatus status, gpointer data); -static void obj_updated_cb (CalClient *client, const char *uid, gpointer data); -static void obj_removed_cb (CalClient *client, const char *uid, gpointer data); static char* e_tasks_get_config_filename (ETasks *tasks); -static void e_tasks_on_filter_selected (GtkMenuShell *menu_shell, - ETasks *tasks); -static void e_tasks_on_categories_changed (CalendarModel *model, - ETasks *tasks); -static void e_tasks_rebuild_categories_menu (ETasks *tasks); -static gint e_tasks_add_menu_item (gpointer key, - gpointer value, - gpointer data); - /* Signal IDs */ enum { SELECTION_CHANGED, @@ -139,8 +120,8 @@ e_tasks_init (ETasks *tasks) priv = g_new0 (ETasksPrivate, 1); tasks->priv = priv; - priv->sexp = g_strdup ("#t"); /* Match all */ - priv->category = NULL; + priv->view_collection = NULL; + priv->view_menus = NULL; setup_widgets (tasks); } @@ -159,49 +140,34 @@ table_selection_change_cb (ETable *etable, gpointer data) n_selected); } -/* Updates the query in the table model by composing the currently selected - * category with the current sexp. - */ +/* Callback used when the sexp in the search bar changes */ static void -update_query (ETasks *tasks) +search_bar_sexp_changed_cb (CalSearchBar *cal_search, const char *sexp, gpointer data) { + ETasks *tasks; ETasksPrivate *priv; - char *new_sexp; - gboolean free_new_sexp; CalendarModel *model; + tasks = E_TASKS (data); priv = tasks->priv; - g_assert (priv->sexp != NULL); - - if (priv->category) { - new_sexp = g_strdup_printf ("(and %s (has-categories? \"%s\"))", - priv->sexp, priv->category); - free_new_sexp = TRUE; - } else { - new_sexp = priv->sexp; - free_new_sexp = FALSE; - } - model = e_calendar_table_get_model (E_CALENDAR_TABLE (priv->tasks_view)); - calendar_model_set_query (model, new_sexp); - - if (free_new_sexp) - g_free (new_sexp); + calendar_model_set_query (model, sexp); } -/* Callback used when the sexp in the search bar changes */ +/* Callback used when the selected category in the search bar changes */ static void -search_bar_sexp_changed_cb (CalSearchBar *cal_search, const char *sexp, gpointer data) +search_bar_category_changed_cb (CalSearchBar *cal_search, const char *category, gpointer data) { ETasks *tasks; ETasksPrivate *priv; + CalendarModel *model; tasks = E_TASKS (data); priv = tasks->priv; - priv->sexp = g_strdup (sexp); - update_query (tasks); + model = e_calendar_table_get_model (E_CALENDAR_TABLE (priv->tasks_view)); + calendar_model_set_default_category (model, category); } #define E_TASKS_TABLE_DEFAULT_STATE \ @@ -220,58 +186,49 @@ setup_widgets (ETasks *tasks) { ETasksPrivate *priv; ETable *etable; - GtkWidget *hbox, *menuitem, *categories_label; CalendarModel *model; priv = tasks->priv; - hbox = gtk_hbox_new (FALSE, GNOME_PAD_SMALL); - gtk_widget_show (hbox); - gtk_table_attach (GTK_TABLE (tasks), hbox, 0, 1, 0, 1, - GTK_EXPAND | GTK_FILL, 0, 0, 0); - priv->search_bar = cal_search_bar_new (); gtk_signal_connect (GTK_OBJECT (priv->search_bar), "sexp_changed", GTK_SIGNAL_FUNC (search_bar_sexp_changed_cb), tasks); - gtk_box_pack_start (GTK_BOX (hbox), priv->search_bar, TRUE, TRUE, 0); - gtk_widget_show (priv->search_bar); - - priv->categories_option_menu = gtk_option_menu_new (); - gtk_widget_show (priv->categories_option_menu); - gtk_box_pack_end (GTK_BOX (hbox), priv->categories_option_menu, - FALSE, FALSE, 0); - - priv->categories_menu = gtk_menu_new (); - - menuitem = gtk_menu_item_new_with_label (_("All")); - gtk_widget_show (menuitem); - gtk_menu_append (GTK_MENU (priv->categories_menu), menuitem); - - gtk_option_menu_set_menu (GTK_OPTION_MENU (priv->categories_option_menu), priv->categories_menu); - - categories_label = gtk_label_new (_("Category:")); - gtk_widget_show (categories_label); - gtk_box_pack_end (GTK_BOX (hbox), categories_label, FALSE, FALSE, 4); + gtk_signal_connect (GTK_OBJECT (priv->search_bar), "category_changed", + GTK_SIGNAL_FUNC (search_bar_category_changed_cb), tasks); + gtk_table_attach (GTK_TABLE (tasks), priv->search_bar, 0, 1, 0, 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtk_widget_show (priv->search_bar); priv->tasks_view = e_calendar_table_new (); model = e_calendar_table_get_model (E_CALENDAR_TABLE (priv->tasks_view)); calendar_model_set_new_comp_vtype (model, CAL_COMPONENT_TODO); - etable = e_table_scrolled_get_table (E_TABLE_SCROLLED (E_CALENDAR_TABLE (priv->tasks_view)->etable)); + + etable = e_table_scrolled_get_table ( + E_TABLE_SCROLLED (E_CALENDAR_TABLE (priv->tasks_view)->etable)); e_table_set_state (etable, E_TASKS_TABLE_DEFAULT_STATE); gtk_table_attach (GTK_TABLE (tasks), priv->tasks_view, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_widget_show (priv->tasks_view); - calendar_config_configure_e_calendar_table (E_CALENDAR_TABLE (priv->tasks_view)); - gtk_signal_connect (GTK_OBJECT (E_CALENDAR_TABLE (priv->tasks_view)->model), - "categories-changed", - GTK_SIGNAL_FUNC (e_tasks_on_categories_changed), tasks); + calendar_config_configure_e_calendar_table (E_CALENDAR_TABLE (priv->tasks_view)); gtk_signal_connect (GTK_OBJECT (etable), "selection_change", GTK_SIGNAL_FUNC (table_selection_change_cb), tasks); } +/* Callback used when the set of categories changes in the calendar client */ +static void +client_categories_changed_cb (CalClient *client, GPtrArray *categories, gpointer data) +{ + ETasks *tasks; + ETasksPrivate *priv; + + tasks = E_TASKS (data); + priv = tasks->priv; + + cal_search_bar_set_categories (CAL_SEARCH_BAR (priv->search_bar), categories); +} GtkWidget * e_tasks_construct (ETasks *tasks) @@ -290,10 +247,8 @@ e_tasks_construct (ETasks *tasks) gtk_signal_connect (GTK_OBJECT (priv->client), "cal_opened", GTK_SIGNAL_FUNC (cal_opened_cb), tasks); - gtk_signal_connect (GTK_OBJECT (priv->client), "obj_updated", - GTK_SIGNAL_FUNC (obj_updated_cb), tasks); - gtk_signal_connect (GTK_OBJECT (priv->client), "obj_removed", - GTK_SIGNAL_FUNC (obj_removed_cb), tasks); + gtk_signal_connect (GTK_OBJECT (priv->client), "categories_changed", + GTK_SIGNAL_FUNC (client_categories_changed_cb), tasks); model = e_calendar_table_get_model (E_CALENDAR_TABLE (priv->tasks_view)); g_assert (model != NULL); @@ -336,15 +291,6 @@ e_tasks_destroy (GtkObject *object) tasks = E_TASKS (object); priv = tasks->priv; - g_assert (priv->sexp != NULL); - g_free (priv->sexp); - priv->sexp = NULL; - - if (priv->category) { - g_free (priv->category); - priv->category = NULL; - } - /* Save the ETable layout. */ config_filename = e_tasks_get_config_filename (tasks); e_calendar_table_save_state (E_CALENDAR_TABLE (priv->tasks_view), @@ -453,38 +399,6 @@ cal_opened_cb (CalClient *client, } -/* Callback from the calendar client when an object is updated */ -static void -obj_updated_cb (CalClient *client, - const char *uid, - gpointer data) -{ - ETasks *tasks; - ETasksPrivate *priv; - - tasks = E_TASKS (data); - priv = tasks->priv; - - /* FIXME: Do we need to do anything? */ -} - - -/* Callback from the calendar client when an object is removed */ -static void -obj_removed_cb (CalClient *client, - const char *uid, - gpointer data) -{ - ETasks *tasks; - ETasksPrivate *priv; - - tasks = E_TASKS (data); - priv = tasks->priv; - - /* FIXME: Do we need to do anything? */ -} - - static char* e_tasks_get_config_filename (ETasks *tasks) { @@ -533,6 +447,7 @@ e_tasks_new_task (ETasks *tasks) ETasksPrivate *priv; TaskEditor *tedit; CalComponent *comp; + const char *category; g_return_if_fail (E_IS_TASKS (tasks)); @@ -544,6 +459,9 @@ e_tasks_new_task (ETasks *tasks) comp = cal_component_new (); cal_component_set_new_vtype (comp, CAL_COMPONENT_TODO); + category = cal_search_bar_get_category (CAL_SEARCH_BAR (priv->search_bar)); + cal_component_set_categories (comp, category); + comp_editor_edit_comp (COMP_EDITOR (tedit), comp); gtk_object_unref (GTK_OBJECT (comp)); @@ -571,140 +489,108 @@ e_tasks_delete_selected (ETasks *tasks) e_calendar_table_delete_selected (cal_table); } +/* Callback used from the view collection when we need to display a new view */ static void -e_tasks_on_filter_selected (GtkMenuShell *menu_shell, - ETasks *tasks) +display_view_cb (GalViewCollection *collection, GalView *view, gpointer data) { - ETasksPrivate *priv; - ECalendarTable *cal_table; - CalendarModel *model; - GtkWidget *label; - char *category; - - g_return_if_fail (E_IS_TASKS (tasks)); - - priv = tasks->priv; - - label = GTK_BIN (priv->categories_option_menu)->child; - gtk_label_get (GTK_LABEL (label), &category); - - cal_table = E_CALENDAR_TABLE (priv->tasks_view); - model = cal_table->model; + ETasks *tasks; - if (priv->category) - g_free (priv->category); + tasks = E_TASKS (data); - if (!strcmp (category, _("All"))) { - calendar_model_set_default_category (model, NULL); - priv->category = NULL; - } else { - calendar_model_set_default_category (model, category); - priv->category = g_strdup (category); + if (GAL_IS_VIEW_ETABLE (view)) { + e_table_set_state_object (e_table_scrolled_get_table (E_TABLE_SCROLLED (E_CALENDAR_TABLE (tasks->priv->tasks_view)->etable)), + GAL_VIEW_ETABLE (view)->state); } - - update_query (tasks); } - -static void -e_tasks_on_categories_changed (CalendarModel *model, - ETasks *tasks) -{ - e_tasks_rebuild_categories_menu (tasks); -} - - -static void -e_tasks_rebuild_categories_menu (ETasks *tasks) +/** + * e_tasks_setup_view_menus: + * @tasks: A tasks widget. + * @uic: UI controller to use for the menus. + * + * Sets up the #GalView menus for a tasks control. This function should be + * called from the Bonobo control activation callback for this tasks control. + * Also, the menus should be discarded using e_tasks_discard_view_menus(). + **/ +void +e_tasks_setup_view_menus (ETasks *tasks, BonoboUIComponent *uic) { ETasksPrivate *priv; - CalendarModel *model; - GTree *categories; - GtkWidget *menuitem; + GalViewFactory *factory; + ETableSpecification *spec; + char *dir; + + g_return_if_fail (tasks != NULL); + g_return_if_fail (E_IS_TASKS (tasks)); + g_return_if_fail (uic != NULL); + g_return_if_fail (BONOBO_IS_UI_COMPONENT (uic)); priv = tasks->priv; - priv->categories_menu = gtk_menu_new (); + g_return_if_fail (priv->view_collection == NULL); - menuitem = gtk_menu_item_new_with_label (_("All")); - gtk_widget_show (menuitem); - gtk_menu_append (GTK_MENU (priv->categories_menu), menuitem); + g_assert (priv->view_collection == NULL); + g_assert (priv->view_menus == NULL); - model = E_CALENDAR_TABLE (priv->tasks_view)->model; - categories = calendar_model_get_categories (model); - g_return_if_fail (categories != NULL); + /* Create the view collection */ - g_tree_traverse (categories, e_tasks_add_menu_item, G_IN_ORDER, - priv->categories_menu); + priv->view_collection = gal_view_collection_new (); - gtk_option_menu_set_menu (GTK_OPTION_MENU (priv->categories_option_menu), priv->categories_menu); + dir = gnome_util_prepend_user_home ("/evolution/views/tasks/"); + gal_view_collection_set_storage_directories (priv->view_collection, + EVOLUTION_DATADIR "/evolution/views/tasks/", + dir); + g_free (dir); - gtk_signal_connect (GTK_OBJECT (priv->categories_menu), "deactivate", - GTK_SIGNAL_FUNC (e_tasks_on_filter_selected), - tasks); -} + /* Create the views */ + spec = e_table_specification_new (); + e_table_specification_load_from_file (spec, + EVOLUTION_ETSPECDIR "/e-calendar-table.etspec"); -static gint -e_tasks_add_menu_item (gpointer key, - gpointer value, - gpointer data) -{ - GtkWidget *menuitem; + factory = gal_view_factory_etable_new (spec); + gtk_object_unref (GTK_OBJECT (spec)); + gal_view_collection_add_factory (priv->view_collection, factory); + gtk_object_unref (GTK_OBJECT (factory)); - menuitem = gtk_menu_item_new_with_label ((char*) key); - gtk_widget_show (menuitem); - gtk_menu_append (GTK_MENU (data), menuitem); + /* Load the collection and create the menus */ - return FALSE; -} + gal_view_collection_load (priv->view_collection); -static void -display_view(GalViewCollection *collection, - GalView *view, - gpointer data) -{ - ETasks *tasks = data; - if (GAL_IS_VIEW_ETABLE(view)) { - e_table_set_state_object (e_table_scrolled_get_table (E_TABLE_SCROLLED (E_CALENDAR_TABLE (tasks->priv->tasks_view)->etable)), GAL_VIEW_ETABLE (view)->state); - } + priv->view_menus = gal_view_menus_new (priv->view_collection); + gal_view_menus_apply (priv->view_menus, uic, NULL); + gtk_signal_connect (GTK_OBJECT (priv->view_collection), "display_view", + GTK_SIGNAL_FUNC (display_view_cb), tasks); } +/** + * e_tasks_discard_view_menus: + * @tasks: A tasks widget. + * + * Discards the #GalView menus used by a tasks control. This function should be + * called from the Bonobo control deactivation callback for this tasks control. + * The menus should have been set up with e_tasks_setup_view_menus(). + **/ void -e_tasks_setup_menus (ETasks *tasks, - BonoboUIComponent *uic) +e_tasks_discard_view_menus (ETasks *tasks) { - GalViewCollection *collection; - GalViewMenus *views; - GalViewFactory *factory; - ETableSpecification *spec; - char *dir; - - collection = gal_view_collection_new(); + ETasksPrivate *priv; - dir = gnome_util_prepend_user_home ("/evolution/views/tasks/"); - gal_view_collection_set_storage_directories (collection, - EVOLUTION_DATADIR "/evolution/views/tasks/", - dir); - g_free (dir); + g_return_if_fail (tasks != NULL); + g_return_if_fail (E_IS_TASKS (tasks)); - spec = e_table_specification_new (); - e_table_specification_load_from_file (spec, - EVOLUTION_ETSPECDIR "/e-calendar-table.etspec"); + priv = tasks->priv; - factory = gal_view_factory_etable_new (spec); - gal_view_collection_add_factory (collection, factory); - gtk_object_sink (GTK_OBJECT (factory)); + g_return_if_fail (priv->view_collection != NULL); - gal_view_collection_load (collection); + g_assert (priv->view_collection != NULL); + g_assert (priv->view_menus != NULL); - views = gal_view_menus_new (collection); - gal_view_menus_apply (views, uic, NULL); /* This function probably needs to sink the views object. */ - gtk_signal_connect (GTK_OBJECT (collection), "display_view", - display_view, tasks); - /* gtk_object_sink(GTK_OBJECT(views)); */ + gtk_object_unref (GTK_OBJECT (priv->view_collection)); + priv->view_collection = NULL; - gtk_object_sink (GTK_OBJECT (collection)); + gtk_object_unref (GTK_OBJECT (priv->view_menus)); + priv->view_menus = NULL; } /** diff --git a/calendar/gui/e-tasks.h b/calendar/gui/e-tasks.h index c509e1d826..274425d780 100644 --- a/calendar/gui/e-tasks.h +++ b/calendar/gui/e-tasks.h @@ -69,8 +69,9 @@ CalClient *e_tasks_get_cal_client (ETasks *tasks); void e_tasks_new_task (ETasks *tasks); void e_tasks_delete_selected (ETasks *tasks); -void e_tasks_setup_menus (ETasks *tasks, - BonoboUIComponent *uic); + +void e_tasks_setup_view_menus (ETasks *tasks, BonoboUIComponent *uic); +void e_tasks_discard_view_menus (ETasks *tasks); ECalendarTable *e_tasks_get_calendar_table (ETasks *tasks); diff --git a/calendar/gui/gnome-cal.c b/calendar/gui/gnome-cal.c index 96f7fbc405..847a89ea06 100644 --- a/calendar/gui/gnome-cal.c +++ b/calendar/gui/gnome-cal.c @@ -312,6 +312,25 @@ search_bar_sexp_changed_cb (CalSearchBar *cal_search, const char *sexp, gpointer gnome_calendar_set_query (gcal, sexp); } +/* Callback used when the selected category in the search bar changes */ +static void +search_bar_category_changed_cb (CalSearchBar *cal_search, const char *category, gpointer data) +{ + GnomeCalendar *gcal; + GnomeCalendarPrivate *priv; + CalendarModel *model; + + gcal = GNOME_CALENDAR (data); + priv = gcal->priv; + + /* FIXME: Set the default category for the calendar views */ + + /* Set the default category for the task pad */ + + model = e_calendar_table_get_model (E_CALENDAR_TABLE (priv->todo)); + calendar_model_set_default_category (model, category); +} + static void setup_widgets (GnomeCalendar *gcal) { @@ -325,6 +344,8 @@ setup_widgets (GnomeCalendar *gcal) priv->search_bar = cal_search_bar_new (); gtk_signal_connect (GTK_OBJECT (priv->search_bar), "sexp_changed", GTK_SIGNAL_FUNC (search_bar_sexp_changed_cb), gcal); + gtk_signal_connect (GTK_OBJECT (priv->search_bar), "category_changed", + GTK_SIGNAL_FUNC (search_bar_category_changed_cb), gcal); gtk_widget_show (priv->search_bar); gtk_box_pack_start (GTK_BOX (gcal), priv->search_bar, FALSE, FALSE, 0); diff --git a/calendar/gui/tasks-control.c b/calendar/gui/tasks-control.c index d72798528b..2aa32f1f30 100644 --- a/calendar/gui/tasks-control.c +++ b/calendar/gui/tasks-control.c @@ -277,10 +277,12 @@ tasks_control_activate (BonoboControl *control, ETasks *tasks) "evolution-tasks.xml", "evolution-tasks"); - e_tasks_setup_menus(tasks, uic); - e_pixmaps_update (uic, pixmaps); + e_tasks_setup_view_menus (tasks, uic); + + /* Signals from the tasks widget; also sensitize the menu items as appropriate */ + gtk_signal_connect (GTK_OBJECT (tasks), "selection_changed", GTK_SIGNAL_FUNC (selection_changed_cb), control); @@ -304,6 +306,8 @@ tasks_control_deactivate (BonoboControl *control, ETasks *tasks) BonoboUIComponent *uic = bonobo_control_get_ui_component (control); g_assert (uic != NULL); + e_tasks_discard_view_menus (tasks); + /* Stop monitoring the "selection_changed" signal */ gtk_signal_disconnect_by_data (GTK_OBJECT (tasks), control); diff --git a/calendar/pcs/cal-backend-file.c b/calendar/pcs/cal-backend-file.c index 0c87760204..58879c8c3c 100644 --- a/calendar/pcs/cal-backend-file.c +++ b/calendar/pcs/cal-backend-file.c @@ -406,6 +406,53 @@ cal_destroy_cb (GtkObject *object, gpointer data) cal_backend_last_client_gone (CAL_BACKEND (cbfile)); } +/* Used from g_hash_table_foreach(), adds a category name to the sequence */ +static void +add_category_cb (gpointer key, gpointer value, gpointer data) +{ + Category *c; + GNOME_Evolution_Calendar_StringSeq *seq; + + c = value; + seq = data; + + seq->_buffer[seq->_length] = CORBA_string_dup (c->name); + seq->_length++; +} + +/* Notifies the clients with the current list of categories */ +static void +notify_categories_changed (CalBackendFile *cbfile) +{ + CalBackendFilePrivate *priv; + GNOME_Evolution_Calendar_StringSeq *seq; + GList *l; + + priv = cbfile->priv; + + /* Build the sequence of category names */ + + seq = GNOME_Evolution_Calendar_StringSeq__alloc (); + seq->_length = 0; + seq->_maximum = g_hash_table_size (priv->categories); + seq->_buffer = CORBA_sequence_CORBA_string_allocbuf (seq->_maximum); + CORBA_sequence_set_release (seq, TRUE); + + g_hash_table_foreach (priv->categories, add_category_cb, seq); + g_assert (seq->_length == seq->_maximum); + + /* Notify the clients */ + + for (l = priv->clients; l; l = l->next) { + Cal *cal; + + cal = CAL (l->data); + cal_notify_categories_changed (cal, seq); + } + + CORBA_free (seq); +} + /* Add_cal handler for the file backend */ static void cal_backend_file_add_cal (CalBackend *backend, Cal *cal) @@ -429,6 +476,12 @@ cal_backend_file_add_cal (CalBackend *backend, Cal *cal) backend); priv->clients = g_list_prepend (priv->clients, cal); + + /* Notify the client about changed categories so that it can populate + * its lists. + */ + + notify_categories_changed (cbfile); } /* Idle handler; we save the calendar since it is dirty */ @@ -693,53 +746,6 @@ scan_vcalendar (CalBackendFile *cbfile) } } -/* Used from g_hash_table_foreach(), adds a category name to the sequence */ -static void -add_category_cb (gpointer key, gpointer value, gpointer data) -{ - Category *c; - GNOME_Evolution_Calendar_StringSeq *seq; - - c = value; - seq = data; - - seq->_buffer[seq->_length] = CORBA_string_dup (c->name); - seq->_length++; -} - -/* Notifies the clients with the current list of categories */ -static void -notify_categories_changed (CalBackendFile *cbfile) -{ - CalBackendFilePrivate *priv; - GNOME_Evolution_Calendar_StringSeq *seq; - GList *l; - - priv = cbfile->priv; - - /* Build the sequence of category names */ - - seq = GNOME_Evolution_Calendar_StringSeq__alloc (); - seq->_length = 0; - seq->_maximum = g_hash_table_size (priv->categories); - seq->_buffer = CORBA_sequence_CORBA_string_allocbuf (seq->_maximum); - CORBA_sequence_set_release (seq, TRUE); - - g_hash_table_foreach (priv->categories, add_category_cb, seq); - g_assert (seq->_length == seq->_maximum); - - /* Notify the clients */ - - for (l = priv->clients; l; l = l->next) { - Cal *cal; - - cal = CAL (l->data); - cal_notify_categories_changed (cal, seq); - } - - CORBA_free (seq); -} - /* Callback used from icalparser_parse() */ static char * get_line_fn (char *s, size_t size, void *data) @@ -804,8 +810,6 @@ open_cal (CalBackendFile *cbfile, GnomeVFSURI *uri, FILE *file) gnome_vfs_uri_ref (uri); priv->uri = uri; - notify_categories_changed (cbfile); - return CAL_BACKEND_OPEN_SUCCESS; } @@ -827,8 +831,6 @@ create_cal (CalBackendFile *cbfile, GnomeVFSURI *uri) mark_dirty (cbfile); - notify_categories_changed (cbfile); - return CAL_BACKEND_OPEN_SUCCESS; } diff --git a/calendar/pcs/query.c b/calendar/pcs/query.c index f96b74b88e..94a998aa44 100644 --- a/calendar/pcs/query.c +++ b/calendar/pcs/query.c @@ -672,8 +672,11 @@ func_contains (ESExp *esexp, int argc, ESExpResult **argv, void *data) } /* (has-categories? STR+) + * (has-categories? #f) * * STR - At least one string specifying a category + * Or you can specify a single #f (boolean false) value for components + * that have no categories assigned to them ("unfiled"). * * Returns a boolean indicating whether the component has all the specified * categories. @@ -684,6 +687,7 @@ func_has_categories (ESExp *esexp, int argc, ESExpResult **argv, void *data) Query *query; QueryPrivate *priv; CalComponent *comp; + gboolean unfiled; int i; GSList *categories; gboolean matches; @@ -702,18 +706,37 @@ func_has_categories (ESExp *esexp, int argc, ESExpResult **argv, void *data) 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; - } + if (argc == 1 && argv[0]->type == ESEXP_RES_BOOL) + unfiled = TRUE; + else + unfiled = FALSE; + + if (!unfiled) + 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 or one and only one " + "argument to be a boolean false (#f)")); + return NULL; + } - /* Search categories */ + /* Search categories. First, if there are no categories we return + * whether unfiled components are supposed to match. + */ cal_component_get_categories_list (comp, &categories); if (!categories) { result = e_sexp_result_new (esexp, ESEXP_RES_BOOL); + result->value.bool = unfiled; + + return result; + } + + /* Otherwise, we *do* have categories but unfiled components were + * requested, so this component does not match. + */ + if (unfiled) { + result = e_sexp_result_new (esexp, ESEXP_RES_BOOL); result->value.bool = FALSE; return result; |