aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/misc/e-search-bar.c
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/misc/e-search-bar.c')
-rw-r--r--widgets/misc/e-search-bar.c265
1 files changed, 240 insertions, 25 deletions
diff --git a/widgets/misc/e-search-bar.c b/widgets/misc/e-search-bar.c
index 19fc41aa71..9461bbecb4 100644
--- a/widgets/misc/e-search-bar.c
+++ b/widgets/misc/e-search-bar.c
@@ -5,8 +5,10 @@
* Copyright (C) 2000, 2001 Ximian, Inc.
*
* Authors:
- * Chris Lahey <clahey@ximian.com>
+ * Chris Lahey <clahey@ximian.com>
* Ettore Perazzoli <ettore@ximian.com>
+ * Jon Trowbridge <trow@ximian.com>
+
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -30,6 +32,7 @@
#include <gtk/gtkeventbox.h>
#include <gtk/gtkmenuitem.h>
#include <gtk/gtkoptionmenu.h>
+#include <gtk/gtkmain.h>
#include <gal/widgets/e-unicode.h>
#include <gal/widgets/e-gui-utils.h>
@@ -56,6 +59,7 @@ static GtkHBoxClass *parent_class = NULL;
enum {
ARG_0,
ARG_OPTION_CHOICE,
+ ARG_SUBOPTION_CHOICE,
ARG_TEXT,
};
@@ -65,6 +69,11 @@ enum {
static void
emit_query_changed (ESearchBar *esb)
{
+ if (esb->pending_change) {
+ gtk_idle_remove (esb->pending_change);
+ esb->pending_change = 0;
+ }
+
gtk_signal_emit (GTK_OBJECT (esb),
esb_signals [QUERY_CHANGED]);
}
@@ -91,19 +100,110 @@ menubar_activated_cb (GtkWidget *widget, ESearchBar *esb)
}
static void
-option_activated_cb (GtkWidget *widget, ESearchBar *esb)
+entry_activated_cb (GtkWidget *widget,
+ ESearchBar *esb)
{
- int id;
+ emit_query_changed (esb);
+}
- id = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (widget), "EsbChoiceId"));
+static void
+subitem_activated_cb (GtkWidget *widget, ESearchBar *esb)
+{
+ gint id, subid;
+
+ id = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (widget), "EsbItemId"));
+ subid = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (widget), "EsbSubitemId"));
esb->option_choice = id;
+ esb->suboption_choice = subid;
emit_query_changed (esb);
}
static void
-entry_activated_cb (GtkWidget *widget, ESearchBar *esb)
+activate_by_subitems (ESearchBar *esb, gint item_id, ESearchBarSubitem *subitems)
{
+ if (subitems == NULL) {
+ /* This item uses the entry. */
+
+ if (esb->entry == NULL) {
+ esb->entry = gtk_entry_new();
+ gtk_object_ref (GTK_OBJECT (esb->entry));
+ gtk_signal_connect (GTK_OBJECT (esb->entry), "activate",
+ GTK_SIGNAL_FUNC (entry_activated_cb), esb);
+ gtk_widget_show(esb->entry);
+
+ esb->suboption_choice = 0;
+ }
+
+ if (esb->suboption_choice >= 0) {
+
+ if (esb->suboption != NULL) {
+ gtk_container_remove (GTK_CONTAINER (esb->entry_box), esb->suboption);
+ }
+
+ gtk_container_add (GTK_CONTAINER (esb->entry_box), esb->entry);
+ }
+
+ gtk_entry_set_text (GTK_ENTRY (esb->entry), "");
+
+ esb->suboption_choice = -1;
+
+ } else {
+
+ GtkWidget *menu;
+ GtkWidget *menu_item;
+ gint i;
+
+ if (esb->suboption == NULL) {
+ esb->suboption = gtk_option_menu_new ();
+ gtk_object_ref (GTK_OBJECT (esb->suboption));
+ gtk_widget_show (esb->suboption);
+ }
+
+ esb->suboption_menu = menu = gtk_menu_new ();
+ for (i = 0; subitems[i].id != -1; ++i) {
+ menu_item = gtk_menu_item_new_with_label (_(subitems[i].text));
+
+ gtk_object_set_data (GTK_OBJECT (menu_item), "EsbItemId", GINT_TO_POINTER (item_id));
+ gtk_object_set_data (GTK_OBJECT (menu_item), "EsbSubitemId", GINT_TO_POINTER (subitems[i].id));
+
+ gtk_signal_connect (GTK_OBJECT (menu_item),
+ "activate",
+ GTK_SIGNAL_FUNC (subitem_activated_cb),
+ esb);
+
+ gtk_widget_show (menu_item);
+ gtk_menu_append (GTK_MENU (menu), menu_item);
+ }
+
+ gtk_option_menu_remove_menu (GTK_OPTION_MENU (esb->suboption));
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (esb->suboption), menu);
+
+ if (esb->entry != NULL) {
+ gtk_container_remove (GTK_CONTAINER (esb->entry_box), esb->entry);
+ }
+
+ gtk_container_add (GTK_CONTAINER (esb->entry_box), esb->suboption);
+
+ esb->suboption_choice = 0;
+ }
+
+ if (esb->activate_button) {
+ gtk_widget_set_sensitive (esb->activate_button, subitems == NULL);
+ }
+}
+
+static void
+option_activated_cb (GtkWidget *widget,
+ ESearchBar *esb)
+{
+ int id;
+
+ id = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (widget), "EsbChoiceId"));
+
+ activate_by_subitems (esb, id, gtk_object_get_data (GTK_OBJECT (widget), "EsbChoiceSubitems"));
+
+ esb->option_choice = id;
emit_query_changed (esb);
}
@@ -133,6 +233,42 @@ put_in_spacer_widget (GtkWidget *widget)
return holder;
}
+static ESearchBarSubitem *
+copy_subitems (ESearchBarSubitem *subitems)
+{
+ gint i, N;
+ ESearchBarSubitem *copy;
+
+ if (subitems == NULL)
+ return NULL;
+
+ for (N=0; subitems[N].id != -1; ++N);
+ copy = g_new (ESearchBarSubitem, N+1);
+
+ for (i=0; i<N; ++i) {
+ copy[i].text = g_strdup (subitems[i].text);
+ copy[i].id = subitems[i].id;
+ }
+
+ copy[N].text = NULL;
+ copy[N].id = -1;
+
+ return copy;
+}
+
+static void
+free_subitems (ESearchBarSubitem *subitems)
+{
+ gint i;
+
+ if (subitems != NULL) {
+ for (i=0; subitems[i].id != -1; ++i) {
+ g_free (subitems[i].text);
+ }
+ g_free (subitems);
+ }
+}
+
static void
add_dropdown (ESearchBar *esb, ESearchBarItem *items)
{
@@ -211,6 +347,7 @@ set_option (ESearchBar *esb, ESearchBarItem *items)
esb->option_menu = menu = gtk_menu_new ();
for (i = 0; items[i].id != -1; i++) {
GtkWidget *item;
+ ESearchBarSubitem *subitems = NULL;
if (items[i].text) {
char *str;
@@ -228,10 +365,22 @@ set_option (ESearchBar *esb, ESearchBarItem *items)
gtk_object_set_data (GTK_OBJECT (item), "EsbChoiceId", GINT_TO_POINTER(items[i].id));
+ if (items[i].subitems != NULL) {
+ subitems = copy_subitems (items[i].subitems);
+ esb->subitem_garbage = g_list_prepend (esb->subitem_garbage, subitems);
+ }
+
+ gtk_object_set_data (GTK_OBJECT (item), "EsbChoiceSubitems", subitems);
+
+ if (i == 0) {
+ activate_by_subitems (esb, items[i].id, subitems);
+ }
+
gtk_signal_connect (GTK_OBJECT (item), "activate",
GTK_SIGNAL_FUNC (option_activated_cb),
esb);
}
+
gtk_widget_show_all (menu);
gtk_option_menu_set_menu (GTK_OPTION_MENU (esb->option), menu);
@@ -250,16 +399,6 @@ set_option (ESearchBar *esb, ESearchBarItem *items)
}
static void
-add_entry (ESearchBar *esb)
-{
- esb->entry = gtk_entry_new ();
- gtk_signal_connect (GTK_OBJECT (esb->entry), "activate",
- GTK_SIGNAL_FUNC (entry_activated_cb), esb);
- gtk_widget_show (esb->entry);
- gtk_box_pack_start (GTK_BOX (esb), esb->entry, TRUE, TRUE, 0);
-}
-
-static void
add_activate_button (ESearchBar *esb)
{
GtkWidget *label;
@@ -319,6 +458,10 @@ impl_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
GTK_VALUE_ENUM (*arg) = e_search_bar_get_option_choice (esb);
break;
+ case ARG_SUBOPTION_CHOICE:
+ GTK_VALUE_ENUM (*arg) = e_search_bar_get_suboption_choice (esb);
+ break;
+
case ARG_TEXT:
GTK_VALUE_STRING (*arg) = e_search_bar_get_text (esb);
break;
@@ -344,6 +487,15 @@ impl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
gtk_option_menu_set_history (GTK_OPTION_MENU (esb->option), row);
emit_query_changed (esb);
break;
+
+ case ARG_SUBOPTION_CHOICE:
+ esb->suboption_choice = GTK_VALUE_ENUM (*arg);
+ row = find_id (esb->suboption_menu, esb->suboption_choice, "EsbSubitemId", NULL);
+ if (row == -1)
+ row = 0;
+ gtk_option_menu_set_history (GTK_OPTION_MENU (esb->suboption), row);
+ emit_query_changed (esb);
+ break;
case ARG_TEXT:
e_utf8_gtk_editable_set_text (GTK_EDITABLE (esb->entry), GTK_VALUE_STRING (*arg));
@@ -362,15 +514,30 @@ impl_destroy (GtkObject *object)
g_return_if_fail (object != NULL);
g_return_if_fail (E_IS_SEARCH_BAR (object));
-
+
+ /* Should we really be unrefing all of these widgets? */
gtk_object_unref (GTK_OBJECT (esb->dropdown));
gtk_object_unref (GTK_OBJECT (esb->option));
- gtk_object_unref (GTK_OBJECT (esb->entry));
+
+ /* These two we do need to unref, because we explicitly hold
+ references to them. */
+ if (esb->entry)
+ gtk_object_unref (GTK_OBJECT (esb->entry));
+ if (esb->suboption)
+ gtk_object_unref (GTK_OBJECT (esb->suboption));
gtk_object_unref (GTK_OBJECT (esb->dropdown_holder));
gtk_object_unref (GTK_OBJECT (esb->option_menu));
gtk_object_unref (GTK_OBJECT (esb->dropdown_menu));
-
+
+ g_list_foreach (esb->subitem_garbage, (GFunc) free_subitems, NULL);
+ g_list_free (esb->subitem_garbage);
+
+ if (esb->pending_change) {
+ gtk_idle_remove (esb->pending_change);
+ esb->pending_change = 0;
+ }
+
if (GTK_OBJECT_CLASS (parent_class)->destroy)
GTK_OBJECT_CLASS (parent_class)->destroy (object);
}
@@ -394,6 +561,8 @@ class_init (ESearchBarClass *klass)
gtk_object_add_arg_type ("ESearchBar::option_choice", GTK_TYPE_ENUM,
GTK_ARG_READWRITE, ARG_OPTION_CHOICE);
+ gtk_object_add_arg_type ("ESearchBar::suboption_choice", GTK_TYPE_ENUM,
+ GTK_ARG_READWRITE, ARG_SUBOPTION_CHOICE);
gtk_object_add_arg_type ("ESearchBar::text", GTK_TYPE_STRING,
GTK_ARG_READWRITE, ARG_TEXT);
@@ -424,11 +593,21 @@ init (ESearchBar *esb)
esb->entry = NULL;
esb->option_choice = 0;
+ esb->suboption_choice = 0;
}
/* Object construction. */
+static gint
+idle_change_hack (gpointer ptr)
+{
+ ESearchBar *esb = E_SEARCH_BAR (ptr);
+ esb->pending_change = 0;
+ emit_query_changed (esb);
+ return FALSE;
+}
+
void
e_search_bar_construct (ESearchBar *search_bar,
ESearchBarItem *menu_items,
@@ -440,14 +619,30 @@ e_search_bar_construct (ESearchBar *search_bar,
g_return_if_fail (option_items != NULL);
gtk_box_set_spacing (GTK_BOX (search_bar), 1);
-
+
e_search_bar_set_menu (search_bar, menu_items);
-
+
+ search_bar->entry_box = gtk_hbox_new (0, FALSE);
+
e_search_bar_set_option (search_bar, option_items);
-
- add_entry (search_bar);
-
+
+ gtk_widget_show (search_bar->entry_box);
+ gtk_box_pack_start (GTK_BOX(search_bar), search_bar->entry_box, TRUE, TRUE, 0);
+
add_activate_button (search_bar);
+
+ /*
+ * If the default choice for the option menu has subitems, then we need to
+ * activate the search immediately. However, the developer won't have
+ * connected to the changed signal until after the object is constructed,
+ * so we can't emit here. Thus we launch a one-shot idle function that will
+ * emit the changed signal, so that the proper callback will get invoked.
+ */
+ if (search_bar->suboption_choice >= 0) {
+ gtk_widget_set_sensitive (search_bar->activate_button, FALSE);
+
+ search_bar->pending_change = gtk_idle_add (idle_change_hack, search_bar);
+ }
}
void
@@ -548,13 +743,33 @@ e_search_bar_get_option_choice (ESearchBar *search_bar)
}
/**
+ * e_search_bar_get_suboption_choice:
+ * @search_bar: A search bar.
+ *
+ * Queries the currently selected item in the suboptions menu of a search bar.
+ *
+ * Return value: Identifier of the selected item in the suboptions menu.
+ * If the search bar currently contains an entry rather than a a suboption menu,
+ * a value less than zero is returned.
+ **/
+int
+e_search_bar_get_suboption_choice (ESearchBar *search_bar)
+{
+ g_return_val_if_fail (search_bar != NULL, -1);
+ g_return_val_if_fail (E_IS_SEARCH_BAR (search_bar), -1);
+
+ return search_bar->suboption_choice;
+}
+
+/**
* e_search_bar_get_text:
* @search_bar: A search bar.
*
* Queries the text of the entry line in a search bar.
*
* Return value: The text string that is in the entry line of the search bar.
- * This must be freed using g_free().
+ * This must be freed using g_free(). If a suboption menu is active instead
+ * of an entry, NULL is returned.
**/
char *
e_search_bar_get_text (ESearchBar *search_bar)
@@ -562,5 +777,5 @@ e_search_bar_get_text (ESearchBar *search_bar)
g_return_val_if_fail (search_bar != NULL, NULL);
g_return_val_if_fail (E_IS_SEARCH_BAR (search_bar), NULL);
- return e_utf8_gtk_editable_get_text (GTK_EDITABLE (search_bar->entry));
+ return search_bar->suboption_choice < 0 ? e_utf8_gtk_editable_get_text (GTK_EDITABLE (search_bar->entry)) : NULL;
}