gaim/gaim

This is a temporary fix for the rate limit problems.
oldstatus
2005-10-15, Mark Doliner
649cffd7a416
This is a temporary fix for the rate limit problems.

Cause: Gaim uses the SNAC 0x0004/0x0015 to fetch the away message
of each user in your buddy list. Previously this SNAC was in a
"rate class" that had an extremely lenient rate limit. Gaim would
fetch a new away message at most every 1.3 seconds. AOL recently
moved this SNAC to be in the same rate class as most of the other
SNACs (send IM, add buddy, connect to chat room, etc.) My temporary
fix is to bump this delay to 10 seconds.

We copied this functionality from iChat, and it looks like they
might actually be having the same problem (but I didn't even take
a packet capture to verify). If this is the case, AOL is really
stupid.

Possibly better fixes:
* Use the rate information to delay packets so that we never hit
a rate limit
* There may be a different SNAC that is now more leniently rate
limited. Possibly 0x0002/0x0005 or 0x0004/0x0006.
/**
* @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 "internal.h"
#include "gtkgaim.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 "gtkdialogs.h"
#include "gtkimhtml.h"
#include "gtkutils.h"
guint accels_save_timer = 0;
static gboolean
url_clicked_idle_cb(gpointer data)
{
gaim_notify_uri(NULL, data);
g_free(data);
return FALSE;
}
static void
url_clicked_cb(GtkWidget *w, const char *uri)
{
g_idle_add(url_clicked_idle_cb, g_strdup(uri));
}
GtkIMHtmlFuncs gtkimhtml_cbs = {
(GtkIMHtmlGetImageFunc)gaim_imgstore_get,
(GtkIMHtmlGetImageDataFunc)gaim_imgstore_get_data,
(GtkIMHtmlGetImageSizeFunc)gaim_imgstore_get_size,
(GtkIMHtmlGetImageFilenameFunc)gaim_imgstore_get_filename,
gaim_imgstore_ref,
gaim_imgstore_unref,
};
void
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);
smiley_themeize(imhtml);
gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), &gtkimhtml_cbs);
}
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'));
}
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_gtk_toggle_showhide(GtkWidget *widget, GtkWidget *to_toggle)
{
if (to_toggle == NULL)
return;
if (GTK_WIDGET_VISIBLE(to_toggle))
gtk_widget_hide(to_toggle);
else
gtk_widget_show(to_toggle);
}
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 = NULL;
button = gtk_button_new();
if (style == GAIM_BUTTON_HORIZONTAL) {
bbox = gtk_hbox_new(FALSE, 0);
ibox = gtk_hbox_new(FALSE, 0);
if (text)
lbox = gtk_hbox_new(FALSE, 0);
} else {
bbox = gtk_vbox_new(FALSE, 0);
ibox = gtk_vbox_new(FALSE, 0);
if (text)
lbox = gtk_vbox_new(FALSE, 0);
}
gtk_container_add(GTK_CONTAINER(button), bbox);
if (icon) {
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);
}
if (text) {
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);
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;
}
GaimAccount *
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");
}
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);
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));
} else {
g_snprintf(buf, sizeof(buf), "%s (%s)",
gaim_account_get_username(account),
gaim_account_get_protocol_name(account));
}
/* 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;
}
void
gaim_gtk_account_option_menu_set_selected(GtkWidget *optmenu, GaimAccount *account)
{
GtkWidget *menu;
GtkWidget *item;
gboolean show_all;
GaimAccount *curaccount;
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)
return;
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);
}
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] != G_DIR_SEPARATOR) {
dirname = g_strconcat(path, G_DIR_SEPARATOR_S, 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_formatting")) {
const char *font;
GdkColor fg_color, bg_color;
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);
}
font = gaim_prefs_get_string("/gaim/gtk/conversations/font_face");
if (strcmp(font, "") != 0) {
g_snprintf(tmp, length, "<FONT FACE=\"%s\">%s</FONT>", font, buf);
strcpy(buf, tmp);
}
g_snprintf(tmp, length, "<FONT SIZE=\"%d\">%s</FONT>",
gaim_prefs_get_int("/gaim/gtk/conversations/font_size"), buf);
strcpy(buf, tmp);
if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/fgcolor"), "") != 0)
{
gdk_color_parse(gaim_prefs_get_string("/gaim/gtk/conversations/fgcolor"),
&fg_color);
g_snprintf(tmp, length, "<FONT COLOR=\"#%02X%02X%02X\">%s</FONT>",
fg_color.red/256, fg_color.green/256,
fg_color.blue/256, buf);
strcpy(buf, tmp);
}
if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/bgcolor"), "") != 0)
{
gdk_color_parse(gaim_prefs_get_string("/gaim/gtk/conversations/bgcolor"),
&bg_color);
g_snprintf(tmp, length, "<BODY BGCOLOR=\"#%02X%02X%02X\">%s</BODY>",
bg_color.red/256, bg_color.green/256,
bg_color.blue/256, buf);
strcpy(buf, tmp);
}
}
g_free(tmp);
return buf;
}
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
}
gboolean
gaim_running_gnome(void)
{
if ((g_getenv("GNOME_DESKTOP_SESSION_ID") != NULL) &&
(g_find_program_in_path("gnome-open") != NULL))
{
return TRUE;
}
return FALSE;
}