# HG changeset patch # User Mark Doliner # Date 1104215072 0 # Node ID 911530134bf8ca304234cff2de4b85edc8a9d88f # Parent d90ece33e72f512c28714770cc8656c6168be5f3 [gaim-migrate @ 11697] sf patch #1086253, from Alex Converse closes sf rfe #991372, from Adam Petaccia "New Buddy Search Results Dialog," used for oscar Also some memleak fixes from me. My bad. committer: Tailor Script diff -r d90ece33e72f -r 911530134bf8 COPYRIGHT --- a/COPYRIGHT Mon Dec 27 21:24:20 2004 +0000 +++ b/COPYRIGHT Tue Dec 28 06:24:32 2004 +0000 @@ -35,6 +35,7 @@ Joe Clarke Todd Cohen Felipe Contreras +Alex Converse Adam Cowell Jeramey Crawford Balwinder Singh Dheeman diff -r d90ece33e72f -r 911530134bf8 ChangeLog --- a/ChangeLog Mon Dec 27 21:24:20 2004 +0000 +++ b/ChangeLog Tue Dec 28 06:24:32 2004 +0000 @@ -10,6 +10,7 @@ * Autoreconnect plugin can hide the reconnect dialog (François Gagné) * Screenname colors in chats now chosen intelligently from GNOME color palette + * New "find buddy" results dialog (Alex Converse) * Yahoo! has the following new "/" commands: /join, /buzz * Smiley selection dialog rewritten to look nicer (Nathan Fredrickson) * If Gaim is exited with the buddy list hidden in the docklet, it will diff -r d90ece33e72f -r 911530134bf8 src/gtkblist.c --- a/src/gtkblist.c Mon Dec 27 21:24:20 2004 +0000 +++ b/src/gtkblist.c Tue Dec 28 06:24:32 2004 +0000 @@ -3186,7 +3186,7 @@ { gboolean result; gchar *enteredstring; - const gchar *withmarkup; + gchar *withmarkup; gchar *nomarkup; const gchar *normalized; @@ -3198,6 +3198,7 @@ result = (g_strstr_len(normalized, strlen(normalized), enteredstring) == NULL); + g_free(withmarkup); g_free(enteredstring); g_free(nomarkup); diff -r d90ece33e72f -r 911530134bf8 src/gtknotify.c --- a/src/gtknotify.c Mon Dec 27 21:24:20 2004 +0000 +++ b/src/gtknotify.c Tue Dec 28 06:24:32 2004 +0000 @@ -33,6 +33,7 @@ #include "gtkstock.h" #include "util.h" +#include "gtkblist.h" #include "gtkimhtml.h" #include "gtknotify.h" #include "gtkutils.h" @@ -46,6 +47,22 @@ } GaimNotifyMailData; +typedef struct +{ + GaimAccount *account; + GtkListStore *model; + GtkWidget *treeview; + GtkWidget *window; + +} GaimNotifySearchResultsData; + +enum +{ + COLUMN_ICON, + COLUMN_SCREENNAME, + NUM_COLUMNS +}; + static void *gaim_gtk_notify_emails(size_t count, gboolean detailed, const char **subjects, const char **froms, const char **tos, @@ -73,6 +90,33 @@ gaim_notify_close(GAIM_NOTIFY_FORMATTED, win); } +static void +searchresults_close_cb(GaimNotifySearchResultsData *data, GdkEvent *event, void *user_data) +{ + gaim_notify_close(GAIM_NOTIFY_SEARCHRESULTS, data); +} + +static void +add_buddy_helper_cb(GtkWidget *widget, GaimNotifySearchResultsData *data) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gchar *buddy; + + g_return_if_fail(data != NULL); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->treeview)); + + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + { + gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, + COLUMN_SCREENNAME, &buddy, -1); + gaim_blist_request_add_buddy(data->account, buddy, NULL, NULL); + g_free(buddy); + } +} + static void * gaim_gtk_notify_message(GaimNotifyMsgType type, const char *title, const char *primary, const char *secondary, @@ -378,6 +422,140 @@ } static void * +gaim_gtk_notify_searchresults(GaimConnection *gc, const char *title, + const char *primary, const char *secondary, + const char **results, GCallback cb, + void *user_data) +{ + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *button_area; + GtkWidget *label; + GtkWidget *close_button; + GtkWidget *add_button; + GtkWidget *sw; + GtkWidget *treeview; + GdkPixbuf *icon, *scaled; + GaimNotifySearchResultsData *data; + GtkListStore *model; + GtkCellRenderer *renderer; + GtkTreeIter iter; + int i; + char *label_text; + + data = g_malloc(sizeof(GaimNotifySearchResultsData)); + + /* Create the window */ + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), (title ? title :_("Search Results"))); + gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_container_set_border_width(GTK_CONTAINER(window), 12); + + g_signal_connect_swapped(G_OBJECT(window), "delete_event", + G_CALLBACK(searchresults_close_cb), data); + + /* Setup the main vbox */ + vbox = gtk_vbox_new(FALSE, 12); + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_widget_show(vbox); + + /* Setup the descriptive label */ + label_text = g_strdup_printf( + "%s%s%s", + (primary ? primary : ""), + (primary && secondary ? "\n" : ""), + (secondary ? secondary : "")); + label = gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(label), label_text); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + g_free(label_text); + + /* Setup the list model */ + model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING); + + /* Setup the scrolled window containing the treeview */ + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); + gtk_widget_show(sw); + + /* Setup the treeview */ + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_widget_set_size_request(treeview, 250, 150); + gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), + GTK_SELECTION_SINGLE); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); + gtk_container_add(GTK_CONTAINER(sw), treeview); + gtk_widget_show(treeview); + + /* icon column */ + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), + -1, "Icon", renderer, + "pixbuf", COLUMN_ICON, + NULL); + + /* screenname column */ + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), + -1, "Screenname", renderer, + "text", COLUMN_SCREENNAME, + NULL); + + /* Setup the button area */ + button_area = gtk_hbutton_box_new(); + gtk_box_pack_start(GTK_BOX(vbox), button_area, FALSE, FALSE, 0); + gtk_button_box_set_layout(GTK_BUTTON_BOX(button_area), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(button_area), 12); + gtk_widget_show(button_area); + + /* Add the Add button */ + add_button = gtk_button_new_from_stock(GTK_STOCK_ADD); + gtk_box_pack_start(GTK_BOX(button_area), add_button, FALSE, FALSE, 0); + gtk_widget_show(add_button); + + /* Add the Close button */ + close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + gtk_box_pack_start(GTK_BOX(button_area), close_button, FALSE, FALSE, 0); + gtk_widget_show(close_button); + + /* Add the buddies to the tree view */ + icon = create_prpl_icon(gc->account); + scaled = gdk_pixbuf_scale_simple(icon, 16, 16, GDK_INTERP_BILINEAR); + + for (i = 0; results[i] != NULL; i++) + { + gtk_list_store_append(model, &iter); + gtk_list_store_set(model, &iter, + COLUMN_ICON, scaled, + COLUMN_SCREENNAME, results[i], + -1); + } + + data->account = gc->account; + data->model = model; + data->treeview = treeview; + data->window = window; + + /* Connect Signals */ + g_signal_connect(G_OBJECT(add_button), "clicked", + G_CALLBACK(add_buddy_helper_cb), data); + g_signal_connect_swapped(G_OBJECT(close_button), "clicked", + G_CALLBACK(searchresults_close_cb), data); + + /* Show the window */ + gtk_widget_show(window); + return data; +} + +static void * gaim_gtk_notify_userinfo(GaimConnection *gc, const char *who, const char *title, const char *primary, const char *secondary, const char *text, @@ -399,6 +577,14 @@ g_free(data->url); g_free(data); } + else if (type == GAIM_NOTIFY_SEARCHRESULTS) + { + GaimNotifySearchResultsData *data = (GaimNotifySearchResultsData *)ui_handle; + + gtk_widget_destroy(data->window); + + g_free(data); + } else gtk_widget_destroy(GTK_WIDGET(ui_handle)); } @@ -612,6 +798,7 @@ gaim_gtk_notify_email, gaim_gtk_notify_emails, gaim_gtk_notify_formatted, + gaim_gtk_notify_searchresults, gaim_gtk_notify_userinfo, gaim_gtk_notify_uri, gaim_gtk_close_notify diff -r d90ece33e72f -r 911530134bf8 src/gtksavedstatuses.c --- a/src/gtksavedstatuses.c Mon Dec 27 21:24:20 2004 +0000 +++ b/src/gtksavedstatuses.c Tue Dec 28 06:24:32 2004 +0000 @@ -89,20 +89,28 @@ status_window_find_savedstatus(GtkTreeIter *iter, const char *title) { GtkTreeModel *model = GTK_TREE_MODEL(status_window->model); - const char *cur; + char *cur; if (!gtk_tree_model_get_iter_first(model, iter)) return FALSE; gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1); if (!strcmp(title, cur)) + { + g_free(cur); return TRUE; + } + g_free(cur); while (gtk_tree_model_iter_next(model, iter)) { gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &cur, -1); if (!strcmp(title, cur)) + { + g_free(cur); return TRUE; + } + g_free(cur); } return FALSE; @@ -129,12 +137,13 @@ status_window_modify_foreach(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) { - const char *title; + char *title; GaimSavedStatus *status; gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1); status = gaim_savedstatus_find(title); + g_free(title); gaim_gtk_status_editor_show(status); } @@ -166,7 +175,7 @@ status_window_delete_foreach(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) { - const char *title; + char *title; char *title_escaped, *buf; gtk_tree_model_get(model, iter, STATUS_WINDOW_COLUMN_TITLE, &title, -1); @@ -174,7 +183,7 @@ title_escaped = gaim_escape_html(title); buf = g_strdup_printf(_("Are you sure you want to delete %s?"), title_escaped); free(title_escaped); - gaim_request_action(NULL, NULL, buf, NULL, 0, g_strdup(title), 2, + gaim_request_action(NULL, NULL, buf, NULL, 0, title, 2, _("Delete"), status_window_delete_confirm_cb, _("Cancel"), g_free); g_free(buf); @@ -251,11 +260,16 @@ static gboolean search_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer search_data) { - const char *haystack; + gboolean result; + char *haystack; gtk_tree_model_get(model, iter, column, &haystack, -1); - return (gaim_strcasestr(haystack, key) == NULL); + result = (gaim_strcasestr(haystack, key) == NULL); + + g_free(haystack); + + return result; } static GtkWidget * diff -r d90ece33e72f -r 911530134bf8 src/notify.c --- a/src/notify.c Mon Dec 27 21:24:20 2004 +0000 +++ b/src/notify.c Tue Dec 28 06:24:32 2004 +0000 @@ -155,8 +155,36 @@ return NULL; } -void *gaim_notify_userinfo(GaimConnection *gc, const char *who, const char *title, - const char *primary, const char *secondary, +void * +gaim_notify_searchresults(GaimConnection *gc, const char *title, + const char *primary, const char *secondary, + const char **results, GCallback cb, void *user_data) +{ + GaimNotifyUiOps *ops; + + ops = gaim_notify_get_ui_ops(); + + if (ops != NULL && ops->notify_searchresults != NULL) { + GaimNotifyInfo *info; + + info = g_new0(GaimNotifyInfo, 1); + info->type = GAIM_NOTIFY_SEARCHRESULTS; + info->handle = gc; + info->ui_handle = ops->notify_searchresults(gc, title, primary, + secondary, results, + cb, user_data); + + handles = g_list_append(handles, info); + + return info->ui_handle; + } + + return NULL; +} + +void * +gaim_notify_userinfo(GaimConnection *gc, const char *who, const char *title, + const char *primary, const char *secondary, const char *text, GCallback cb, void *user_data) { GaimNotifyUiOps *ops; diff -r d90ece33e72f -r 911530134bf8 src/notify.h --- a/src/notify.h Mon Dec 27 21:24:20 2004 +0000 +++ b/src/notify.h Tue Dec 28 06:24:32 2004 +0000 @@ -36,12 +36,13 @@ */ typedef enum { - GAIM_NOTIFY_MESSAGE = 0, /**< Message notification. */ - GAIM_NOTIFY_EMAIL, /**< Single e-mail notification. */ - GAIM_NOTIFY_EMAILS, /**< Multiple e-mail notification. */ - GAIM_NOTIFY_FORMATTED, /**< Formatted text. */ - GAIM_NOTIFY_USERINFO, /**< Formatted userinfo text. */ - GAIM_NOTIFY_URI /**< URI notification or display. */ + GAIM_NOTIFY_MESSAGE = 0, /**< Message notification. */ + GAIM_NOTIFY_EMAIL, /**< Single e-mail notification. */ + GAIM_NOTIFY_EMAILS, /**< Multiple e-mail notification. */ + GAIM_NOTIFY_FORMATTED, /**< Formatted text. */ + GAIM_NOTIFY_SEARCHRESULTS, /**< Buddy search results. */ + GAIM_NOTIFY_USERINFO, /**< Formatted userinfo text. */ + GAIM_NOTIFY_URI /**< URI notification or display. */ } GaimNotifyType; @@ -74,6 +75,10 @@ void *(*notify_formatted)(const char *title, const char *primary, const char *secondary, const char *text, GCallback cb, void *user_data); + void *(*notify_searchresults)(GaimConnection *gc, const char *title, + const char *primary, const char *secondary, + const char **results, GCallback cb, + void *user_data); void *(*notify_userinfo)(GaimConnection *gc, const char *who, const char *title, const char *primary, const char *secondary, const char *text, @@ -176,6 +181,28 @@ const char *text, GCallback cb, void *user_data); /** + * Displays results from a buddy search. This can be, for example, + * a window with a list of all found buddies, where you are given the + * option of adding buddies to your buddy list. + * + * @param gc The GaimConnection handle associated with the information. + * @param title The title of the message. If this is NULL, the title + * will be "Search Results." + * @param primary The main point of the message. + * @param secondary The secondary information. + * @param results An array of null-terminated buddy names. + * @param cb The callback to call when the user closes + * the notification. + * @param user_data The data to pass to the callback. + * + * @return A UI-specific handle. + */ +void *gaim_notify_searchresults(GaimConnection *gc, const char *title, + const char *primary, const char *secondary, + const char **results, GCallback cb, + void *user_data); + +/** * Displays user information with formatted text, passing information giving * the connection and username from which the user information came. * diff -r d90ece33e72f -r 911530134bf8 src/protocols/oscar/oscar.c --- a/src/protocols/oscar/oscar.c Mon Dec 27 21:24:20 2004 +0000 +++ b/src/protocols/oscar/oscar.c Tue Dec 28 06:24:32 2004 +0000 @@ -5197,7 +5197,7 @@ { GaimConnection *gc = sess->aux_data; gchar *secondary; - GString *text; + gchar **screennames; int i, num; va_list ap; char *email, *SNs; @@ -5208,14 +5208,19 @@ SNs = va_arg(ap, char *); va_end(ap); + /* TODO: Need to use ngettext() here */ secondary = g_strdup_printf(_("The following screen names are associated with %s"), email); - text = g_string_new(""); + + screennames = g_malloc((num + 1) * sizeof(gchar *)); for (i = 0; i < num; i++) - g_string_append_printf(text, "%s
", &SNs[i * (MAXSNLEN + 1)]); - gaim_notify_formatted(gc, NULL, _("Search Results"), secondary, text->str, NULL, NULL); + screennames[i] = g_strdup(&SNs[i * (MAXSNLEN + 1)]); + screennames[num] = NULL; + + gaim_notify_searchresults(gc, NULL, NULL, secondary, + (const char **)screennames, NULL, NULL); g_free(secondary); - g_string_free(text, TRUE); + g_strfreev(screennames); return 1; }