aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--calendar/ChangeLog66
-rw-r--r--calendar/gui/cal-search-bar.c280
-rw-r--r--calendar/gui/cal-search-bar.h10
-rw-r--r--calendar/gui/calendar-model.c87
-rw-r--r--calendar/gui/calendar-model.h6
-rw-r--r--calendar/gui/e-tasks.c334
-rw-r--r--calendar/gui/e-tasks.h5
-rw-r--r--calendar/gui/gnome-cal.c21
-rw-r--r--calendar/gui/tasks-control.c8
-rw-r--r--calendar/pcs/cal-backend-file.c104
-rw-r--r--calendar/pcs/query.c37
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;