Mercurial > pidgin
view src/gtkutils.c @ 8503:4c3ec649fd7d
[gaim-migrate @ 9239]
Another patch from marv. I think this is his hojillionth patch since
0.75 was released. They should just call him Tim Rockinbach.
"unescape outgoing status msgs"
...so that < doesn't show up as < to people
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 25 Mar 2004 06:51:51 +0000 |
parents | 9773e3f3ec7a |
children | b994b0223335 |
line wrap: on
line source
/** * @file gtkutils.h GTK+ utility functions * @ingroup gtkui * * gaim * * 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 * source distribution. * * 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 "gtkinternal.h" #ifndef _WIN32 # include <X11/Xlib.h> #else # ifdef small # undef small # endif #endif /*_WIN32*/ #ifdef USE_GTKSPELL # include <gtkspell/gtkspell.h> # ifdef _WIN32 # include "wspell.h" # endif #endif #include <gdk/gdkkeysyms.h> #include "debug.h" #include "imgstore.h" #include "notify.h" #include "prefs.h" #include "prpl.h" #include "signals.h" #include "util.h" #include "gtkconv.h" #include "gtkimhtml.h" #include "gtkutils.h" #include "ui.h" guint accels_save_timer = 0; static void url_clicked_cb(GtkWidget *w, const char *uri) { gaim_notify_uri(NULL, uri); } void gaim_setup_imhtml(GtkWidget *imhtml) { g_return_if_fail(imhtml != NULL); g_return_if_fail(GTK_IS_IMHTML(imhtml)); if (!gaim_prefs_get_bool("/gaim/gtk/conversations/show_smileys")) gtk_imhtml_show_smileys(GTK_IMHTML(imhtml), FALSE); g_signal_connect(G_OBJECT(imhtml), "url_clicked", G_CALLBACK(url_clicked_cb), NULL); smiley_themeize(imhtml); } void toggle_sensitive(GtkWidget *widget, GtkWidget *to_toggle) { gboolean sensitivity = GTK_WIDGET_IS_SENSITIVE(to_toggle); gtk_widget_set_sensitive(to_toggle, !sensitivity); } void 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, (*text != '\0')); } static void gaim_gtk_remove_tags(GaimGtkConversation *gtkconv, const char *tag) { GtkTextIter start, end, m_start, m_end; if (gtkconv == NULL || tag == NULL) return; if (!gtk_text_buffer_get_selection_bounds(gtkconv->entry_buffer, &start, &end)) return; /* FIXMEif (strstr(tag, "<FONT SIZE=")) { while ((t = strstr(t, "<FONT SIZE="))) { if (((t - s) < finish) && ((t - s) >= start)) { gtk_editable_delete_text(GTK_EDITABLE(entry), (t - s), (t - s) + strlen(tag)); g_free(s); s = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1); t = s; } else t++; } } else*/ { while (gtk_text_iter_forward_search(&start, tag, 0, &m_start, &m_end, &end)) { gtk_text_buffer_delete(gtkconv->entry_buffer, &m_start, &m_end); gtk_text_buffer_get_selection_bounds(gtkconv->entry_buffer, &start, &end); } } } void gaim_gtk_surround(GaimGtkConversation *gtkconv, const char *pre, const char *post) { GtkTextIter start, end; GtkTextMark *mark_start, *mark_end; GtkTextBuffer *entry_buffer; if (gtkconv == NULL || pre == NULL || post == NULL) return; entry_buffer = gtkconv->entry_buffer; if (gtk_text_buffer_get_selection_bounds(entry_buffer, &start, &end)) { gaim_gtk_remove_tags(gtkconv, pre); gaim_gtk_remove_tags(gtkconv, post); mark_start = gtk_text_buffer_create_mark(entry_buffer, "m1", &start, TRUE); mark_end = gtk_text_buffer_create_mark(entry_buffer, "m2", &end, FALSE); gtk_text_buffer_insert(entry_buffer, &start, pre, -1); gtk_text_buffer_get_selection_bounds(entry_buffer, &start, &end); gtk_text_buffer_insert(entry_buffer, &end, post, -1); gtk_text_buffer_get_iter_at_mark(entry_buffer, &start, mark_start); gtk_text_buffer_move_mark_by_name(entry_buffer, "selection_bound", &start); } else { gtk_text_buffer_insert(entry_buffer, &start, pre, -1); gtk_text_buffer_insert(entry_buffer, &start, post, -1); mark_start = gtk_text_buffer_get_insert(entry_buffer); gtk_text_buffer_get_iter_at_mark(entry_buffer, &start, mark_start); gtk_text_iter_backward_chars(&start, strlen(post)); gtk_text_buffer_place_cursor(entry_buffer, &start); } gtk_widget_grab_focus(gtkconv->entry); } static gboolean invert_tags(GtkTextBuffer *buffer, const char *s1, const char *s2, gboolean really) { GtkTextIter start1, start2, end1, end2; char *b1, *b2; if (gtk_text_buffer_get_selection_bounds(buffer, &start1, &end2)) { start2 = start1; end1 = end2; if (!gtk_text_iter_forward_chars(&start2, strlen(s1))) return FALSE; if (!gtk_text_iter_backward_chars(&end1, strlen(s2))) return FALSE; b1 = gtk_text_buffer_get_text(buffer, &start1, &start2, FALSE); b2 = gtk_text_buffer_get_text(buffer, &end1, &end2, FALSE); if (!g_ascii_strncasecmp(b1, s1, strlen(s1)) && !g_ascii_strncasecmp(b2, s2, strlen(s2))) { if (really) { GtkTextMark *m_end1, *m_end2; m_end1= gtk_text_buffer_create_mark(buffer, "m1", &end1, TRUE); m_end2= gtk_text_buffer_create_mark(buffer, "m2", &end2, TRUE); gtk_text_buffer_delete(buffer, &start1, &start2); gtk_text_buffer_get_iter_at_mark(buffer, &end1, m_end1); gtk_text_buffer_get_iter_at_mark(buffer, &end2, m_end2); gtk_text_buffer_delete(buffer, &end1, &end2); gtk_text_buffer_delete_mark(buffer, m_end1); gtk_text_buffer_delete_mark(buffer, m_end2); } g_free(b1); g_free(b2); return TRUE; } g_free(b1); g_free(b2); } return FALSE; } void gaim_gtk_advance_past(GaimGtkConversation *gtkconv, const char *pre, const char *post) { GtkTextIter current_pos, start, end; if (invert_tags(gtkconv->entry_buffer, pre, post, TRUE)) return; gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, ¤t_pos, gtk_text_buffer_get_insert(gtkconv->entry_buffer)); if (gtk_text_iter_forward_search(¤t_pos, post, 0, &start, &end, NULL)) gtk_text_buffer_place_cursor(gtkconv->entry_buffer, &end); else gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, post, -1); gtk_widget_grab_focus(gtkconv->entry); } void gaim_gtk_set_font_face(GaimGtkConversation *gtkconv, const char *font) { if (gtkconv == NULL || font == NULL) return; strncpy(gtkconv->fontface, (font && *font ? font : DEFAULT_FONT_FACE), sizeof(gtkconv->fontface)); gtkconv->has_font = TRUE; gtk_imhtml_toggle_fontface(GTK_IMHTML(gtkconv->entry), gtkconv->fontface); gtk_widget_grab_focus(gtkconv->entry); } static int des_save_icon(GtkObject *obj, GdkEvent *e, GaimGtkConversation *gtkconv) { gtk_widget_destroy(gtkconv->u.im->save_icon); gtkconv->u.im->save_icon = NULL; return TRUE; } static void do_save_icon(GtkObject *obj, GaimConversation *c) { GaimGtkConversation *gtkconv; FILE *file; const char *f; GaimBuddyIcon *icon; size_t len; const void *data; gtkconv = GAIM_GTK_CONVERSATION(c); f = gtk_file_selection_get_filename( GTK_FILE_SELECTION(gtkconv->u.im->save_icon)); if (gaim_gtk_check_if_dir(f, GTK_FILE_SELECTION(gtkconv->u.im->save_icon))) return; icon = gaim_conv_im_get_icon(GAIM_CONV_IM(c)); data = gaim_buddy_icon_get_data(icon, &len); if ((len > 0) && (data != NULL) && (file = fopen(f, "wb")) != NULL) { fwrite(data, 1, len, file); fclose(file); } else { gaim_notify_error(NULL, NULL, _("Can't save icon file to disk."), NULL); } gtk_widget_destroy(gtkconv->u.im->save_icon); gtkconv->u.im->save_icon = NULL; } static void cancel_save_icon(GtkObject *obj, GaimGtkConversation *gtkconv) { gtk_widget_destroy(gtkconv->u.im->save_icon); gtkconv->u.im->save_icon = NULL; } void gaim_gtk_save_icon_dialog(GtkObject *obj, GaimConversation *conv) { GaimGtkConversation *gtkconv; char buf[BUF_LEN]; if (conv == NULL || gaim_conversation_get_type(conv) != GAIM_CONV_IM) return; if (!GAIM_IS_GTK_CONVERSATION(conv)) return; gtkconv = GAIM_GTK_CONVERSATION(conv); if (gtkconv->u.im->save_icon != NULL) { gdk_window_raise(gtkconv->u.im->save_icon->window); return; } gtkconv->u.im->save_icon = gtk_file_selection_new(_("Save Icon")); gtk_file_selection_hide_fileop_buttons( GTK_FILE_SELECTION(gtkconv->u.im->save_icon)); g_snprintf(buf, BUF_LEN - 1, "%s" G_DIR_SEPARATOR_S "%s.icon", gaim_home_dir(), gaim_conversation_get_name(conv)); gtk_file_selection_set_filename( GTK_FILE_SELECTION(gtkconv->u.im->save_icon), buf); g_signal_connect(G_OBJECT(gtkconv->u.im->save_icon), "delete_event", G_CALLBACK(des_save_icon), gtkconv); g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(gtkconv->u.im->save_icon)->ok_button), "clicked", G_CALLBACK(do_save_icon), conv); g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(gtkconv->u.im->save_icon)->cancel_button), "clicked", G_CALLBACK(cancel_save_icon), gtkconv); gtk_widget_show(gtkconv->u.im->save_icon); } int gaim_gtk_get_dispstyle(GaimConversationType type) { int dispstyle = 2; int value; if (type == GAIM_CONV_CHAT) { value = gaim_prefs_get_int("/gaim/gtk/conversations/chat/button_type"); switch (value) { case GAIM_BUTTON_TEXT: dispstyle = 1; break; case GAIM_BUTTON_IMAGE: dispstyle = 0; break; default: dispstyle = 2; break; /* both/neither */ } } else if (type == GAIM_CONV_IM) { value = gaim_prefs_get_int("/gaim/gtk/conversations/im/button_type"); switch (value) { case GAIM_BUTTON_TEXT: dispstyle = 1; break; case GAIM_BUTTON_IMAGE: dispstyle = 0; break; default: dispstyle = 2; break; /* both/neither */ } } return dispstyle; } GtkWidget * gaim_gtk_change_text(const char *text, GtkWidget *button, const char *stock, GaimConversationType type) { int dispstyle = gaim_gtk_get_dispstyle(type); if (button != NULL) gtk_widget_destroy(button); button = gaim_pixbuf_button_from_stock((dispstyle == 0 ? NULL : text), (dispstyle == 1 ? NULL : stock), GAIM_BUTTON_VERTICAL); gtk_widget_show(button); return button; } void gaim_gtk_toggle_sensitive(GtkWidget *widget, GtkWidget *to_toggle) { gboolean sensitivity; if (to_toggle == NULL) return; sensitivity = GTK_WIDGET_IS_SENSITIVE(to_toggle); gtk_widget_set_sensitive(to_toggle, !sensitivity); } void gtk_toggle_sensitive_array(GtkWidget *w, GPtrArray *data) { gboolean sensitivity; gpointer element; int i; for (i=0; i < data->len; i++) { element = g_ptr_array_index(data,i); if (element == NULL) continue; sensitivity = GTK_WIDGET_IS_SENSITIVE(element); gtk_widget_set_sensitive(element, !sensitivity); } } void gaim_separator(GtkWidget *menu) { GtkWidget *menuitem; 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) { GtkWidget *menuitem; GtkWidget *label; menuitem = gtk_menu_item_new(); if (menu) 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); gtk_widget_show(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); return menuitem; } GtkWidget *gaim_new_check_item(GtkWidget *menu, const char *str, GtkSignalFunc sf, gpointer data, gboolean checked) { GtkWidget *menuitem; menuitem = gtk_check_menu_item_new_with_mnemonic(str); if (menu) gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), checked); if (sf) g_signal_connect(G_OBJECT(menuitem), "activate", sf, data); gtk_widget_show_all(menuitem); return menuitem; } GtkWidget * 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); return button; } GtkWidget * gaim_pixbuf_button_from_stock(const char *text, const char *icon, GaimButtonOrientation style) { GtkWidget *button, *image, *label, *bbox, *ibox, *lbox; button = gtk_button_new(); if (style == GAIM_BUTTON_HORIZONTAL) { bbox = gtk_hbox_new(FALSE, 5); ibox = gtk_hbox_new(FALSE, 0); lbox = gtk_hbox_new(FALSE, 0); } else { bbox = gtk_vbox_new(FALSE, 5); 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); gtk_box_pack_start_defaults(GTK_BOX(bbox), lbox); if (icon) { image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_BUTTON); gtk_box_pack_end(GTK_BOX(ibox), image, FALSE, FALSE, 0); } if (text) { 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, FALSE, 0); gaim_set_accessible_label (button, label); } gtk_widget_show_all(bbox); return button; } 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) { GtkWidget *menuitem; /* GtkWidget *hbox; GtkWidget *label; */ GtkWidget *image; if (icon == NULL) menuitem = gtk_menu_item_new_with_mnemonic(str); else menuitem = gtk_image_menu_item_new_with_mnemonic(str); if (menu) gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); if (sf) g_signal_connect(G_OBJECT(menuitem), "activate", sf, data); if (icon != NULL) { 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 if (mod) { label = gtk_label_new(mod); gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 2); gtk_widget_show(label); } */ /* if (accel_key) { gtk_widget_add_accelerator(menuitem, "activate", accel, accel_key, accel_mods, GTK_ACCEL_LOCKED); } */ gtk_widget_show_all(menuitem); return menuitem; } GtkWidget * gaim_gtk_make_frame(GtkWidget *parent, const char *title) { GtkWidget *vbox, *label, *hbox; char labeltitle[256]; vbox = gtk_vbox_new(FALSE, 6); gtk_box_pack_start(GTK_BOX(parent), vbox, FALSE, FALSE, 0); gtk_widget_show(vbox); label = gtk_label_new(NULL); g_snprintf(labeltitle, sizeof(labeltitle), "<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); gtk_widget_show(label); gaim_set_accessible_label (vbox, label); hbox = gtk_hbox_new(FALSE, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); label = gtk_label_new(" "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); vbox = gtk_vbox_new(FALSE, 6); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); gtk_widget_show(vbox); return vbox; } static void protocol_menu_cb(GtkWidget *optmenu, GCallback cb) { GtkWidget *menu; GtkWidget *item; const char *protocol; gpointer user_data; 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")); if (cb != NULL) ((void (*)(GtkWidget *, const char *, gpointer))cb)(item, protocol, user_data); } GtkWidget * gaim_gtk_protocol_option_menu_new(const char *id, GCallback cb, gpointer user_data) { GaimPluginProtocolInfo *prpl_info; GaimPlugin *plugin; GtkWidget *hbox; GtkWidget *label; GtkWidget *optmenu; GtkWidget *menu; GtkWidget *item; GtkWidget *image; GdkPixbuf *pixbuf; GdkPixbuf *scale; GList *p; GtkSizeGroup *sg; char *filename; const char *proto_name; char buf[256]; 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); menu = gtk_menu_new(); gtk_widget_show(menu); sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); for (p = gaim_plugins_get_protocols(), i = 0; p != NULL; p = p->next, i++) { plugin = (GaimPlugin *)p->data; prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); /* Create the item. */ item = gtk_menu_item_new(); /* Create the hbox. */ hbox = gtk_hbox_new(FALSE, 4); gtk_container_add(GTK_CONTAINER(item), hbox); gtk_widget_show(hbox); /* Load the image. */ 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", "default", buf, NULL); pixbuf = gdk_pixbuf_new_from_file(filename, NULL); g_free(filename); if (pixbuf != NULL) { /* Scale and insert the image */ scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); image = gtk_image_new_from_pixbuf(scale); g_object_unref(G_OBJECT(pixbuf)); g_object_unref(G_OBJECT(scale)); } else image = gtk_image_new(); gtk_size_group_add_widget(sg, image); gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); gtk_widget_show(image); /* Create the label. */ 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); gtk_widget_show(label); g_object_set_data(G_OBJECT(item), "protocol", plugin->info->id); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); gaim_set_accessible_label (item, label); if (id != NULL && !strcmp(plugin->info->id, id)) selected_index = i; } 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); g_object_unref(sg); return optmenu; } static void account_menu_cb(GtkWidget *optmenu, GCallback cb) { GtkWidget *menu; GtkWidget *item; GaimAccount *account; gpointer user_data; 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"); if (cb != NULL) ((void (*)(GtkWidget *, GaimAccount *, gpointer))cb)(item, account, user_data); } static void create_account_menu(GtkWidget *optmenu, GaimAccount *default_account, GaimFilterAccountFunc filter_func, gboolean show_all) { GaimAccount *account; GtkWidget *menu; GtkWidget *item; GtkWidget *image; GtkWidget *hbox; GtkWidget *label; GdkPixbuf *pixbuf; GdkPixbuf *scale; GList *list; GList *p; GtkSizeGroup *sg; char *filename; const char *proto_name; char buf[256]; int i, selected_index = -1; if (show_all) list = gaim_accounts_get_all(); else list = gaim_connections_get_all(); menu = gtk_menu_new(); gtk_widget_show(menu); sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); for (p = list, i = 0; p != NULL; p = p->next, i++) { GaimPluginProtocolInfo *prpl_info = NULL; GaimPlugin *plugin; if (show_all) account = (GaimAccount *)p->data; else { GaimConnection *gc = (GaimConnection *)p->data; account = gaim_connection_get_account(gc); } if (filter_func && !filter_func(account)) { i--; continue; } plugin = gaim_find_prpl(gaim_account_get_protocol_id(account)); if (plugin != NULL) prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); /* Create the item. */ item = gtk_menu_item_new(); /* Create the hbox. */ hbox = gtk_hbox_new(FALSE, 4); gtk_container_add(GTK_CONTAINER(item), hbox); gtk_widget_show(hbox); /* Load the image. */ if (prpl_info != NULL) { 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", "default", buf, NULL); pixbuf = gdk_pixbuf_new_from_file(filename, NULL); g_free(filename); if (pixbuf != NULL) { /* Scale and insert the image */ scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); if (!gaim_account_is_connected(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)); } else image = gtk_image_new(); } else image = gtk_image_new(); gtk_size_group_add_widget(sg, image); gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); gtk_widget_show(image); g_snprintf(buf, sizeof(buf), "%s (%s)", gaim_account_get_username(account), (plugin != NULL) ? plugin->info->name : _("Unknown")); /* Create the label. */ 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); gtk_widget_show(label); g_object_set_data(G_OBJECT(item), "account", account); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); gaim_set_accessible_label (item, label); if (default_account != NULL && account == default_account) selected_index = i; } g_object_unref(sg); 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); } static void regenerate_account_menu(GtkWidget *optmenu) { GtkWidget *menu; GtkWidget *item; gboolean show_all; GaimAccount *account; 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), "show_all")); filter_func = g_object_get_data(G_OBJECT(optmenu), "filter_func"); gtk_option_menu_remove_menu(GTK_OPTION_MENU(optmenu)); create_account_menu(optmenu, account, filter_func, show_all); } static void account_menu_sign_on_off_cb(GaimConnection *gc, GtkWidget *optmenu) { regenerate_account_menu(optmenu); } static void account_menu_added_removed_cb(GaimAccount *account, GtkWidget *optmenu) { regenerate_account_menu(optmenu); } static gboolean account_menu_destroyed_cb(GtkWidget *optmenu, GdkEvent *event, void *user_data) { gaim_signals_disconnect_by_handle(optmenu); return FALSE; } GtkWidget * gaim_gtk_account_option_menu_new(GaimAccount *default_account, gboolean show_all, GCallback cb, GaimFilterAccountFunc filter_func, gpointer user_data) { GtkWidget *optmenu; /* 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), optmenu); gaim_signal_connect(gaim_connections_get_handle(), "signed-off", optmenu, GAIM_CALLBACK(account_menu_sign_on_off_cb), optmenu); gaim_signal_connect(gaim_accounts_get_handle(), "account-added", optmenu, GAIM_CALLBACK(account_menu_added_removed_cb), optmenu); gaim_signal_connect(gaim_accounts_get_handle(), "account-removed", optmenu, GAIM_CALLBACK(account_menu_added_removed_cb), optmenu); /* Set some data. */ 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", 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); return optmenu; } gboolean gaim_gtk_check_if_dir(const char *path, GtkFileSelection *filesel) { char *dirname; if (g_file_test(path, G_FILE_TEST_IS_DIR)) { /* append a / if needed */ if (path[strlen(path) - 1] != '/') { dirname = g_strconcat(path, "/", NULL); } else { dirname = g_strdup(path); } gtk_file_selection_set_filename(filesel, dirname); g_free(dirname); return TRUE; } return FALSE; } char *stylize(const gchar *text, int length) { gchar *buf; char *tmp = g_malloc(length); buf = g_malloc(length); g_snprintf(buf, length, "%s", text); if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_bold")) { g_snprintf(tmp, length, "<B>%s</B>", buf); strcpy(buf, tmp); } if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_italic")) { g_snprintf(tmp, length, "<I>%s</I>", buf); strcpy(buf, tmp); } if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_underline")) { g_snprintf(tmp, length, "<U>%s</U>", buf); strcpy(buf, tmp); } if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_font")) { const char *fontface; fontface = gaim_prefs_get_string("/gaim/gtk/conversations/font_face"); g_snprintf(tmp, length, "<FONT FACE=\"%s\">%s</FONT>", fontface, buf); strcpy(buf, tmp); } if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_size")) { int fontsize = gaim_prefs_get_int("/gaim/gtk/conversations/font_size"); g_snprintf(tmp, length, "<FONT SIZE=\"%d\">%s</FONT>", fontsize, buf); strcpy(buf, tmp); } if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_fgcolor")) { GdkColor fgcolor; gdk_color_parse( gaim_prefs_get_string("/gaim/gtk/conversations/fgcolor"), &fgcolor); g_snprintf(tmp, length, "<FONT COLOR=\"#%02X%02X%02X\">%s</FONT>", fgcolor.red/256, fgcolor.green/256, fgcolor.blue/256, buf); strcpy(buf, tmp); } if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_bgcolor")) { GdkColor bgcolor; gdk_color_parse( gaim_prefs_get_string("/gaim/gtk/conversations/bgcolor"), &bgcolor); g_snprintf(tmp, length, "<BODY BGCOLOR=\"#%02X%02X%02X\">%s</BODY>", bgcolor.red/256, bgcolor.green/256, bgcolor.blue/256, buf); strcpy(buf, tmp); } g_free(tmp); return buf; } void gaim_gtk_find_images(const char *message, GSList **list) { GData *attribs; const char *tmp, *start, *end; g_return_if_fail(message != NULL); g_return_if_fail( list != NULL); tmp = message; while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) { GaimStoredImage *image = NULL; GdkPixbufLoader *loader = NULL; GdkPixbuf *pixbuf = NULL; GError *error = NULL; char *id = g_datalist_get_data(&attribs, "id"); tmp = end + 1; if (id) image = gaim_imgstore_get(atoi(id)); g_datalist_clear(&attribs); if (!image) { *list = g_slist_append(*list, NULL); continue; } loader = gdk_pixbuf_loader_new(); if (gdk_pixbuf_loader_write(loader, image->data, image->size, &error) && (pixbuf = gdk_pixbuf_loader_get_pixbuf(loader))) { if (image->filename) g_object_set_data_full(G_OBJECT(pixbuf), "filename", g_strdup(image->filename), g_free); g_object_ref(G_OBJECT(pixbuf)); *list = g_slist_append(*list, pixbuf); } else { if (error) { gaim_debug(GAIM_DEBUG_ERROR, "gtkutils", "Failed to make pixbuf from image store: %s\n", error->message); g_error_free(error); } else { gaim_debug(GAIM_DEBUG_ERROR, "gtkutils", "Failed to make pixbuf from image store: unknown reason\n"); } *list = g_slist_append(*list, NULL); } gdk_pixbuf_loader_close(loader, NULL); } } void gaim_gtk_setup_gtkspell(GtkTextView *textview) { #ifdef USE_GTKSPELL GError *error = NULL; char *locale = NULL; 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", error->message); g_error_free(error); } #endif /* USE_GTKSPELL */ } void gaim_gtk_save_accels_cb(GtkAccelGroup *accel_group, guint arg1, GdkModifierType arg2, GClosure *arg3, gpointer data) { gaim_debug(GAIM_DEBUG_MISC, "accels", "accel changed, scheduling save.\n"); if (!accels_save_timer) accels_save_timer = g_timeout_add(5000, gaim_gtk_save_accels, NULL); } gboolean gaim_gtk_save_accels(gpointer data) { char *filename = NULL; filename = g_build_filename(gaim_user_dir(), G_DIR_SEPARATOR_S, "accels", NULL); gaim_debug(GAIM_DEBUG_MISC, "accels", "saving accels to %s\n", filename); gtk_accel_map_save(filename); g_free(filename); accels_save_timer = 0; return FALSE; } void gaim_gtk_load_accels(gpointer data) { char *filename = NULL; filename = g_build_filename(gaim_user_dir(), G_DIR_SEPARATOR_S, "accels", NULL); gtk_accel_map_load(filename); g_free(filename); } gboolean gaim_gtk_parse_x_im_contact(const char *msg, gboolean all_accounts, GaimAccount **ret_account, char **ret_protocol, char **ret_username, char **ret_alias) { char *protocol = NULL; char *username = NULL; char *alias = NULL; char *str; char *c, *s; gboolean valid; 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); s = str = g_strdup(msg); while (*s != '\r' && *s != '\n' && *s != '\0') { char *key, *value; key = s; /* Grab the key */ while (*s != '\r' && *s != '\n' && *s != '\0' && *s != ' ') s++; if (*s == '\r') s++; if (*s == '\n') { s++; continue; } if (*s != '\0') *s++ = '\0'; /* Clear past any whitespace */ while (*s != '\0' && *s == ' ') s++; /* Now let's grab until the end of the line. */ value = s; while (*s != '\r' && *s != '\n' && *s != '\0') s++; 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:")) alias = g_strdup(value); } } if (username != NULL && protocol != NULL) { valid = TRUE; *ret_username = username; *ret_protocol = protocol; if (ret_alias != NULL) *ret_alias = alias; /* Check for a compatible account. */ if (ret_account != NULL) { GList *list; GaimAccount *account = NULL; GList *l; const char *protoname; if (all_accounts) list = gaim_accounts_get_all(); else list = gaim_connections_get_all(); for (l = list; l != NULL; l = l->next) { GaimConnection *gc; GaimPluginProtocolInfo *prpl_info = NULL; GaimPlugin *plugin; if (all_accounts) { account = (GaimAccount *)l->data; plugin = gaim_plugins_find_with_id( gaim_account_get_protocol_id(account)); if (plugin == NULL) { account = NULL; continue; } prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); } else { 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)) break; account = NULL; } /* Special case for AIM and ICQ */ if (account == NULL && (!strcmp(protocol, "aim") || !strcmp(protocol, "icq"))) { for (l = list; l != NULL; l = l->next) { GaimConnection *gc; GaimPluginProtocolInfo *prpl_info = NULL; GaimPlugin *plugin; if (all_accounts) { account = (GaimAccount *)l->data; plugin = gaim_plugins_find_with_id( gaim_account_get_protocol_id(account)); if (plugin == NULL) { account = NULL; continue; } prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); } else { 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")) break; account = NULL; } } *ret_account = account; } } else { valid = FALSE; if (username != NULL) g_free(username); if (protocol != NULL) g_free(protocol); if (alias != NULL) g_free(alias); } g_free(str); return valid; } void gaim_set_accessible_label (GtkWidget *w, GtkWidget *l) { AtkObject *acc, *label; AtkObject *rel_obj[1]; AtkRelationSet *set; AtkRelation *relation; const gchar *label_text; 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); if (!existing_name) { label_text = gtk_label_get_text (GTK_LABEL(l)); if (label_text) atk_object_set_name (acc, label_text); } /* Create the labeled-by relation */ set = atk_object_ref_relation_set (acc); rel_obj[0] = label; 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); rel_obj[0] = acc; 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) static void gaim_gtk_menu_position_func(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) { GtkWidget *widget; GtkRequisition requisition; GdkScreen *screen; GdkRectangle monitor; gint monitor_num; gint space_left, space_right, space_above, space_below; gint needed_width; gint needed_height; gint xthickness; gint ythickness; gboolean rtl; 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); push_in = FALSE; /* * The placement of popup menus horizontally works like this (with * RTL in parentheses) * * - 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)) { /* position left */ *x = *x + xthickness - requisition.width + 1; } else { /* position right */ *x = *x - xthickness; } /* 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) { /* left justify */ *x = monitor.x; } else { /* right justify */ *x = monitor.x + monitor.width - requisition.width; } } else /* menu is simply too big for the monitor */ { if (rtl) { /* right justify */ *x = monitor.x + monitor.width - requisition.width; } else { /* left justify */ *x = monitor.x; } } /* 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; else *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; else *y = monitor.y; } else { *y = monitor.y; } } #endif void gaim_gtk_treeview_popup_menu_position_func(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) { GtkWidget *widget = GTK_WIDGET(data); GtkTreeView *tv = GTK_TREE_VIEW(data); GtkTreePath *path; GtkTreeViewColumn *col; GdkRectangle rect; 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); *x += rect.x+rect.width; *y += rect.y+rect.height+ythickness; #if GTK_CHECK_VERSION(2,2,0) gaim_gtk_menu_position_func (menu, x, y, push_in, data); #endif }