* @file gtkutils.h GTK+ utility functions * Gaim is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * 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 # include <gtkspell/gtkspell.h> #include <gdk/gdkkeysyms.h> #include "conversation.h" #include "gtkimhtmltoolbar.h" static guint accels_save_timer = 0; url_clicked_idle_cb(gpointer data) gaim_notify_uri(NULL, data); url_clicked_cb(GtkWidget *w, const char *uri) g_idle_add(url_clicked_idle_cb, g_strdup(uri)); static GtkIMHtmlFuncs gtkimhtml_cbs = { (GtkIMHtmlGetImageFunc)gaim_imgstore_get, (GtkIMHtmlGetImageDataFunc)gaim_imgstore_get_data, (GtkIMHtmlGetImageSizeFunc)gaim_imgstore_get_size, (GtkIMHtmlGetImageFilenameFunc)gaim_imgstore_get_filename, gaim_setup_imhtml(GtkWidget *imhtml) g_return_if_fail(imhtml != NULL); g_return_if_fail(GTK_IS_IMHTML(imhtml)); g_signal_connect(G_OBJECT(imhtml), "url_clicked", G_CALLBACK(url_clicked_cb), NULL); gaim_gtkthemes_smiley_themeize(imhtml); gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), >kimhtml_cbs); gaim_gtk_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret) GtkWidget *toolbar = NULL; frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), vbox); toolbar = gtk_imhtmltoolbar_new(); gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); gtk_widget_show(toolbar); sep = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0); sw = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); imhtml = gtk_imhtml_new(NULL, NULL); gtk_imhtml_set_editable(GTK_IMHTML(imhtml), editable); gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml), GTK_IMHTML_ALL ^ GTK_IMHTML_IMAGE); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(imhtml), GTK_WRAP_WORD_CHAR); if (editable && gaim_prefs_get_bool("/gaim/gtk/conversations/spellcheck")) gaim_gtk_setup_gtkspell(GTK_TEXT_VIEW(imhtml)); gtk_imhtmltoolbar_attach(GTK_IMHTMLTOOLBAR(toolbar), imhtml); gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(toolbar), "default"); gaim_setup_imhtml(imhtml); gtk_container_add(GTK_CONTAINER(sw), imhtml); if (editable && (toolbar_ret != NULL)) gaim_gtk_set_sensitive_if_input(GtkWidget *entry, GtkWidget *dialog) const char *text = gtk_entry_get_text(GTK_ENTRY(entry)); gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK, gaim_gtk_toggle_sensitive(GtkWidget *widget, GtkWidget *to_toggle) sensitivity = GTK_WIDGET_IS_SENSITIVE(to_toggle); gtk_widget_set_sensitive(to_toggle, !sensitivity); gaim_gtk_toggle_sensitive_array(GtkWidget *w, GPtrArray *data) for (i=0; i < data->len; i++) { element = g_ptr_array_index(data,i); sensitivity = GTK_WIDGET_IS_SENSITIVE(element); gtk_widget_set_sensitive(element, !sensitivity); gaim_gtk_toggle_showhide(GtkWidget *widget, GtkWidget *to_toggle) if (GTK_WIDGET_VISIBLE(to_toggle)) gtk_widget_hide(to_toggle); gtk_widget_show(to_toggle); void gaim_separator(GtkWidget *menu) menuitem = gtk_separator_menu_item_new(); gtk_widget_show(menuitem); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); GtkWidget *gaim_new_item(GtkWidget *menu, const char *str) menuitem = gtk_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); gtk_widget_show(menuitem); label = gtk_label_new(str); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_label_set_pattern(GTK_LABEL(label), "_"); gtk_container_add(GTK_CONTAINER(menuitem), label); /* FIXME: Go back and fix this gtk_widget_add_accelerator(menuitem, "activate", accel, str[0], GDK_MOD1_MASK, GTK_ACCEL_LOCKED); gaim_set_accessible_label (menuitem, label); GtkWidget *gaim_new_check_item(GtkWidget *menu, const char *str, GtkSignalFunc sf, gpointer data, gboolean checked) menuitem = gtk_check_menu_item_new_with_mnemonic(str); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), checked); g_signal_connect(G_OBJECT(menuitem), "activate", sf, data); gtk_widget_show_all(menuitem); gaim_pixbuf_toolbar_button_from_stock(const char *icon) GtkWidget *button, *image, *bbox; button = gtk_toggle_button_new(); gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); bbox = gtk_vbox_new(FALSE, 0); gtk_container_add (GTK_CONTAINER(button), bbox); image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU); gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0); gtk_widget_show_all(bbox); gaim_pixbuf_button_from_stock(const char *text, const char *icon, GaimButtonOrientation style) GtkWidget *button, *image, *label, *bbox, *ibox, *lbox = NULL; button = gtk_button_new(); if (style == GAIM_BUTTON_HORIZONTAL) { bbox = gtk_hbox_new(FALSE, 0); ibox = gtk_hbox_new(FALSE, 0); lbox = gtk_hbox_new(FALSE, 0); bbox = gtk_vbox_new(FALSE, 0); ibox = gtk_vbox_new(FALSE, 0); lbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(button), bbox); gtk_box_pack_start_defaults(GTK_BOX(bbox), ibox); image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_BUTTON); gtk_box_pack_end(GTK_BOX(ibox), image, FALSE, TRUE, 0); gtk_box_pack_start_defaults(GTK_BOX(bbox), lbox); label = gtk_label_new(NULL); gtk_label_set_text_with_mnemonic(GTK_LABEL(label), text); gtk_label_set_mnemonic_widget(GTK_LABEL(label), button); gtk_box_pack_start(GTK_BOX(lbox), label, FALSE, TRUE, 0); gaim_set_accessible_label (button, label); gtk_widget_show_all(bbox); GtkWidget *gaim_new_item_from_stock(GtkWidget *menu, const char *str, const char *icon, GtkSignalFunc sf, gpointer data, guint accel_key, guint accel_mods, char *mod) menuitem = gtk_menu_item_new_with_mnemonic(str); menuitem = gtk_image_menu_item_new_with_mnemonic(str); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); g_signal_connect(G_OBJECT(menuitem), "activate", sf, data); image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); /* FIXME: this isn't right label = gtk_label_new(mod); gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 2); gtk_widget_add_accelerator(menuitem, "activate", accel, accel_key, accel_mods, GTK_ACCEL_LOCKED); gtk_widget_show_all(menuitem); gaim_gtk_make_frame(GtkWidget *parent, const char *title) GtkWidget *vbox, *label, *hbox; vbox = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(parent), vbox, FALSE, FALSE, 0); label = gtk_label_new(NULL); labeltitle = g_strdup_printf("<span weight=\"bold\">%s</span>", title); gtk_label_set_markup(GTK_LABEL(label), labeltitle); gtk_misc_set_alignment(GTK_MISC(label), 0, 0); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); gaim_set_accessible_label (vbox, label); hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); label = gtk_label_new(" "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); vbox = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); protocol_menu_cb(GtkWidget *optmenu, GCallback cb) menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)); item = gtk_menu_get_active(GTK_MENU(menu)); protocol = g_object_get_data(G_OBJECT(item), "protocol"); user_data = (g_object_get_data(G_OBJECT(optmenu), "user_data")); ((void (*)(GtkWidget *, const char *, gpointer))cb)(item, protocol, gaim_gtk_protocol_option_menu_new(const char *id, GCallback cb, GaimPluginProtocolInfo *prpl_info; int i, selected_index = -1; optmenu = gtk_option_menu_new(); gtk_widget_show(optmenu); g_object_set_data(G_OBJECT(optmenu), "user_data", user_data); sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); for (p = gaim_plugins_get_protocols(), i = 0; plugin = (GaimPlugin *)p->data; prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); item = gtk_menu_item_new(); hbox = gtk_hbox_new(FALSE, 4); gtk_container_add(GTK_CONTAINER(item), hbox); proto_name = prpl_info->list_icon(NULL, NULL); g_snprintf(buf, sizeof(buf), "%s.png", proto_name); filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", pixbuf = gdk_pixbuf_new_from_file(filename, NULL); /* Scale and insert the image */ scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, image = gtk_image_new_from_pixbuf(scale); g_object_unref(G_OBJECT(pixbuf)); g_object_unref(G_OBJECT(scale)); gtk_size_group_add_widget(sg, image); gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); label = gtk_label_new(plugin->info->name); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); g_object_set_data(G_OBJECT(item), "protocol", plugin->info->id); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gaim_set_accessible_label (item, label); if (id != NULL && !strcmp(plugin->info->id, id)) gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu); if (selected_index != -1) gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), selected_index); g_signal_connect(G_OBJECT(optmenu), "changed", G_CALLBACK(protocol_menu_cb), cb); gaim_gtk_account_option_menu_get_selected(GtkWidget *optmenu) GtkWidget *menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)); GtkWidget *item = gtk_menu_get_active(GTK_MENU(menu)); return g_object_get_data(G_OBJECT(item), "account"); account_menu_cb(GtkWidget *optmenu, GCallback cb) menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)); item = gtk_menu_get_active(GTK_MENU(menu)); account = g_object_get_data(G_OBJECT(item), "account"); user_data = g_object_get_data(G_OBJECT(optmenu), "user_data"); ((void (*)(GtkWidget *, GaimAccount *, gpointer))cb)(item, account, create_account_menu(GtkWidget *optmenu, GaimAccount *default_account, GaimFilterAccountFunc filter_func, gboolean show_all) int i, selected_index = -1; list = gaim_accounts_get_all(); list = gaim_connections_get_all(); sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); for (p = list, i = 0; p != NULL; p = p->next, i++) { GaimPluginProtocolInfo *prpl_info = NULL; account = (GaimAccount *)p->data; GaimConnection *gc = (GaimConnection *)p->data; account = gaim_connection_get_account(gc); if (filter_func && !filter_func(account)) { plugin = gaim_find_prpl(gaim_account_get_protocol_id(account)); prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); item = gtk_menu_item_new(); hbox = gtk_hbox_new(FALSE, 4); gtk_container_add(GTK_CONTAINER(item), hbox); proto_name = prpl_info->list_icon(account, NULL); g_snprintf(buf, sizeof(buf), "%s.png", proto_name); filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", pixbuf = gdk_pixbuf_new_from_file(filename, NULL); /* Scale and insert the image */ scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, if (gaim_account_is_disconnected(account) && show_all && gaim_connections_get_all()) gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); image = gtk_image_new_from_pixbuf(scale); g_object_unref(G_OBJECT(pixbuf)); g_object_unref(G_OBJECT(scale)); gtk_size_group_add_widget(sg, image); gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); if (gaim_account_get_alias(account)) { g_snprintf(buf, sizeof(buf), "%s (%s) (%s)", gaim_account_get_username(account), gaim_account_get_alias(account), gaim_account_get_protocol_name(account)); g_snprintf(buf, sizeof(buf), "%s (%s)", gaim_account_get_username(account), gaim_account_get_protocol_name(account)); label = gtk_label_new(buf); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); g_object_set_data(G_OBJECT(item), "account", account); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gaim_set_accessible_label (item, label); if (default_account != NULL && account == default_account) gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu); /* Set the place we should be at. */ if (selected_index != -1) gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), selected_index); regenerate_account_menu(GtkWidget *optmenu) GaimFilterAccountFunc filter_func; menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)); item = gtk_menu_get_active(GTK_MENU(menu)); account = g_object_get_data(G_OBJECT(item), "account"); show_all = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(optmenu), filter_func = g_object_get_data(G_OBJECT(optmenu), gtk_option_menu_remove_menu(GTK_OPTION_MENU(optmenu)); create_account_menu(optmenu, account, filter_func, show_all); account_menu_sign_on_off_cb(GaimConnection *gc, GtkWidget *optmenu) regenerate_account_menu(optmenu); account_menu_added_removed_cb(GaimAccount *account, GtkWidget *optmenu) regenerate_account_menu(optmenu); account_menu_destroyed_cb(GtkWidget *optmenu, GdkEvent *event, gaim_signals_disconnect_by_handle(optmenu); gaim_gtk_account_option_menu_set_selected(GtkWidget *optmenu, GaimAccount *account) GaimFilterAccountFunc filter_func; menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)); item = gtk_menu_get_active(GTK_MENU(menu)); curaccount = g_object_get_data(G_OBJECT(item), "account"); if (account == curaccount) show_all = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(optmenu), filter_func = g_object_get_data(G_OBJECT(optmenu), gtk_option_menu_remove_menu(GTK_OPTION_MENU(optmenu)); create_account_menu(optmenu, account, filter_func, show_all); gaim_gtk_account_option_menu_new(GaimAccount *default_account, gboolean show_all, GCallback cb, GaimFilterAccountFunc filter_func, /* Create the option menu */ optmenu = gtk_option_menu_new(); gtk_widget_show(optmenu); g_signal_connect(G_OBJECT(optmenu), "destroy", G_CALLBACK(account_menu_destroyed_cb), NULL); /* Register the gaim sign on/off event callbacks. */ gaim_signal_connect(gaim_connections_get_handle(), "signed-on", optmenu, GAIM_CALLBACK(account_menu_sign_on_off_cb), gaim_signal_connect(gaim_connections_get_handle(), "signed-off", optmenu, GAIM_CALLBACK(account_menu_sign_on_off_cb), gaim_signal_connect(gaim_accounts_get_handle(), "account-added", optmenu, GAIM_CALLBACK(account_menu_added_removed_cb), gaim_signal_connect(gaim_accounts_get_handle(), "account-removed", optmenu, GAIM_CALLBACK(account_menu_added_removed_cb), g_object_set_data(G_OBJECT(optmenu), "user_data", user_data); g_object_set_data(G_OBJECT(optmenu), "show_all", GINT_TO_POINTER(show_all)); g_object_set_data(G_OBJECT(optmenu), "filter_func", /* Create and set the actual menu. */ create_account_menu(optmenu, default_account, filter_func, show_all); /* And now the last callback. */ g_signal_connect(G_OBJECT(optmenu), "changed", G_CALLBACK(account_menu_cb), cb); gaim_gtk_check_if_dir(const char *path, GtkFileSelection *filesel) if (g_file_test(path, G_FILE_TEST_IS_DIR)) { /* append a / if needed */ if (path[strlen(path) - 1] != G_DIR_SEPARATOR) { dirname = g_strconcat(path, G_DIR_SEPARATOR_S, NULL); dirname = g_strdup(path); gtk_file_selection_set_filename(filesel, dirname); gaim_gtk_setup_gtkspell(GtkTextView *textview) g_return_if_fail(textview != NULL); g_return_if_fail(GTK_IS_TEXT_VIEW(textview)); if (gtkspell_new_attach(textview, locale, &error) == NULL && error) gaim_debug_warning("gtkspell", "Failed to setup GtkSpell: %s\n", #endif /* USE_GTKSPELL */ gaim_gtk_save_accels_cb(GtkAccelGroup *accel_group, guint arg1, GdkModifierType arg2, GClosure *arg3, gaim_debug(GAIM_DEBUG_MISC, "accels", "accel changed, scheduling save.\n"); accels_save_timer = g_timeout_add(5000, gaim_gtk_save_accels, NULL); gaim_gtk_save_accels(gpointer data) filename = g_build_filename(gaim_user_dir(), G_DIR_SEPARATOR_S, gaim_debug(GAIM_DEBUG_MISC, "accels", "saving accels to %s\n", filename); gtk_accel_map_save(filename); filename = g_build_filename(gaim_user_dir(), G_DIR_SEPARATOR_S, gtk_accel_map_load(filename); gaim_gtk_parse_x_im_contact(const char *msg, gboolean all_accounts, GaimAccount **ret_account, char **ret_protocol, char **ret_username, char **ret_alias) g_return_val_if_fail(msg != NULL, FALSE); g_return_val_if_fail(ret_protocol != NULL, FALSE); g_return_val_if_fail(ret_username != NULL, FALSE); while (*s != '\r' && *s != '\n' && *s != '\0') while (*s != '\r' && *s != '\n' && *s != '\0' && *s != ' ') if (*s != '\0') *s++ = '\0'; /* Clear past any whitespace */ while (*s != '\0' && *s == ' ') /* Now let's grab until the end of the line. */ while (*s != '\r' && *s != '\n' && *s != '\0') if (*s == '\r') *s++ = '\0'; if (*s == '\n') *s++ = '\0'; if ((c = strchr(key, ':')) != NULL) if (!g_ascii_strcasecmp(key, "X-IM-Username:")) username = g_strdup(value); else if (!g_ascii_strcasecmp(key, "X-IM-Protocol:")) protocol = g_strdup(value); else if (!g_ascii_strcasecmp(key, "X-IM-Alias:")) if (username != NULL && protocol != NULL) *ret_username = username; *ret_protocol = protocol; /* Check for a compatible account. */ GaimAccount *account = NULL; list = gaim_accounts_get_all(); list = gaim_connections_get_all(); for (l = list; l != NULL; l = l->next) GaimPluginProtocolInfo *prpl_info = NULL; account = (GaimAccount *)l->data; plugin = gaim_plugins_find_with_id( gaim_account_get_protocol_id(account)); prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); gc = (GaimConnection *)l->data; account = gaim_connection_get_account(gc); prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); protoname = prpl_info->list_icon(account, NULL); if (!strcmp(protoname, protocol)) /* Special case for AIM and ICQ */ if (account == NULL && (!strcmp(protocol, "aim") || !strcmp(protocol, "icq"))) for (l = list; l != NULL; l = l->next) GaimPluginProtocolInfo *prpl_info = NULL; account = (GaimAccount *)l->data; plugin = gaim_plugins_find_with_id( gaim_account_get_protocol_id(account)); prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); gc = (GaimConnection *)l->data; account = gaim_connection_get_account(gc); prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); protoname = prpl_info->list_icon(account, NULL); if (!strcmp(protoname, "aim") || !strcmp(protoname, "icq")) if (username != NULL) g_free(username); if (protocol != NULL) g_free(protocol); if (alias != NULL) g_free(alias); gaim_set_accessible_label (GtkWidget *w, GtkWidget *l) const gchar *existing_name; acc = gtk_widget_get_accessible (w); label = gtk_widget_get_accessible (l); /* If this object has no name, set it's name with the label text */ existing_name = atk_object_get_name (acc); label_text = gtk_label_get_text (GTK_LABEL(l)); atk_object_set_name (acc, label_text); /* Create the labeled-by relation */ set = atk_object_ref_relation_set (acc); relation = atk_relation_new (rel_obj, 1, ATK_RELATION_LABELLED_BY); atk_relation_set_add (set, relation); g_object_unref (relation); /* Create the label-for relation */ set = atk_object_ref_relation_set (label); relation = atk_relation_new (rel_obj, 1, ATK_RELATION_LABEL_FOR); atk_relation_set_add (set, relation); g_object_unref (relation); #if GTK_CHECK_VERSION(2,2,0) gaim_gtk_menu_position_func(GtkMenu *menu, GtkRequisition requisition; gint space_left, space_right, space_above, space_below; g_return_if_fail(GTK_IS_MENU(menu)); widget = GTK_WIDGET(menu); screen = gtk_widget_get_screen(widget); xthickness = widget->style->xthickness; ythickness = widget->style->ythickness; rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL); * We need the requisition to figure out the right place to * popup the menu. In fact, we always need to ask here, since * if a size_request was queued while we weren't popped up, * the requisition won't have been recomputed yet. gtk_widget_size_request (widget, &requisition); monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y); * The placement of popup menus horizontally works like this (with * - If there is enough room to the right (left) of the mouse cursor, * position the menu there. * - Otherwise, if if there is enough room to the left (right) of the * mouse cursor, position the menu there. * - Otherwise if the menu is smaller than the monitor, position it * on the side of the mouse cursor that has the most space available * - Otherwise (if there is simply not enough room for the menu on the * monitor), position it as far left (right) as possible. * Positioning in the vertical direction is similar: first try below * mouse cursor, then above. gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); space_left = *x - monitor.x; space_right = monitor.x + monitor.width - *x - 1; space_above = *y - monitor.y; space_below = monitor.y + monitor.height - *y - 1; /* position horizontally */ /* the amount of space we need to position the menu. Note the * menu is offset "xthickness" pixels needed_width = requisition.width - xthickness; if (needed_width <= space_left || needed_width <= space_right) if ((rtl && needed_width <= space_left) || (!rtl && needed_width > space_right)) *x = *x + xthickness - requisition.width + 1; /* x is clamped on-screen further down */ else if (requisition.width <= monitor.width) /* the menu is too big to fit on either side of the mouse * cursor, but smaller than the monitor. Position it on * the side that has the most space if (space_left > space_right) *x = monitor.x + monitor.width - requisition.width; else /* menu is simply too big for the monitor */ *x = monitor.x + monitor.width - requisition.width; /* Position vertically. The algorithm is the same as above, but * simpler because we don't have to take RTL into account. needed_height = requisition.height - ythickness; if (needed_height <= space_above || needed_height <= space_below) if (needed_height <= space_below) *y = *y + ythickness - requisition.height + 1; *y = CLAMP (*y, monitor.y, monitor.y + monitor.height - requisition.height); else if (needed_height > space_below && needed_height > space_above) if (space_below >= space_above) *y = monitor.y + monitor.height - requisition.height; gaim_gtk_treeview_popup_menu_position_func(GtkMenu *menu, GtkWidget *widget = GTK_WIDGET(data); GtkTreeView *tv = GTK_TREE_VIEW(data); gint ythickness = GTK_WIDGET(menu)->style->ythickness; gdk_window_get_origin (widget->window, x, y); gtk_tree_view_get_cursor (tv, &path, &col); gtk_tree_view_get_cell_area (tv, path, col, &rect); *y += rect.y+rect.height+ythickness; #if GTK_CHECK_VERSION(2,2,0) gaim_gtk_menu_position_func (menu, x, y, push_in, data); static void dnd_image_ok_callback(_DndData *data, int choice) GaimGtkConversation *gtkconv; if (!g_file_get_contents(data->filename, &filedata, &size, str = g_strdup_printf(_("The following error has occurred loading %s: %s"), data->filename, err->message); gaim_notify_error(NULL, NULL, _("Failed to load image"), gaim_buddy_icons_set_for_user(data->account, data->who, filedata, size); serv_send_file(gaim_account_get_connection(data->account), data->who, data->filename); conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, data->account, data->who); gtkconv = GAIM_GTK_CONVERSATION(conv); if (!g_file_get_contents(data->filename, &filedata, &size, str = g_strdup_printf(_("The following error has occurred loading %s: %s"), data->filename, err->message); gaim_notify_error(NULL, NULL, _("Failed to load image"), id = gaim_imgstore_add(filedata, size, data->filename); gtk_text_buffer_get_iter_at_mark(GTK_IMHTML(gtkconv->entry)->text_buffer, &iter, gtk_text_buffer_get_insert(GTK_IMHTML(gtkconv->entry)->text_buffer)); gtk_imhtml_insert_image_at_iter(GTK_IMHTML(gtkconv->entry), id, &iter); static void dnd_image_cancel_callback(_DndData *data, int choice) static void dnd_set_icon_ok_cb(_DndData *data) dnd_image_ok_callback(data, DND_BUDDY_ICON); static void dnd_set_icon_cancel_cb(_DndData *data) gaim_dnd_file_manage(GtkSelectionData *sd, GaimAccount *account, const char *who) GList *files = gaim_uri_list_extract_filenames((const gchar *)sd->data); GaimConnection *gc = gaim_account_get_connection(account); GaimPluginProtocolInfo *prpl_info = NULL; gboolean file_send_ok = FALSE; g_return_if_fail(account != NULL); g_return_if_fail(who != NULL); for(tmp = files; tmp != NULL ; tmp = g_list_next(tmp)) { gchar *filename = tmp->data; gchar *basename = g_path_get_basename(filename); /* Set the default action: don't send anything */ /* XXX - Make ft API support creating a transfer with more than one file */ if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { /* XXX - make ft api suupport sending a directory */ /* Are we dealing with a directory? */ if (g_file_test(filename, G_FILE_TEST_IS_DIR)) { str = g_strdup_printf(_("Cannot send folder %s."), basename); gaim_notify_error(NULL, NULL, str,_("Gaim cannot transfer a folder. You will need to send the files within individually")); /* Are we dealing with an image? */ pb = gdk_pixbuf_new_from_file(filename, NULL); _DndData *data = g_malloc(sizeof(_DndData)); gboolean ft = FALSE, im = FALSE; data->who = g_strdup(who); data->filename = g_strdup(filename); prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); if (prpl_info && prpl_info->options & OPT_PROTO_IM_IMAGE) if (prpl_info && prpl_info->can_receive_file) ft = prpl_info->can_receive_file(gc, who); gaim_request_choice(NULL, NULL, _("You have dragged an image"), _("You can send this image as a file transfer, " "embed it into this message, or use it as the buddy icon for this user."), DND_FILE_TRANSFER, "OK", (GCallback)dnd_image_ok_callback, "Cancel", (GCallback)dnd_image_cancel_callback, data, _("Set as buddy icon"), DND_BUDDY_ICON, _("Send image file"), DND_FILE_TRANSFER, _("Insert in message"), DND_IM_IMAGE, NULL); gaim_request_yes_no(NULL, NULL, _("You have dragged an image"), _("Would you like to set it as the buddy icon for this user?"), 0, data, (GCallback)dnd_set_icon_ok_cb, (GCallback)dnd_set_icon_cancel_cb); gaim_request_choice(NULL, NULL, _("You have dragged an image"), ft ? _("You can send this image as a file transfer or " "embed it into this message, or use it as the buddy icon for this user.") : _("You can insert this image into this message, or use it as the buddy icon for this user"), ft ? DND_FILE_TRANSFER : DND_IM_IMAGE, "OK", (GCallback)dnd_image_ok_callback, "Cancel", (GCallback)dnd_image_cancel_callback, data, _("Set as buddy icon"), DND_BUDDY_ICON, ft ? _("Send image file") : _("Insert in message"), ft ? DND_FILE_TRANSFER : DND_IM_IMAGE, NULL); /* Are we trying to send a .desktop file? */ else if (gaim_str_has_suffix(basename, ".desktop") && (item = gaim_desktop_item_new_from_file(filename))) { GaimDesktopItemType dtype; const char *itemname = NULL; #if GTK_CHECK_VERSION(2,6,0) const char * const *langs; langs = g_get_language_names(); for (i = 0; langs[i]; i++) { g_snprintf(key, sizeof(key), "Name[%s]", langs[i]); itemname = gaim_desktop_item_get_string(item, key); const char *lang = g_getenv("LANG"); g_snprintf(key, sizeof(key), "Name[%s]", lang); itemname = gaim_desktop_item_get_string(item, key); itemname = gaim_desktop_item_get_string(item, "Name"); dtype = gaim_desktop_item_get_entry_type(item); GaimGtkConversation *gtkconv; case GAIM_DESKTOP_ITEM_TYPE_LINK: conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, who); gtkconv = GAIM_GTK_CONVERSATION(conv); gtk_imhtml_insert_link(GTK_IMHTML(gtkconv->entry), gtk_text_buffer_get_insert(GTK_IMHTML(gtkconv->entry)->text_buffer), gaim_desktop_item_get_string(item, "URL"), itemname); /* I don't know if we really want to do anything here. Most of the desktop item types are crap like * "MIME Type" (I have no clue how that would be a desktop item) and "Comment"... nothing we can really * send. The only logical one is "Application," but do we really want to send a binary and nothing else? * Probably not. I'll just give an error and return. */ /* The original patch sent the icon used by the launcher. That's probably wrong */ gaim_notify_error(NULL, NULL, _("Cannot send launcher"), _("You dragged a desktop launcher. " "Most likely you wanted to send whatever this launcher points to instead of this launcher" gaim_desktop_item_unref(item); /* Everything is fine, let's send */ serv_send_file(gc, who, filename); void gaim_gtk_buddy_icon_get_scale_size(GdkPixbuf *buf, GaimBuddyIconSpec *spec, int *width, int *height) *width = gdk_pixbuf_get_width(buf); *height = gdk_pixbuf_get_height(buf); gaim_buddy_icon_get_scale_size(spec, width, height); /* and now for some arbitrary sanity checks */ gaim_gtk_create_prpl_icon(GaimAccount *account, double scale_factor) GaimPluginProtocolInfo *prpl_info; const char *protoname = NULL; char buf[256]; /* TODO: We should use a define for max file length */ GdkPixbuf *pixbuf, *scaled; g_return_val_if_fail(account != NULL, NULL); prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); if (prpl_info->list_icon == NULL) protoname = prpl_info->list_icon(account, NULL); * Status icons will be themeable too, and then it will look up * protoname from the theme g_snprintf(buf, sizeof(buf), "%s.png", protoname); filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", pixbuf = gdk_pixbuf_new_from_file(filename, NULL); scaled = gdk_pixbuf_scale_simple(pixbuf, 32*scale_factor, 32*scale_factor, GDK_INTERP_BILINEAR); overlay_status_onto_icon(GdkPixbuf *pixbuf, GaimStatusPrimitive primitive) type_name = gaim_primitive_get_id_from_type(primitive); g_snprintf(basename, sizeof(basename), "%s.png", type_name); filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", basename, NULL); emblem = gdk_pixbuf_new_from_file(filename, NULL); int width, height, emblem_width, emblem_height; int new_emblem_width, new_emblem_height; width = gdk_pixbuf_get_width(pixbuf); height = gdk_pixbuf_get_height(pixbuf); emblem_width = gdk_pixbuf_get_width(emblem); emblem_height = gdk_pixbuf_get_height(emblem); * Figure out how big to make the emblem. Normally the emblem * will have half the width of the pixbuf. But we don't make * an emblem any smaller than 10 pixels because it becomes * unrecognizable, unless the width of the pixbuf is less than * 10 pixels, in which case we make the emblem width the same new_emblem_width = MAX(width / 2, MIN(width, 10)); new_emblem_height = MAX(height / 2, MIN(height, 10)); /* Overlay emblem onto the bottom right corner of pixbuf */ gdk_pixbuf_composite(emblem, pixbuf, width - new_emblem_width, height - new_emblem_height, new_emblem_width, new_emblem_height, width - new_emblem_width, height - new_emblem_height, (double)new_emblem_width / (double)emblem_width, (double)new_emblem_height / (double)emblem_height, gaim_gtk_create_prpl_icon_with_status(GaimAccount *account, GaimStatusType *status_type, double scale_factor) pixbuf = gaim_gtk_create_prpl_icon(account, scale_factor); * TODO: Let the prpl pick the emblem on a per status basis, * and only use the primitive as a fallback? return overlay_status_onto_icon(pixbuf, gaim_status_type_get_primitive(status_type)); gaim_gtk_create_gaim_icon_with_status(GaimStatusPrimitive primitive, double scale_factor) GdkPixbuf *orig, *pixbuf; filename = g_build_filename(DATADIR, "pixmaps", "gaim.png", NULL); orig = gdk_pixbuf_new_from_file(filename, NULL); pixbuf = gdk_pixbuf_scale_simple(orig, 32*scale_factor, 32*scale_factor, GDK_INTERP_BILINEAR); g_object_unref(G_OBJECT(orig)); return overlay_status_onto_icon(pixbuf, primitive); menu_action_cb(GtkMenuItem *item, gpointer object) void (*callback)(gpointer, gpointer); callback = g_object_get_data(G_OBJECT(item), "gaimcallback"); data = g_object_get_data(G_OBJECT(item), "gaimcallbackdata"); gaim_gtk_append_menu_action(GtkWidget *menu, GaimMenuAction *act, GtkWidget *menuitem, *label; if (act->children == NULL) { menuitem = gtk_menu_item_new(); label = gtk_label_new(""); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_label_set_pattern(GTK_LABEL(label), "_"); gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), gtk_container_add(GTK_CONTAINER(menuitem), label); if (act->callback != NULL) { g_object_set_data(G_OBJECT(menuitem), g_object_set_data(G_OBJECT(menuitem), g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menu_action_cb), gtk_widget_set_sensitive(menuitem, FALSE); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); GtkWidget *submenu = NULL; menuitem = gtk_menu_item_new_with_mnemonic(act->label); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); submenu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); for (l = act->children; l; l = l->next) { GaimMenuAction *act = (GaimMenuAction *)l->data; gaim_gtk_append_menu_action(submenu, act, object); g_list_free(act->children); gaim_menu_action_free(act); #if GTK_CHECK_VERSION(2,3,0) # define NEW_STYLE_COMPLETION #ifndef NEW_STYLE_COMPLETION gboolean completion_started; #ifndef NEW_STYLE_COMPLETION completion_entry_event(GtkEditable *entry, GdkEventKey *event, GaimGtkCompletionData *data) if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Tab) gtk_editable_get_selection_bounds(entry, &pos, &end_pos); if (data->completion_started && pos != end_pos && pos > 1 && end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry)))) gtk_editable_select_region(entry, 0, 0); gtk_editable_set_position(entry, -1); else if (event->type == GDK_KEY_PRESS && event->length > 0) gtk_editable_get_selection_bounds(entry, &pos, &end_pos); if (data->completion_started && pos != end_pos && pos > 1 && end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry)))) temp = gtk_editable_get_chars(entry, 0, pos); prefix = g_strconcat(temp, event->string, NULL); else if (pos == end_pos && pos > 1 && end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry)))) prefix = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)), g_completion_complete(data->completion, prefix, &nprefix); gtk_entry_set_text(GTK_ENTRY(entry), nprefix); gtk_editable_set_position(entry, pos); gtk_editable_select_region(entry, pos, -1); data->completion_started = TRUE; destroy_completion_data(GtkWidget *w, GaimGtkCompletionData *data) g_list_foreach(data->completion->items, (GFunc)g_free, NULL); g_completion_free(data->completion); #endif /* !NEW_STYLE_COMPLETION */ #ifdef NEW_STYLE_COMPLETION static gboolean screenname_completion_match_func(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data) model = gtk_entry_completion_get_model (completion); gtk_tree_model_get_value(model, iter, 2, &val1); tmp = g_value_get_string(&val1); if (tmp != NULL && gaim_str_has_prefix(tmp, key)) gtk_tree_model_get_value(model, iter, 3, &val2); tmp = g_value_get_string(&val2); if (tmp != NULL && gaim_str_has_prefix(tmp, key)) static gboolean screenname_completion_match_selected_cb(GtkEntryCompletion *completion, GtkTreeModel *model, GtkTreeIter *iter, gpointer *user_data) GtkWidget *optmenu = user_data[1]; gtk_tree_model_get_value(model, iter, 1, &val); gtk_entry_set_text(GTK_ENTRY(user_data[0]), g_value_get_string(&val)); gtk_tree_model_get_value(model, iter, 4, &val); account = g_value_get_pointer(&val); gaim_gtk_account_option_menu_set_selected(optmenu, account); items = GTK_MENU_SHELL(gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)))->children; if (account == g_object_get_data(G_OBJECT(items->data), "account")) { /* Set the account in the GUI. */ gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), index); } while ((items = items->next) != NULL); add_screenname_autocomplete_entry(GtkListStore *store, const char *buddy_alias, const char *contact_alias, const GaimAccount *account, const char *screenname) gboolean completion_added = FALSE; gchar *normalized_screenname; tmp = g_utf8_normalize(screenname, -1, G_NORMALIZE_DEFAULT); normalized_screenname = g_utf8_casefold(tmp, -1); /* There's no sense listing things like: 'xxx "xxx"' when the screenname and buddy alias match. */ if (buddy_alias && strcmp(buddy_alias, screenname)) { char *completion_entry = g_strdup_printf("%s \"%s\"", screenname, buddy_alias); char *tmp2 = g_utf8_normalize(buddy_alias, -1, G_NORMALIZE_DEFAULT); tmp = g_utf8_casefold(tmp2, -1); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 2, normalized_screenname, g_free(completion_entry); /* There's no sense listing things like: 'xxx "xxx"' when the screenname and contact alias match. */ if (contact_alias && strcmp(contact_alias, screenname)) { /* We don't want duplicates when the contact and buddy alias match. */ if (!buddy_alias || strcmp(contact_alias, buddy_alias)) { char *completion_entry = g_strdup_printf("%s \"%s\"", screenname, contact_alias); char *tmp2 = g_utf8_normalize(contact_alias, -1, G_NORMALIZE_DEFAULT); tmp = g_utf8_casefold(tmp2, -1); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 2, normalized_screenname, g_free(completion_entry); if (completion_added == FALSE) { /* Add the buddy's screenname. */ gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 2, normalized_screenname, g_free(normalized_screenname); #endif /* NEW_STYLE_COMPLETION */ static void get_log_set_name(GaimLogSet *set, gpointer value, gpointer **set_hash_data) /* 1. Don't show buddies because we will have gotten them already. * 2. Only show those with non-NULL accounts that are currently connected. * 3. The boxes that use this autocomplete code handle only IMs. */ (GPOINTER_TO_INT(set_hash_data[1]) || (set->account != NULL && gaim_account_is_connected(set->account))) && set->type == GAIM_LOG_IM) { #ifdef NEW_STYLE_COMPLETION add_screenname_autocomplete_entry((GtkListStore *)set_hash_data[0], NULL, NULL, set->account, set->name); GList **items = ((GList **)set_hash_data[0]); /* Steal the name for the GCompletion. */ *items = g_list_append(*items, set->name); set->name = set->normalized_name = NULL; #endif /* NEW_STYLE_COMPLETION */ #ifdef NEW_STYLE_COMPLETION add_completion_list(GtkListStore *store) GaimBlistNode *gnode, *cnode, *bnode; gboolean all = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(store), "screenname-all")); gpointer set_hash_data[] = {store, GINT_TO_POINTER(all)}; gtk_list_store_clear(store); for (gnode = gaim_get_blist()->root; gnode != NULL; gnode = gnode->next) if (!GAIM_BLIST_NODE_IS_GROUP(gnode)) for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) if (!GAIM_BLIST_NODE_IS_CONTACT(cnode)) for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) GaimBuddy *buddy = (GaimBuddy *)bnode; if (!all && !gaim_account_is_connected(buddy->account)) add_screenname_autocomplete_entry(store, ((GaimContact *)cnode)->alias, gaim_buddy_get_contact_alias(buddy), sets = gaim_log_get_log_sets(); g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data); g_hash_table_destroy(sets); add_completion_list(GaimGtkCompletionData *data) GaimBlistNode *gnode, *cnode, *bnode; GList *item = g_list_append(NULL, NULL); gpointer set_hash_data[2]; completion = data->completion; g_list_foreach(completion->items, (GFunc)g_free, NULL); g_completion_clear_items(completion); for (gnode = gaim_get_blist()->root; gnode != NULL; gnode = gnode->next) if (!GAIM_BLIST_NODE_IS_GROUP(gnode)) for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) if (!GAIM_BLIST_NODE_IS_CONTACT(cnode)) for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) GaimBuddy *buddy = (GaimBuddy *)bnode; if (!data->all && !gaim_account_is_connected(buddy->account)) item->data = g_strdup(buddy->name); g_completion_add_items(data->completion, item); sets = gaim_log_get_log_sets(); set_hash_data[0] = &item; set_hash_data[1] = GINT_TO_POINTER(data->all); g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data); g_hash_table_destroy(sets); g_completion_add_items(data->completion, item); screenname_autocomplete_destroyed_cb(GtkWidget *widget, gpointer data) gaim_signals_disconnect_by_handle(widget); repopulate_autocomplete(gpointer something, gpointer data) add_completion_list(data); gaim_gtk_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *accountopt, gboolean all) #ifdef NEW_STYLE_COMPLETION /* Store the displayed completion value, the screenname, the UTF-8 normalized & casefolded screenname, * the UTF-8 normalized & casefolded value for comparison, and the account. */ GtkListStore *store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); GtkEntryCompletion *completion; g_object_set_data(G_OBJECT(store), "screenname-all", GINT_TO_POINTER(all)); add_completion_list(store); /* Sort the completion list by screenname. */ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), completion = gtk_entry_completion_new(); gtk_entry_completion_set_match_func(completion, screenname_completion_match_func, NULL, NULL); data = g_new0(gpointer, 2); g_signal_connect(G_OBJECT(completion), "match-selected", G_CALLBACK(screenname_completion_match_selected_cb), data); gtk_entry_set_completion(GTK_ENTRY(entry), completion); g_object_unref(completion); gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store)); gtk_entry_completion_set_text_column(completion, 0); #else /* !NEW_STYLE_COMPLETION */ GaimGtkCompletionData *data; data = g_new0(GaimGtkCompletionData, 1); data->completion = g_completion_new(NULL); g_completion_set_compare(data->completion, g_ascii_strncasecmp); add_completion_list(data); g_signal_connect(G_OBJECT(entry), "event", G_CALLBACK(completion_entry_event), data); g_signal_connect(G_OBJECT(entry), "destroy", G_CALLBACK(destroy_completion_data), data); #endif /* !NEW_STYLE_COMPLETION */ gaim_signal_connect(gaim_connections_get_handle(), "signed-on", entry, GAIM_CALLBACK(repopulate_autocomplete), cb_data); gaim_signal_connect(gaim_connections_get_handle(), "signed-off", entry, GAIM_CALLBACK(repopulate_autocomplete), cb_data); gaim_signal_connect(gaim_accounts_get_handle(), "account-added", entry, GAIM_CALLBACK(repopulate_autocomplete), cb_data); gaim_signal_connect(gaim_accounts_get_handle(), "account-removed", entry, GAIM_CALLBACK(repopulate_autocomplete), cb_data); g_signal_connect(G_OBJECT(entry), "destroy", G_CALLBACK(screenname_autocomplete_destroyed_cb), NULL); void gaim_gtk_set_cursor(GtkWidget *widget, GdkCursorType cursor_type) g_return_if_fail(widget != NULL); if (widget->window == NULL) cursor = gdk_cursor_new(GDK_WATCH); gdk_window_set_cursor(widget->window, cursor); gdk_cursor_unref(cursor); #if GTK_CHECK_VERSION(2,4,0) gdk_display_flush(gdk_drawable_get_display(GDK_DRAWABLE(widget->window))); void gaim_gtk_clear_cursor(GtkWidget *widget) g_return_if_fail(widget != NULL); if (widget->window == NULL) gdk_window_set_cursor(widget->window, NULL);