/* Evolution calendar - Search bar widget for calendar views * * Copyright (C) 2001 Ximian, Inc. * * Author: Federico Mena-Quintero * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "cal-search-bar.h" /* Menu items for the ESearchBar */ static ESearchBarItem search_menu_items[] = { E_FILTERBAR_RESET, { NULL, -1 } }; /* IDs and option items for the ESearchBar */ enum { SEARCH_ANY_FIELD_CONTAINS, SEARCH_SUMMARY_CONTAINS, SEARCH_DESCRIPTION_CONTAINS, SEARCH_COMMENT_CONTAINS }; static ESearchBarItem search_option_items[] = { { N_("Any field contains"), SEARCH_ANY_FIELD_CONTAINS }, { N_("Summary contains"), SEARCH_SUMMARY_CONTAINS }, { N_("Description contains"), SEARCH_DESCRIPTION_CONTAINS }, { N_("Comment contains"), SEARCH_COMMENT_CONTAINS }, { 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); /* Signal IDs */ enum { SEXP_CHANGED, CATEGORY_CHANGED, LAST_SIGNAL }; static guint cal_search_bar_signals[LAST_SIGNAL] = { 0 }; /** * cal_search_bar_get_type: * * Registers the #CalSearchBar class if necessary and returns the type ID * associated to it. * * Return value: The type ID of the #CalSearchBar class. **/ GtkType cal_search_bar_get_type (void) { static GtkType cal_search_bar_type = 0; if (!cal_search_bar_type) { static const GtkTypeInfo cal_search_bar_info = { "CalSearchBar", sizeof (CalSearchBar), sizeof (CalSearchBarClass), (GtkClassInitFunc) cal_search_bar_class_init, (GtkObjectInitFunc) cal_search_bar_init, NULL, /* reserved_1 */ NULL, /* reserved_2 */ (GtkClassInitFunc) NULL }; cal_search_bar_type = gtk_type_unique (E_SEARCH_BAR_TYPE, &cal_search_bar_info); } return cal_search_bar_type; } /* Class initialization function for the calendar search bar */ static void cal_search_bar_class_init (CalSearchBarClass *class) { ESearchBarClass *e_search_bar_class; GtkObjectClass *object_class; e_search_bar_class = (ESearchBarClass *) class; object_class = (GtkObjectClass *) class; cal_search_bar_signals[SEXP_CHANGED] = gtk_signal_new ("sexp_changed", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (CalSearchBarClass, sexp_changed), gtk_marshal_NONE__STRING, 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 */ static void notify_sexp_changed (CalSearchBar *cal_search, const char *sexp) { gtk_signal_emit (GTK_OBJECT (cal_search), cal_search_bar_signals[SEXP_CHANGED], 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); notify_sexp_changed (cal_search, sexp); if (category_sexp) g_free (category_sexp); g_free (sexp); } /* Creates a new query from the values in the widgets and notifies upstream */ static void regen_query (CalSearchBar *cal_search) { CalSearchBarPrivate *priv; int item; char *text; priv = cal_search->priv; /* 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); break; case SEARCH_SUMMARY_CONTAINS: notify_query_contains (cal_search, "summary", text); break; case SEARCH_DESCRIPTION_CONTAINS: notify_query_contains (cal_search, "description", text); break; case SEARCH_COMMENT_CONTAINS: notify_query_contains (cal_search, "comment", text); break; default: g_assert_not_reached (); } 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) { CalSearchBar *cal_search; cal_search = CAL_SEARCH_BAR (search); switch (item) { case E_FILTERBAR_RESET_ID: notify_sexp_changed (cal_search, "#t"); /* match all */ /* FIXME: should we change the rest of the search bar so that * the user sees that he selected "show all" instead of some * type/text search combination? */ break; default: g_assert_not_reached (); } } /* 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. * * Constructs a calendar search bar by binding its menu and option items. * * Return value: The same value as @cal_search. **/ CalSearchBar * cal_search_bar_construct (CalSearchBar *cal_search) { g_return_val_if_fail (cal_search != NULL, NULL); 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; } /** * cal_search_bar_new: * * Creates a new calendar search bar. * * Return value: A newly-created calendar search bar. You should connect to the * "sexp_changed" signal to monitor changes in the generated sexps. **/ GtkWidget * cal_search_bar_new (void) { CalSearchBar *cal_search; 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; }