From 1f52a5d0fa5edfa1c138971100df9143ef0c0f8b Mon Sep 17 00:00:00 2001 From: Jon Trowbridge Date: Mon, 14 May 2001 05:06:16 +0000 Subject: Use secondary searches here, so that we control the interference between 2001-05-14 Jon Trowbridge * folder-browser.c (folder_browser_config_search): Use secondary searches here, so that we control the interference between the two bits of searching UI. 2001-05-13 Jon Trowbridge * mail-search.c (mail_search_construct): Destroy the MailSearch dialog if the underlying MailDisplay is destroyed. I don't like the way that label in the dialog with the message subject in it looks, so I've #ifdef-ed it out for now. Center the Matches label --- it makes the dialog look more balanced, I think. (dialog_clicked_cb): Changed to reflect adjusted ESearchingTokenizer API, using primary searches. (toggled_case_cb): Use the primary search API. * e-searching-tokenizer.c: Make searching routines utf8-friendly. Rationalize how the match begin/end markup is handled; allow for begin/end markup that varies by search. Add concept of primary and secondary matching, to disentangle possible interactions between search-bar searches and search-dialog searches. svn path=/trunk/; revision=9789 --- mail/ChangeLog | 23 +++++ mail/e-searching-tokenizer.c | 207 +++++++++++++++++++++++++++++++------------ mail/e-searching-tokenizer.h | 10 ++- mail/folder-browser.c | 4 +- mail/mail-search.c | 51 ++++++++--- 5 files changed, 222 insertions(+), 73 deletions(-) (limited to 'mail') diff --git a/mail/ChangeLog b/mail/ChangeLog index 37712c7e7a..508ef65d7e 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,3 +1,26 @@ +2001-05-14 Jon Trowbridge + + * folder-browser.c (folder_browser_config_search): Use secondary + searches here, so that we control the interference between the two + bits of searching UI. + +2001-05-13 Jon Trowbridge + + * mail-search.c (mail_search_construct): Destroy the MailSearch + dialog if the underlying MailDisplay is destroyed. I don't like + the way that label in the dialog with the message subject in it + looks, so I've #ifdef-ed it out for now. Center the Matches + label --- it makes the dialog look more balanced, I think. + (dialog_clicked_cb): Changed to reflect adjusted + ESearchingTokenizer API, using primary searches. + (toggled_case_cb): Use the primary search API. + + * e-searching-tokenizer.c: Make searching routines utf8-friendly. + Rationalize how the match begin/end markup is handled; allow for + begin/end markup that varies by search. Add concept of primary and + secondary matching, to disentangle possible interactions between + search-bar searches and search-dialog searches. + 2001-05-13 Jeffrey Stedfast * mail-local.c (mail_local_storage_shutdown): Get rid of this - we diff --git a/mail/e-searching-tokenizer.c b/mail/e-searching-tokenizer.c index e5b3eaf195..47266fc836 100644 --- a/mail/e-searching-tokenizer.c +++ b/mail/e-searching-tokenizer.c @@ -67,7 +67,13 @@ typedef struct _SearchInfo SearchInfo; struct _SearchInfo { gchar *search; gchar *current; + gboolean case_sensitive; + gboolean allow_space_tags_to_match_whitespace; + + gint match_size_incr; + gchar *match_color; + gboolean match_bold; }; struct _ESearchingTokenizerPrivate { @@ -75,12 +81,11 @@ struct _ESearchingTokenizerPrivate { SearchInfo *search; GList *pending; GList *trash; - gchar **match_begin; - gchar **match_end; - gchar *pending_search_string; - gboolean pending_search_string_clear; - gboolean pending_case_sensitivity; + gchar *str_primary; + gchar *str_secondary; + gboolean case_sensitive_primary; + gboolean case_sensitive_secondary; }; /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/ @@ -93,6 +98,12 @@ search_info_new (void) si = g_new0 (SearchInfo, 1); si->case_sensitive = FALSE; + si->match_size_incr = 1; + si->match_color = g_strdup ("red"); + si->match_bold = FALSE; + + si->allow_space_tags_to_match_whitespace = TRUE; + return si; } @@ -101,6 +112,7 @@ search_info_free (SearchInfo *si) { if (si) { g_free (si->search); + g_free (si->match_color); g_free (si); } } @@ -131,12 +143,49 @@ search_info_set_string (SearchInfo *si, const gchar *str) } static void -search_info_reset (SearchInfo *si) +search_info_set_case_sensitivity (SearchInfo *si, gboolean flag) +{ + g_return_if_fail (si); + + si->case_sensitive = flag; +} + +static void +search_info_set_match_size_increase (SearchInfo *si, gint incr) +{ + g_return_if_fail (si); + g_return_if_fail (incr >= 0); + + si->match_size_incr = incr; +} + +static void +search_info_set_match_color (SearchInfo *si, const gchar *color) { g_return_if_fail (si); + + g_free (si->match_color); + si->match_color = g_strdup (color); +} + +static void +search_info_set_match_bold (SearchInfo *si, gboolean flag) +{ + g_return_if_fail (si); + + si->match_bold = flag; +} + +static void +search_info_reset (SearchInfo *si) +{ + if (si == NULL) + return; si->current = NULL; } +/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ + static const gchar * find_whole (SearchInfo *si, const gchar *haystack, const gchar *needle) { @@ -313,7 +362,8 @@ search_info_compare (SearchInfo *si, const gchar *token, gint *start_pos, gint * } /* "Space tags" only match whitespace in our ongoing match. */ - if (g_unichar_isspace (g_utf8_get_char (si->current))) { + if (si->allow_space_tags_to_match_whitespace + && g_unichar_isspace (g_utf8_get_char (si->current))) { for (i=0; space_tags[i]; ++i) { if (tag_match (token, space_tags[i])) { si->current = g_utf8_next_char (si->current); @@ -326,8 +376,6 @@ search_info_compare (SearchInfo *si, const gchar *token, gint *start_pos, gint * return MATCH_FAILED; } - - s = find_partial (si, token, si->current); if (s) { if (start_pos) @@ -360,8 +408,8 @@ e_searching_tokenizer_cleanup (ESearchingTokenizer *st) } if (st->priv->pending) { - //g_list_foreach (st->priv->pending, (GFunc) g_free, NULL); - //g_list_free (st->priv->pending); + g_list_foreach (st->priv->pending, (GFunc) g_free, NULL); + g_list_free (st->priv->pending); st->priv->pending = NULL; } } @@ -375,8 +423,8 @@ e_searching_tokenizer_destroy (GtkObject *obj) search_info_free (st->priv->search); - g_strfreev (st->priv->match_begin); - g_strfreev (st->priv->match_end); + g_free (st->priv->str_primary); + g_free (st->priv->str_secondary); g_free (st->priv); st->priv = NULL; @@ -418,13 +466,6 @@ static void e_searching_tokenizer_init (ESearchingTokenizer *st) { st->priv = g_new0 (struct _ESearchingTokenizerPrivate, 1); - - /* For now, hard coded match markup. */ - st->priv->match_begin = g_new0 (gchar *, 2); - st->priv->match_begin[0] = g_strdup_printf ("%c", TAG_ESCAPE); - - st->priv->match_end = g_new0 (gchar *, 2); - st->priv->match_end[0] = g_strdup_printf ("%c", TAG_ESCAPE); } GtkType @@ -493,19 +534,37 @@ add_pending (ESearchingTokenizer *st, gchar *tok) } static void -add_pending_match_begin (ESearchingTokenizer *st) +add_pending_match_begin (ESearchingTokenizer *st, SearchInfo *si) { - gint i; - for (i=0; st->priv->match_begin[i]; ++i) - add_pending (st, g_strdup (st->priv->match_begin[i])); + gchar *size_str = NULL; + gchar *color_str= NULL; + + if (si->match_size_incr > 0) + size_str = g_strdup_printf (" size=+%d", si->match_size_incr); + if (si->match_color) + color_str = g_strdup_printf (" color=%s", si->match_color); + + if (size_str || color_str) + add_pending (st, g_strdup_printf ("%c", + TAG_ESCAPE, + size_str ? size_str : "", + color_str ? color_str : "")); + + g_free (size_str); + g_free (color_str); + + if (si->match_bold) + add_pending (st, g_strdup_printf ("%c", TAG_ESCAPE)); } static void -add_pending_match_end (ESearchingTokenizer *st) +add_pending_match_end (ESearchingTokenizer *st, SearchInfo *si) { - gint i; - for (i=0; st->priv->match_end[i]; ++i) - add_pending (st, g_strdup (st->priv->match_end[i])); + if (si->match_bold) + add_pending (st, g_strdup_printf ("%c", TAG_ESCAPE)); + + if (si->match_size_incr > 0 || si->match_color) + add_pending (st, g_strdup_printf ("%c", TAG_ESCAPE)); } static void @@ -528,7 +587,7 @@ get_next_token (ESearchingTokenizer *st) * the appropriate tokens. */ static GList * -queue_matched (ESearchingTokenizer *st, GList *q) +queue_matched (ESearchingTokenizer *st, SearchInfo *si, GList *q) { GList *qh = q; gboolean post_start = FALSE; @@ -536,18 +595,18 @@ queue_matched (ESearchingTokenizer *st, GList *q) while (q != NULL) { GList *q_next = g_list_next (q); if (!strcmp ((gchar *) q->data, START_MAGIC)) { - add_pending_match_begin (st); + add_pending_match_begin (st, si); post_start = TRUE; } else if (!strcmp ((gchar *) q->data, END_MAGIC)) { - add_pending_match_end (st); + add_pending_match_end (st, si); q_next = NULL; } else { gboolean is_tag = *((gchar *)q->data) == TAG_ESCAPE; if (is_tag && post_start) - add_pending_match_end (st); + add_pending_match_end (st, si); add_pending (st, g_strdup ((gchar *) q->data)); if (is_tag && post_start) - add_pending_match_begin (st); + add_pending_match_begin (st, si); } qh = g_list_remove_link (qh, q); g_list_free_1 (q); @@ -651,9 +710,9 @@ get_pending_tokens (ESearchingTokenizer *st) if (start_pos != 0) add_pending (st, g_strndup (token, start_pos)); - add_pending_match_begin (st); + add_pending_match_begin (st, st->priv->search); add_pending (st, g_strndup (token+start_pos, end_pos-start_pos)); - add_pending_match_end (st); + add_pending_match_end (st, st->priv->search); if (*(token+end_pos)) { queue->data = g_strdup (token+end_pos); add_to_trash (st, (gchar *) queue->data); @@ -705,7 +764,7 @@ get_pending_tokens (ESearchingTokenizer *st) add_to_trash (st, s3); queue = g_list_remove_link (queue, q); - queue = queue_matched (st, queue); + queue = queue_matched (st, st->priv->search, queue); matched (st); @@ -744,29 +803,42 @@ static void e_searching_tokenizer_begin (HTMLTokenizer *t, gchar *content_type) { ESearchingTokenizer *st = E_SEARCHING_TOKENIZER (t); + SearchInfo *si; + + if (st->priv->search == NULL && (st->priv->str_primary || st->priv->str_secondary)) { + st->priv->search = search_info_new (); + } + si = st->priv->search; + - if (st->priv->pending_search_string) { + if (st->priv->str_primary) { - if (st->priv->search == NULL) - st->priv->search = search_info_new (); + search_info_set_string (si, st->priv->str_primary); + search_info_set_case_sensitivity (si, st->priv->case_sensitive_primary); - search_info_set_string (st->priv->search, st->priv->pending_search_string); - g_free (st->priv->pending_search_string); - st->priv->pending_search_string = NULL; + search_info_set_match_color (si, "red"); + search_info_set_match_size_increase (si, 1); + search_info_set_match_bold (si, TRUE); - } else if (st->priv->pending_search_string_clear) { + } else if (st->priv->str_secondary) { + search_info_set_string (si, st->priv->str_secondary); + search_info_set_case_sensitivity (si, st->priv->case_sensitive_secondary); + + search_info_set_match_color (si, "purple"); + search_info_set_match_size_increase (si, 1); + search_info_set_match_bold (si, TRUE); + + } else { + search_info_free (st->priv->search); st->priv->search = NULL; - st->priv->pending_search_string_clear = FALSE; - } - if (st->priv->search) { - st->priv->search->case_sensitive = st->priv->pending_case_sensitivity; } e_searching_tokenizer_cleanup (st); search_info_reset (st->priv->search); + st->priv->match_count = 0; HTML_TOKENIZER_CLASS (parent_class)->begin (t, content_type); @@ -855,30 +927,51 @@ only_whitespace (const gchar *p) } void -e_searching_tokenizer_set_search_string (ESearchingTokenizer *st, const gchar *search_str) +e_searching_tokenizer_set_primary_search_string (ESearchingTokenizer *st, const gchar *search_str) { g_return_if_fail (st && E_IS_SEARCHING_TOKENIZER (st)); - if (search_str == NULL - || !g_utf8_validate (search_str, -1, NULL) - || only_whitespace (search_str)) { + g_free (st->priv->str_primary); + st->priv->str_primary = NULL; - st->priv->pending_search_string_clear = TRUE; + if (search_str != NULL + && g_utf8_validate (search_str, -1, NULL) + && !only_whitespace (search_str)) { - } else { + st->priv->str_primary = g_strdup (search_str); + } +} + +void +e_searching_tokenizer_set_primary_case_sensitivity (ESearchingTokenizer *st, gboolean is_case_sensitive) +{ + g_return_if_fail (st && E_IS_SEARCHING_TOKENIZER (st)); + + st->priv->case_sensitive_primary = is_case_sensitive; +} + +void +e_searching_tokenizer_set_secondary_search_string (ESearchingTokenizer *st, const gchar *search_str) +{ + g_return_if_fail (st && E_IS_SEARCHING_TOKENIZER (st)); - st->priv->pending_search_string_clear = FALSE; - st->priv->pending_search_string = g_strdup (search_str); + g_free (st->priv->str_secondary); + st->priv->str_secondary = NULL; + if (search_str != NULL + && g_utf8_validate (search_str, -1, NULL) + && !only_whitespace (search_str)) { + + st->priv->str_secondary = g_strdup (search_str); } } void -e_searching_tokenizer_set_case_sensitivity (ESearchingTokenizer *st, gboolean is_case_sensitive) +e_searching_tokenizer_set_secondary_case_sensitivity (ESearchingTokenizer *st, gboolean is_case_sensitive) { g_return_if_fail (st && E_IS_SEARCHING_TOKENIZER (st)); - st->priv->pending_case_sensitivity = is_case_sensitive; + st->priv->case_sensitive_secondary = is_case_sensitive; } gint diff --git a/mail/e-searching-tokenizer.h b/mail/e-searching-tokenizer.h index bf7e473ded..7852548c04 100644 --- a/mail/e-searching-tokenizer.h +++ b/mail/e-searching-tokenizer.h @@ -59,8 +59,14 @@ GtkType e_searching_tokenizer_get_type (void); HTMLTokenizer *e_searching_tokenizer_new (void); /* For now, just a simple API */ -void e_searching_tokenizer_set_search_string (ESearchingTokenizer *, const gchar *); -void e_searching_tokenizer_set_case_sensitivity (ESearchingTokenizer *, gboolean is_case_sensitive); + +void e_searching_tokenizer_set_primary_search_string (ESearchingTokenizer *, const gchar *); +void e_searching_tokenizer_set_primary_case_sensitivity (ESearchingTokenizer *, gboolean is_case_sensitive); + +void e_searching_tokenizer_set_secondary_search_string (ESearchingTokenizer *, const gchar *); +void e_searching_tokenizer_set_secondary_case_sensitivity (ESearchingTokenizer *, gboolean is_case_sensitive); + + gint e_searching_tokenizer_match_count (ESearchingTokenizer *); diff --git a/mail/folder-browser.c b/mail/folder-browser.c index 74bce1c6c8..2d904dab22 100644 --- a/mail/folder-browser.c +++ b/mail/folder-browser.c @@ -302,7 +302,7 @@ static void folder_browser_config_search(EFilterBar *efb, FilterRule *rule, int st = E_SEARCHING_TOKENIZER (fb->mail_display->html->engine->ht); - e_searching_tokenizer_set_search_string (st, NULL); + e_searching_tokenizer_set_secondary_search_string (st, NULL); /* we scan the parts of a rule, and set all the types we know about to the query string */ partl = rule->parts; @@ -317,7 +317,7 @@ static void folder_browser_config_search(EFilterBar *efb, FilterRule *rule, int FilterInput *input = (FilterInput *)filter_part_find_element(part, "word"); if (input) filter_input_set_value(input, query); - e_searching_tokenizer_set_search_string (st, query); + e_searching_tokenizer_set_secondary_search_string (st, query); } else if(!strcmp(part->name, "sender")) { FilterInput *input = (FilterInput *)filter_part_find_element(part, "sender"); if (input) diff --git a/mail/mail-search.c b/mail/mail-search.c index 2fae3e06e8..7c19ed393a 100644 --- a/mail/mail-search.c +++ b/mail/mail-search.c @@ -113,8 +113,8 @@ toggled_case_cb (GtkToggleButton *b, MailSearch *ms) { ms->case_sensitive = gtk_toggle_button_get_active (b); - e_searching_tokenizer_set_case_sensitivity (mail_search_tokenizer (ms), - ms->case_sensitive); + e_searching_tokenizer_set_primary_case_sensitivity (mail_search_tokenizer (ms), + ms->case_sensitive); mail_search_redisplay_message (ms); } @@ -154,7 +154,9 @@ dialog_clicked_cb (GtkWidget *w, gint button_number, MailSearch *ms) g_free (ms->last_search); ms->last_search = NULL; - e_searching_tokenizer_set_search_string (st, search_text); + e_searching_tokenizer_set_primary_search_string (st, search_text); + e_searching_tokenizer_set_primary_case_sensitivity (st, ms->case_sensitive); + mail_search_redisplay_message (ms); if (gtk_html_engine_search (ms->mail->html, search_text, @@ -170,7 +172,7 @@ dialog_clicked_cb (GtkWidget *w, gint button_number, MailSearch *ms) } else if (button_number == 1) { /* "Close" */ - e_searching_tokenizer_set_search_string (st, NULL); + e_searching_tokenizer_set_primary_search_string (st, NULL); mail_search_redisplay_message (ms); gtk_widget_destroy (w); @@ -183,13 +185,16 @@ begin_cb (ESearchingTokenizer *st, gchar *foo, MailSearch *ms) { gtk_label_set_text (GTK_LABEL (ms->count_label), "0"); +#ifdef SUBJECT_IN_DIALOG if (ms->mail->current_message->subject && *ms->mail->current_message->subject) { gchar *msg_subject = e_utf8_to_gtk_string (GTK_WIDGET (ms->msg_label), ms->mail->current_message->subject); gtk_label_set_text (GTK_LABEL (ms->msg_label), msg_subject); /* Use the converted string */ g_free (msg_subject); - } - else + } else { gtk_label_set_text (GTK_LABEL (ms->msg_label), _("(Untitled Message)")); + } +#endif + } static void @@ -207,20 +212,23 @@ mail_search_construct (MailSearch *ms, MailDisplay *mail) GNOME_STOCK_BUTTON_CLOSE, NULL }; gchar *title = NULL; - gchar *utf8_subject; - gchar *msg_subject; - GtkWidget *msg_hbox; GtkWidget *find_hbox; GtkWidget *matches_hbox; GtkWidget *toggles_hbox; GtkWidget *entry; - GtkWidget *msg_label; GtkWidget *count_label; GtkWidget *case_check; GtkWidget *fwd_check; +#ifdef SUBJECT_IN_DIALOG + gchar *utf8_subject; + gchar *msg_subject; + GtkWidget *msg_hbox; + GtkWidget *msg_label; +#endif + g_return_if_fail (ms != NULL && IS_MAIL_SEARCH (ms)); g_return_if_fail (mail != NULL && IS_MAIL_DISPLAY (mail)); @@ -231,10 +239,12 @@ mail_search_construct (MailSearch *ms, MailDisplay *mail) title = g_strdup (_("Find in Message")); +#ifdef SUBJECT_IN_DIALOG if (mail->current_message->subject && *mail->current_message->subject) utf8_subject = g_strdup (mail->current_message->subject); else utf8_subject = g_strdup (_("(Untitled Message)")); +#endif gnome_dialog_constructv (GNOME_DIALOG (ms), title, buttons); g_free (title); @@ -253,7 +263,7 @@ mail_search_construct (MailSearch *ms, MailDisplay *mail) /* Construct the dialog contents. */ - msg_hbox = gtk_hbox_new (FALSE, 0); + /* msg_hbox = gtk_hbox_new (FALSE, 0); */ find_hbox = gtk_hbox_new (FALSE, 0); matches_hbox = gtk_hbox_new (FALSE, 0); toggles_hbox = gtk_hbox_new (FALSE, 0); @@ -261,33 +271,44 @@ mail_search_construct (MailSearch *ms, MailDisplay *mail) entry = gtk_entry_new (); count_label = gtk_label_new ("0"); +#ifdef SUBJECT_IN_DIALOG msg_label = gtk_label_new (""); msg_subject = e_utf8_to_gtk_string (GTK_WIDGET (msg_label), utf8_subject); gtk_label_set_text (GTK_LABEL (msg_label), msg_subject); /* Use converted string instead */ g_free (utf8_subject); g_free (msg_subject); +#endif case_check = gtk_check_button_new_with_label (_("Case Sensitive")); fwd_check = gtk_check_button_new_with_label (_("Search Forward")); ms->entry = entry; ms->count_label = count_label; + +#ifdef SUBJECT_IN_DIALOG ms->msg_label = msg_label; +#endif gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fwd_check), ms->search_forward); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (case_check), ms->case_sensitive); - gtk_box_pack_start (GTK_BOX (msg_hbox), gtk_label_new (_("Message subject:")), FALSE, FALSE, 3); +#ifdef SUBJECT_IN_DIALOG + /* gtk_box_pack_start (GTK_BOX (msg_hbox), gtk_label_new (_("Message subject:")), FALSE, FALSE, 3); */ gtk_box_pack_start (GTK_BOX (msg_hbox), msg_label, TRUE, TRUE, 0); +#endif gtk_box_pack_start (GTK_BOX (find_hbox), gtk_label_new (_("Find:")), FALSE, FALSE, 3); gtk_box_pack_start (GTK_BOX (find_hbox), entry, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (matches_hbox), gtk_hbox_new (FALSE, 0), TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (matches_hbox), gtk_label_new (_("Matches:")), FALSE, FALSE, 3); gtk_box_pack_start (GTK_BOX (matches_hbox), count_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (matches_hbox), gtk_hbox_new (FALSE, 0), TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (toggles_hbox), case_check, FALSE, FALSE, 4); gtk_box_pack_start (GTK_BOX (toggles_hbox), fwd_check, FALSE, FALSE, 4); +#ifdef SUBJECT_IN_DIALOG gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (ms)->vbox), msg_hbox, TRUE, TRUE, 0); +#endif gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (ms)->vbox), find_hbox, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (ms)->vbox), matches_hbox, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (ms)->vbox), toggles_hbox, TRUE, TRUE, 0); @@ -296,7 +317,9 @@ mail_search_construct (MailSearch *ms, MailDisplay *mail) gnome_dialog_set_default (GNOME_DIALOG (ms), 0); gnome_dialog_editable_enters (GNOME_DIALOG (ms), GTK_EDITABLE(entry)); /* Make run the search */ +#ifdef SUBJECT_IN_DIALOG gtk_widget_show_all (msg_hbox); +#endif gtk_widget_show_all (find_hbox); gtk_widget_show_all (matches_hbox); gtk_widget_show_all (toggles_hbox); @@ -315,6 +338,10 @@ mail_search_construct (MailSearch *ms, MailDisplay *mail) "clicked", GTK_SIGNAL_FUNC (dialog_clicked_cb), ms); + gtk_signal_connect_object (GTK_OBJECT (ms->mail), + "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT (ms)); } GtkWidget * -- cgit