/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */ /* Balsa E-Mail Client * Copyright (C) 1997-2002 Stuart Parmenter and others, * See the file AUTHORS for a list. * * 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, 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. */ #include "config.h" #include #include #include "balsa-app.h" #include "sendmsg-window.h" #include "ab-window.h" enum { LIST_COLUMN_NAME, LIST_COLUMN_ADDRESS_STRING, LIST_COLUMN_ADDRESS, LIST_COLUMN_WHICH, N_COLUMNS }; /* Object system functions ... */ static void balsa_ab_window_init(BalsaAbWindow *ab); static void balsa_ab_window_class_init(BalsaAbWindowClass *klass); /* Loading ... */ static void balsa_ab_window_load_cb(LibBalsaAddressBook *libbalsa_ab, LibBalsaAddress *address, BalsaAbWindow *ab); static void balsa_ab_window_load(BalsaAbWindow *ab); static void balsa_ab_window_reload(GtkWidget *w, BalsaAbWindow *av); /* Callbacks ... */ static void balsa_ab_window_dist_mode_toggled(GtkWidget * w, BalsaAbWindow *ab); static void balsa_ab_window_menu_changed(GtkWidget * widget, BalsaAbWindow *ab); static void balsa_ab_window_run_gnomecard(GtkWidget * widget, gpointer data); static void balsa_ab_window_response_cb(BalsaAbWindow *ab, gint resp); static void balsa_ab_window_find(GtkWidget * group_entry, BalsaAbWindow *ab); /* address and recipient list management ... */ static void balsa_ab_window_swap_list_entry(GtkTreeView * src, GtkTreeView * dst); static void balsa_ab_window_activate_address(GtkTreeView * view, GtkTreePath * path, GtkTreeViewColumn * column, gpointer data); static void balsa_ab_window_select_recipient(GtkTreeView * view, GtkTreePath * path, GtkTreeViewColumn * column, gpointer data); static void balsa_ab_window_move_to_recipient_list(GtkWidget *widget, BalsaAbWindow *ab); static void balsa_ab_window_remove_from_recipient_list(GtkWidget *widget, BalsaAbWindow *ab); /* Utility ... */ static gint balsa_ab_window_compare_entries(GtkTreeModel * model, GtkTreeIter * iter1, GtkTreeIter * iter2, gpointer data); struct _BalsaAbWindowClass { GtkDialogClass parent_class; }; GtkType balsa_ab_window_get_type(void) { static GtkType ab_type = 0; if ( !ab_type ) { static const GTypeInfo ab_info = { sizeof(BalsaAbWindowClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) balsa_ab_window_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof(BalsaAbWindow), 0, /* n_preallocs */ (GInstanceInitFunc) balsa_ab_window_init }; ab_type = g_type_register_static(GTK_TYPE_DIALOG, "BalsaAbWindow", &ab_info, 0); } return ab_type; } GtkWidget * balsa_ab_window_new(gboolean composing, GtkWindow* parent) { GtkWidget *ret; ret = g_object_new(BALSA_TYPE_AB_WINDOW, NULL); g_return_val_if_fail(ret, NULL); BALSA_AB_WINDOW(ret)->composing = composing; if ( composing ) { gtk_dialog_add_buttons(GTK_DIALOG(ret), GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); gtk_widget_show(GTK_WIDGET(BALSA_AB_WINDOW(ret)->send_to_box)); gtk_widget_show(GTK_WIDGET(BALSA_AB_WINDOW(ret)->arrow_box)); } else { gtk_dialog_add_buttons(GTK_DIALOG(ret), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); gtk_widget_hide(GTK_WIDGET(BALSA_AB_WINDOW(ret)->send_to_box)); gtk_widget_hide(GTK_WIDGET(BALSA_AB_WINDOW(ret)->arrow_box)); } if(parent) gtk_window_set_transient_for(GTK_WINDOW(ret), parent); return ret; } static GtkWidget * balsa_ab_window_list(BalsaAbWindow * ab, GCallback row_activated_cb) { GtkListStore *store; GtkWidget *tree; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *selection; store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, /* LIST_COLUMN_NAME */ G_TYPE_STRING, /* LIST_COLUMN_ADDRESS_STRING */ G_TYPE_OBJECT, /* LIST_COLUMN_ADDRESS */ G_TYPE_INT); /* LIST_COLUMN_WHICH */ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), 0, balsa_ab_window_compare_entries, GINT_TO_POINTER(0), NULL); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), 0, GTK_SORT_ASCENDING); tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); g_object_unref(store); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", LIST_COLUMN_NAME, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("E-Mail Address"), renderer, "text", LIST_COLUMN_ADDRESS_STRING, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)); gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); gtk_widget_show(tree); g_signal_connect(G_OBJECT(tree), "row-activated", row_activated_cb, ab); return tree; } static void balsa_ab_window_init(BalsaAbWindow *ab) { GtkWidget *find_label, *vbox, *vbox2, *w, *hbox, *box2, *scrolled_window, *ab_option, *ab_menu, *menu_item, *frame, *label; GList *ab_list; LibBalsaAddressBook *address_book; guint default_offset = 0; ab->current_address_book = NULL; gtk_window_set_title(GTK_WINDOW(ab), _("Address Book")); g_signal_connect(G_OBJECT(ab), "response", G_CALLBACK(balsa_ab_window_response_cb), NULL); vbox = GTK_DIALOG(ab)->vbox; gtk_window_set_wmclass(GTK_WINDOW(ab), "addressbook", "Balsa"); /* The main address list */ ab->address_list = balsa_ab_window_list(ab, G_CALLBACK (balsa_ab_window_activate_address)); /* The clist for selected addresses in compose mode */ ab->recipient_list = balsa_ab_window_list(ab, G_CALLBACK (balsa_ab_window_select_recipient)); /* The address book selection menu */ ab_menu = gtk_menu_new(); ab->current_address_book = balsa_app.default_address_book; ab_list = balsa_app.address_book_list; while (ab_list) { address_book = LIBBALSA_ADDRESS_BOOK(ab_list->data); if (ab->current_address_book == NULL) ab->current_address_book = address_book; menu_item = gtk_menu_item_new_with_label(address_book->name); gtk_widget_show(menu_item); gtk_menu_shell_append(GTK_MENU_SHELL(ab_menu), menu_item); g_object_set_data(G_OBJECT(menu_item), "address-book", address_book); g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(balsa_ab_window_menu_changed), ab); if (address_book == balsa_app.default_address_book) gtk_menu_set_active(GTK_MENU(ab_menu), default_offset); default_offset++; ab_list = g_list_next(ab_list); } gtk_widget_show(ab_menu); ab_option = gtk_option_menu_new(); gtk_option_menu_set_menu(GTK_OPTION_MENU(ab_option), ab_menu); if(g_list_length(balsa_app.address_book_list)>1) gtk_widget_show(ab_option); gtk_box_pack_start(GTK_BOX(vbox), ab_option, FALSE, FALSE, 0); /* Entry widget for finding an address */ find_label = gtk_label_new_with_mnemonic(_("_Search for Name:")); gtk_widget_show(find_label); ab->filter_entry = gtk_entry_new(); gtk_widget_show(ab->filter_entry); gtk_label_set_mnemonic_widget(GTK_LABEL(find_label), ab->filter_entry); g_signal_connect(G_OBJECT(ab->filter_entry), "changed", G_CALLBACK(balsa_ab_window_find), ab); /* Horizontal layout */ hbox = gtk_hbox_new(FALSE, 1); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); /* Column for address list */ vbox2 = gtk_vbox_new(FALSE, 1); gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 1); gtk_widget_show(vbox2); /* Pack the find stuff into a box */ box2 = gtk_hbox_new(FALSE, 1); gtk_box_pack_start(GTK_BOX(vbox2), box2, FALSE, FALSE, 1); gtk_box_pack_start(GTK_BOX(box2), find_label, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box2), ab->filter_entry, TRUE, TRUE, 0); gtk_widget_show(GTK_WIDGET(box2)); /* A scrolled window for the address clist */ scrolled_window = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(vbox2), scrolled_window, TRUE, TRUE, 0); gtk_widget_show(scrolled_window); gtk_container_add(GTK_CONTAINER(scrolled_window), ab->address_list); gtk_widget_set_size_request(scrolled_window, 300, 250); /* Column for arrows in compose mode */ ab->arrow_box = gtk_vbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(hbox), ab->arrow_box, FALSE, FALSE, 1); gtk_widget_show(ab->arrow_box); w = balsa_stock_button_with_label(GTK_STOCK_GO_FORWARD, ""); gtk_box_pack_start(GTK_BOX(ab->arrow_box), w, TRUE, FALSE, 0); gtk_widget_show(w); g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(balsa_ab_window_move_to_recipient_list), ab); w = balsa_stock_button_with_label(GTK_STOCK_GO_BACK, ""); gtk_box_pack_start(GTK_BOX(ab->arrow_box), w, TRUE, FALSE, 0); gtk_widget_show(w); g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(balsa_ab_window_remove_from_recipient_list), ab); /* Column for selected addresses in compose mode */ ab->send_to_box = gtk_vbox_new(FALSE, 1); gtk_box_pack_start(GTK_BOX(hbox), ab->send_to_box, TRUE, TRUE, 1); label = gtk_label_new(_("Send-To")); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(ab->send_to_box), label, FALSE, FALSE, 1); scrolled_window = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(scrolled_window); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(ab->send_to_box), scrolled_window, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(scrolled_window), ab->recipient_list); gtk_widget_set_size_request(scrolled_window, 300, 250); /* Buttons ... */ hbox = gtk_hbutton_box_new(); gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_SPREAD); gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0); gtk_widget_show(GTK_WIDGET(hbox)); w = balsa_stock_button_with_label(GTK_STOCK_OPEN, _("Run GnomeCard")); g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(balsa_ab_window_run_gnomecard), NULL); gtk_container_add(GTK_CONTAINER(hbox), w); gtk_widget_show(GTK_WIDGET(w)); w = balsa_stock_button_with_label(GTK_STOCK_ADD, _("_Re-Import")); g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(balsa_ab_window_reload), ab); gtk_container_add(GTK_CONTAINER(hbox), w); gtk_widget_show(w); balsa_ab_window_load(ab); /* mode switching stuff */ frame = gtk_frame_new(_("Treat multiple addresses as:")); gtk_widget_show(frame); ab->single_address_mode_radio = gtk_radio_button_new_with_label (NULL, _("alternative addresses for the same person")); gtk_widget_show(ab->single_address_mode_radio); ab->dist_address_mode_radio = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(ab->single_address_mode_radio), _("a distribution list")); gtk_widget_show(ab->dist_address_mode_radio); ab->toggle_handler_id = g_signal_connect(G_OBJECT(ab->single_address_mode_radio), "toggled", G_CALLBACK(balsa_ab_window_dist_mode_toggled), ab); if(ab->current_address_book) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ab->dist_address_mode_radio), ab->current_address_book->dist_list_mode); /* Pack them into a box */ box2 = gtk_vbox_new(TRUE, 1); gtk_container_add(GTK_CONTAINER(frame), box2); gtk_box_pack_start(GTK_BOX(box2), ab->single_address_mode_radio, FALSE, FALSE, 1); gtk_box_pack_start(GTK_BOX(box2), ab->dist_address_mode_radio, FALSE, FALSE, 1); gtk_widget_show(box2); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 1); gtk_widget_grab_focus(ab->filter_entry); } static void balsa_ab_window_class_init(BalsaAbWindowClass *klass) { } /* Runs gnome card */ static void balsa_ab_window_run_gnomecard(GtkWidget * widget, gpointer data) { char *argv[] = { "gnomecard" }; gnome_execute_async(NULL, 1, argv); } /* Returns a string of all the selected recipients. */ gchar * balsa_ab_window_get_recipients(BalsaAbWindow * ab) { GtkTreeModel *model; GtkTreeIter iter; gboolean valid; GString *str = g_string_new(NULL); gchar *text; g_return_val_if_fail(ab->composing, NULL); model = gtk_tree_view_get_model(GTK_TREE_VIEW(ab->recipient_list)); for (valid = gtk_tree_model_get_iter_first(model, &iter); valid; valid = gtk_tree_model_iter_next(model, &iter)) { LibBalsaAddress *address = NULL; gint which_multiple = 0; gtk_tree_model_get(model, &iter, LIST_COLUMN_ADDRESS, &address, LIST_COLUMN_WHICH, &which_multiple, -1); text = libbalsa_address_to_gchar(address, which_multiple); g_object_unref(G_OBJECT(address)); if (text) { if (str->len > 0) g_string_append(str, ", "); g_string_append(str, text); g_free(text); } } text = str->str; g_string_free(str, FALSE); return text; } /* Moves an entry between two CLists. */ /* FIXME: Need to only move it back if it belongs in current address book?? */ /* * balsa_ab_window_swap_list_entry is the method. * * balsa_ab_window_swap_make_gslist, balsa_ab_window_swap_real, * and balsa_ab_window_swap_do_swap are callbacks/helpers. */ static void balsa_ab_window_swap_make_gslist(GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer data) { GtkTreeRowReference *reference = gtk_tree_row_reference_new(model, path); GSList **selected = data; *selected = g_slist_prepend(*selected, reference); } static void balsa_ab_window_swap_real(GtkTreeModel * model, GtkTreePath * path, GtkTreeModel * dst_model) { GtkTreeIter iter; GtkTreeIter dst_iter; gchar *name; gchar *address_string; LibBalsaAddress *address; gint which_multiple; gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, LIST_COLUMN_NAME, &name, LIST_COLUMN_ADDRESS_STRING, &address_string, LIST_COLUMN_ADDRESS, &address, LIST_COLUMN_WHICH, &which_multiple, -1); gtk_list_store_prepend(GTK_LIST_STORE(dst_model), &dst_iter); gtk_list_store_set(GTK_LIST_STORE(dst_model), &dst_iter, LIST_COLUMN_NAME, name, LIST_COLUMN_ADDRESS_STRING, address_string, LIST_COLUMN_ADDRESS, address, LIST_COLUMN_WHICH, which_multiple, -1); /* gtk_list_store_remove unrefs address, but gtk_list_store_set has * already reffed it, so it won't be finalized, even if there * were no other outstanding refs. */ gtk_list_store_remove(GTK_LIST_STORE(model), &iter); g_object_unref(G_OBJECT(address)); g_free(name); g_free(address_string); } #define BALSA_TREE_VIEW "balsa-tree-view" static void balsa_ab_window_swap_do_swap(gpointer data, gpointer user_data) { GtkTreeRowReference *reference = data; GtkTreePath *path = gtk_tree_row_reference_get_path(reference); if (path) { GtkTreeView *tree_view = g_object_get_data(G_OBJECT(user_data), BALSA_TREE_VIEW); GtkTreeModel *model = gtk_tree_view_get_model(tree_view); GtkTreeModel *dst_model = gtk_tree_view_get_model(user_data); balsa_ab_window_swap_real(model, path, dst_model); gtk_tree_path_free(path); } gtk_tree_row_reference_free(reference); } static void balsa_ab_window_swap_list_entry(GtkTreeView * src, GtkTreeView * dst) { GtkTreeSelection *selection; GSList *selected; g_return_if_fail(GTK_IS_TREE_VIEW(src)); g_return_if_fail(GTK_IS_TREE_VIEW(dst)); selection = gtk_tree_view_get_selection(src); selected = NULL; gtk_tree_selection_selected_foreach(selection, balsa_ab_window_swap_make_gslist, &selected); g_object_set_data(G_OBJECT(dst), BALSA_TREE_VIEW, src); g_slist_foreach(selected, balsa_ab_window_swap_do_swap, dst); } /* Handle a click on the main address list If composing then move the address to the other clist, otherwise open a compose window. */ static void balsa_ab_window_activate_address(GtkTreeView * view, GtkTreePath * path, GtkTreeViewColumn * column, gpointer data) { BalsaAbWindow *ab = data; g_return_if_fail(BALSA_IS_AB_WINDOW(ab)); if (ab->composing) { balsa_ab_window_swap_list_entry(GTK_TREE_VIEW(ab->address_list), GTK_TREE_VIEW(ab-> recipient_list)); } else { BalsaSendmsg *snd; GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(ab->address_list)); GtkTreeIter iter; LibBalsaAddress *address; gint which_multiple; gchar *addr; snd = sendmsg_window_new(GTK_WIDGET(balsa_app.main_window), NULL, SEND_NORMAL); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, LIST_COLUMN_ADDRESS, &address, LIST_COLUMN_WHICH, &which_multiple, -1); addr = libbalsa_address_to_gchar(address, which_multiple); g_object_unref(G_OBJECT(address)); gtk_entry_set_text(GTK_ENTRY(snd->to[1]), addr); g_free(addr); gtk_widget_grab_focus(snd->subject[1]); } } /* Handle a click on the recipient list. Only sane if composing. Move to address list. */ static void balsa_ab_window_select_recipient(GtkTreeView * view, GtkTreePath * path, GtkTreeViewColumn * column, gpointer data) { BalsaAbWindow *ab = data; g_return_if_fail( BALSA_IS_AB_WINDOW(ab) ); g_return_if_fail( ab->composing ); balsa_ab_window_swap_list_entry(GTK_TREE_VIEW(ab->recipient_list), GTK_TREE_VIEW(ab->address_list)); } /* Handle a click on forward button */ static void balsa_ab_window_move_to_recipient_list(GtkWidget *widget, BalsaAbWindow *ab) { g_return_if_fail( BALSA_IS_AB_WINDOW(ab) ); g_return_if_fail( ab->composing ); balsa_ab_window_swap_list_entry(GTK_TREE_VIEW(ab->address_list), GTK_TREE_VIEW(ab->recipient_list)); } /* Handle a click on the back button */ static void balsa_ab_window_remove_from_recipient_list(GtkWidget *widget, BalsaAbWindow *ab) { g_return_if_fail( BALSA_IS_AB_WINDOW(ab) ); g_return_if_fail( ab->composing ); balsa_ab_window_swap_list_entry(GTK_TREE_VIEW(ab->recipient_list), GTK_TREE_VIEW(ab->address_list)); } /* Handle a click on the reload button. */ static void balsa_ab_window_reload(GtkWidget *w, BalsaAbWindow *ab) { balsa_ab_window_load(ab); } /* * Loads the addressbooks into a clist. */ static void balsa_ab_window_load(BalsaAbWindow *ab) { GtkTreeModel *model; LibBalsaABErr err; const gchar *filter; g_return_if_fail(BALSA_IS_AB_WINDOW(ab)); model = gtk_tree_view_get_model(GTK_TREE_VIEW(ab->address_list)); gtk_list_store_clear(GTK_LIST_STORE(model)); if (ab->current_address_book == NULL) return; filter = gtk_entry_get_text(GTK_ENTRY(ab->filter_entry)); if( (err=libbalsa_address_book_load(ab->current_address_book, filter, (LibBalsaAddressBookLoadFunc) balsa_ab_window_load_cb, ab)) != LBABERR_OK) { const gchar *desc = libbalsa_address_book_strerror(ab->current_address_book, err); balsa_information_parented(GTK_WINDOW(ab), LIBBALSA_INFORMATION_ERROR, _("Error opening address book '%s':\n%s"), ab->current_address_book->name, desc); } } /* The address load callback. Adds a single address to the address list. If the current address book is in dist list mode then create a single entry, or else create an entry for each address in the book. */ static void balsa_ab_window_load_cb(LibBalsaAddressBook *libbalsa_ab, LibBalsaAddress *address, BalsaAbWindow *ab) { GtkTreeModel *model; GtkTreeIter iter; GList *address_list; gint count; g_return_if_fail ( BALSA_IS_AB_WINDOW(ab)); g_return_if_fail ( LIBBALSA_IS_ADDRESS_BOOK(libbalsa_ab) ); if ( address == NULL ) return; model = gtk_tree_view_get_model(GTK_TREE_VIEW(ab->address_list)); if ( libbalsa_address_is_dist_list(libbalsa_ab, address) ) { gchar *address_string = libbalsa_address_to_gchar(address, -1); gtk_list_store_prepend(GTK_LIST_STORE(model), &iter); /* GtkListStore refs address, and unrefs it when cleared */ gtk_list_store_set(GTK_LIST_STORE(model), &iter, LIST_COLUMN_NAME, address->full_name, LIST_COLUMN_ADDRESS_STRING, address_string, LIST_COLUMN_ADDRESS, address, LIST_COLUMN_WHICH, -1, -1); g_free(address_string); } else { address_list = address->address_list; count = 0; while ( address_list ) { gtk_list_store_prepend(GTK_LIST_STORE(model), &iter); /* GtkListStore refs address once for each address in * the list, and unrefs it the same number of times when * cleared */ gtk_list_store_set(GTK_LIST_STORE(model), &iter, LIST_COLUMN_NAME, address->full_name, LIST_COLUMN_ADDRESS_STRING, address_list->data, LIST_COLUMN_ADDRESS, address, LIST_COLUMN_WHICH, count, -1); address_list = g_list_next(address_list); count++; } } } /* Search for an address in the address list. Attached to the changed signal of the find entry. */ static void balsa_ab_window_find(GtkWidget * group_entry, BalsaAbWindow * ab) { const gchar *entry_text; GtkTreeSelection *selection; GtkTreeView *tree_view; GtkTreeModel *model; GtkTreeIter iter; gboolean valid; gboolean first_path; g_return_if_fail(BALSA_IS_AB_WINDOW(ab)); entry_text = gtk_entry_get_text(GTK_ENTRY(group_entry)); if (*entry_text == '\0') return; tree_view = GTK_TREE_VIEW(ab->address_list); selection = gtk_tree_view_get_selection(tree_view); gtk_tree_selection_unselect_all(selection); model = gtk_tree_view_get_model(tree_view); first_path = TRUE; for (valid = gtk_tree_model_get_iter_first(model, &iter); valid; valid = gtk_tree_model_iter_next(model, &iter)) { gchar *new; gtk_tree_model_get(model, &iter, LIST_COLUMN_NAME, &new, -1); if (g_ascii_strncasecmp(new, entry_text, strlen(entry_text)) == 0) { GtkTreePath *path = gtk_tree_model_get_path(model, &iter); gtk_tree_selection_select_path(selection, path); if (first_path) { gtk_tree_view_set_cursor(tree_view, path, NULL, FALSE); #if 0 gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, 0.5, 0); #endif first_path = FALSE; } gtk_tree_path_free(path); } g_free(new); } } /* Handle a change in the dist mode */ static void balsa_ab_window_dist_mode_toggled(GtkWidget * w, BalsaAbWindow *ab) { gboolean active; g_return_if_fail(BALSA_IS_AB_WINDOW(ab)); if(ab->current_address_book == NULL) return; active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(ab->single_address_mode_radio)); ab->current_address_book->dist_list_mode = !active; balsa_ab_window_load(ab); } /* Handle a change in the current address book. */ static void balsa_ab_window_menu_changed(GtkWidget * widget, BalsaAbWindow *ab) { LibBalsaAddressBook *addr; addr = LIBBALSA_ADDRESS_BOOK(g_object_get_data (G_OBJECT(widget), "address-book")); g_assert(addr != NULL); ab->current_address_book = addr; g_signal_handler_block(G_OBJECT(ab->single_address_mode_radio), ab->toggle_handler_id); if ( ab->current_address_book->dist_list_mode ) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ab->dist_address_mode_radio), TRUE); else gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ab->single_address_mode_radio), TRUE); g_signal_handler_unblock(G_OBJECT(ab->single_address_mode_radio), ab->toggle_handler_id); balsa_ab_window_load(ab); } /* Compare two rows in a clist. */ static gint balsa_ab_window_compare_entries(GtkTreeModel * model, GtkTreeIter * iter1, GtkTreeIter * iter2, gpointer data) { gchar *c1 = NULL; gchar *c2 = NULL; gtk_tree_model_get(model, iter1, LIST_COLUMN_NAME, &c1, -1); gtk_tree_model_get(model, iter2, LIST_COLUMN_NAME, &c2, -1); if (c1 == NULL || c2 == NULL) return 0; return g_ascii_strcasecmp(c1, c2); } /* balsa_ab_window_response_cb: Normally, we should not destroy the window in the response callback. This time, we can make an exception - nobody is waiting for the result anyway. */ static void balsa_ab_window_response_cb(BalsaAbWindow *ab, gint response) { switch(response) { case GTK_RESPONSE_CLOSE: if ( !ab->composing ) gtk_widget_destroy(GTK_WIDGET(ab)); break; default: /* nothing */ break; } }