comparison gtk/gtkutils.c @ 14191:009db0b357b5

This is a hand-crafted commit to migrate across subversion revisions 16854:16861, due to some vagaries of the way the original renames were done. Witness that monotone can do in one revision what svn had to spread across several.
author Ethan Blanton <elb@pidgin.im>
date Sat, 16 Dec 2006 04:59:55 +0000
parents
children c18bdf510325
comparison
equal deleted inserted replaced
14190:366be2ce35a7 14191:009db0b357b5
1 /**
2 * @file gtkutils.h GTK+ utility functions
3 * @ingroup gtkui
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25 #include "internal.h"
26 #include "gtkgaim.h"
27
28 #ifndef _WIN32
29 # include <X11/Xlib.h>
30 #else
31 # ifdef small
32 # undef small
33 # endif
34 #endif /*_WIN32*/
35
36 #ifdef USE_GTKSPELL
37 # include <gtkspell/gtkspell.h>
38 # ifdef _WIN32
39 # include "wspell.h"
40 # endif
41 #endif
42
43 #include <gdk/gdkkeysyms.h>
44
45 #include "conversation.h"
46 #include "debug.h"
47 #include "desktopitem.h"
48 #include "imgstore.h"
49 #include "notify.h"
50 #include "prefs.h"
51 #include "prpl.h"
52 #include "request.h"
53 #include "signals.h"
54 #include "util.h"
55
56 #include "gtkconv.h"
57 #include "gtkdialogs.h"
58 #include "gtkimhtml.h"
59 #include "gtkimhtmltoolbar.h"
60 #include "gaimstock.h"
61 #include "gtkthemes.h"
62 #include "gtkutils.h"
63
64 static guint accels_save_timer = 0;
65
66 static gboolean
67 url_clicked_idle_cb(gpointer data)
68 {
69 gaim_notify_uri(NULL, data);
70 g_free(data);
71 return FALSE;
72 }
73
74 static void
75 url_clicked_cb(GtkWidget *w, const char *uri)
76 {
77 g_idle_add(url_clicked_idle_cb, g_strdup(uri));
78 }
79
80 static GtkIMHtmlFuncs gtkimhtml_cbs = {
81 (GtkIMHtmlGetImageFunc)gaim_imgstore_get,
82 (GtkIMHtmlGetImageDataFunc)gaim_imgstore_get_data,
83 (GtkIMHtmlGetImageSizeFunc)gaim_imgstore_get_size,
84 (GtkIMHtmlGetImageFilenameFunc)gaim_imgstore_get_filename,
85 gaim_imgstore_ref,
86 gaim_imgstore_unref,
87 };
88
89 void
90 gaim_setup_imhtml(GtkWidget *imhtml)
91 {
92 g_return_if_fail(imhtml != NULL);
93 g_return_if_fail(GTK_IS_IMHTML(imhtml));
94
95 g_signal_connect(G_OBJECT(imhtml), "url_clicked",
96 G_CALLBACK(url_clicked_cb), NULL);
97
98 gaim_gtkthemes_smiley_themeize(imhtml);
99
100 gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), &gtkimhtml_cbs);
101 }
102
103 GtkWidget *
104 gaim_gtk_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret)
105 {
106 GtkWidget *frame;
107 GtkWidget *imhtml;
108 GtkWidget *sep;
109 GtkWidget *sw;
110 GtkWidget *toolbar = NULL;
111 GtkWidget *vbox;
112
113 frame = gtk_frame_new(NULL);
114 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
115
116 vbox = gtk_vbox_new(FALSE, 0);
117 gtk_container_add(GTK_CONTAINER(frame), vbox);
118 gtk_widget_show(vbox);
119
120 if (editable) {
121 toolbar = gtk_imhtmltoolbar_new();
122 gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
123 gtk_widget_show(toolbar);
124
125 sep = gtk_hseparator_new();
126 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
127 gtk_widget_show(sep);
128 }
129
130 sw = gtk_scrolled_window_new(NULL, NULL);
131 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
132 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
133 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
134 gtk_widget_show(sw);
135
136 imhtml = gtk_imhtml_new(NULL, NULL);
137 gtk_imhtml_set_editable(GTK_IMHTML(imhtml), editable);
138 gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml), GTK_IMHTML_ALL ^ GTK_IMHTML_IMAGE);
139 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(imhtml), GTK_WRAP_WORD_CHAR);
140 #ifdef USE_GTKSPELL
141 if (editable && gaim_prefs_get_bool("/gaim/gtk/conversations/spellcheck"))
142 gaim_gtk_setup_gtkspell(GTK_TEXT_VIEW(imhtml));
143 #endif
144 gtk_widget_show(imhtml);
145
146 if (editable) {
147 gtk_imhtmltoolbar_attach(GTK_IMHTMLTOOLBAR(toolbar), imhtml);
148 gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(toolbar), "default");
149 }
150 gaim_setup_imhtml(imhtml);
151
152 gtk_container_add(GTK_CONTAINER(sw), imhtml);
153
154 if (imhtml_ret != NULL)
155 *imhtml_ret = imhtml;
156
157 if (editable && (toolbar_ret != NULL))
158 *toolbar_ret = toolbar;
159
160 if (sw_ret != NULL)
161 *sw_ret = sw;
162
163 return frame;
164 }
165
166 void
167 gaim_gtk_set_sensitive_if_input(GtkWidget *entry, GtkWidget *dialog)
168 {
169 const char *text = gtk_entry_get_text(GTK_ENTRY(entry));
170 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK,
171 (*text != '\0'));
172 }
173
174 void
175 gaim_gtk_toggle_sensitive(GtkWidget *widget, GtkWidget *to_toggle)
176 {
177 gboolean sensitivity;
178
179 if (to_toggle == NULL)
180 return;
181
182 sensitivity = GTK_WIDGET_IS_SENSITIVE(to_toggle);
183
184 gtk_widget_set_sensitive(to_toggle, !sensitivity);
185 }
186
187 void
188 gaim_gtk_toggle_sensitive_array(GtkWidget *w, GPtrArray *data)
189 {
190 gboolean sensitivity;
191 gpointer element;
192 int i;
193
194 for (i=0; i < data->len; i++) {
195 element = g_ptr_array_index(data,i);
196 if (element == NULL)
197 continue;
198
199 sensitivity = GTK_WIDGET_IS_SENSITIVE(element);
200
201 gtk_widget_set_sensitive(element, !sensitivity);
202 }
203 }
204
205 void
206 gaim_gtk_toggle_showhide(GtkWidget *widget, GtkWidget *to_toggle)
207 {
208 if (to_toggle == NULL)
209 return;
210
211 if (GTK_WIDGET_VISIBLE(to_toggle))
212 gtk_widget_hide(to_toggle);
213 else
214 gtk_widget_show(to_toggle);
215 }
216
217 void gaim_separator(GtkWidget *menu)
218 {
219 GtkWidget *menuitem;
220
221 menuitem = gtk_separator_menu_item_new();
222 gtk_widget_show(menuitem);
223 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
224 }
225
226 GtkWidget *gaim_new_item(GtkWidget *menu, const char *str)
227 {
228 GtkWidget *menuitem;
229 GtkWidget *label;
230
231 menuitem = gtk_menu_item_new();
232 if (menu)
233 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
234 gtk_widget_show(menuitem);
235
236 label = gtk_label_new(str);
237 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
238 gtk_label_set_pattern(GTK_LABEL(label), "_");
239 gtk_container_add(GTK_CONTAINER(menuitem), label);
240 gtk_widget_show(label);
241 /* FIXME: Go back and fix this
242 gtk_widget_add_accelerator(menuitem, "activate", accel, str[0],
243 GDK_MOD1_MASK, GTK_ACCEL_LOCKED);
244 */
245 gaim_set_accessible_label (menuitem, label);
246 return menuitem;
247 }
248
249 GtkWidget *gaim_new_check_item(GtkWidget *menu, const char *str,
250 GtkSignalFunc sf, gpointer data, gboolean checked)
251 {
252 GtkWidget *menuitem;
253 menuitem = gtk_check_menu_item_new_with_mnemonic(str);
254
255 if (menu)
256 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
257
258 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), checked);
259
260 if (sf)
261 g_signal_connect(G_OBJECT(menuitem), "activate", sf, data);
262
263 gtk_widget_show_all(menuitem);
264
265 return menuitem;
266 }
267
268 GtkWidget *
269 gaim_pixbuf_toolbar_button_from_stock(const char *icon)
270 {
271 GtkWidget *button, *image, *bbox;
272
273 button = gtk_toggle_button_new();
274 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
275
276 bbox = gtk_vbox_new(FALSE, 0);
277
278 gtk_container_add (GTK_CONTAINER(button), bbox);
279
280 image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU);
281 gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
282
283 gtk_widget_show_all(bbox);
284
285 return button;
286 }
287
288 GtkWidget *
289 gaim_pixbuf_button_from_stock(const char *text, const char *icon,
290 GaimButtonOrientation style)
291 {
292 GtkWidget *button, *image, *label, *bbox, *ibox, *lbox = NULL;
293
294 button = gtk_button_new();
295
296 if (style == GAIM_BUTTON_HORIZONTAL) {
297 bbox = gtk_hbox_new(FALSE, 0);
298 ibox = gtk_hbox_new(FALSE, 0);
299 if (text)
300 lbox = gtk_hbox_new(FALSE, 0);
301 } else {
302 bbox = gtk_vbox_new(FALSE, 0);
303 ibox = gtk_vbox_new(FALSE, 0);
304 if (text)
305 lbox = gtk_vbox_new(FALSE, 0);
306 }
307
308 gtk_container_add(GTK_CONTAINER(button), bbox);
309
310 if (icon) {
311 gtk_box_pack_start_defaults(GTK_BOX(bbox), ibox);
312 image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_BUTTON);
313 gtk_box_pack_end(GTK_BOX(ibox), image, FALSE, TRUE, 0);
314 }
315
316 if (text) {
317 gtk_box_pack_start_defaults(GTK_BOX(bbox), lbox);
318 label = gtk_label_new(NULL);
319 gtk_label_set_text_with_mnemonic(GTK_LABEL(label), text);
320 gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
321 gtk_box_pack_start(GTK_BOX(lbox), label, FALSE, TRUE, 0);
322 gaim_set_accessible_label (button, label);
323 }
324
325 gtk_widget_show_all(bbox);
326
327 return button;
328 }
329
330
331 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)
332 {
333 GtkWidget *menuitem;
334 /*
335 GtkWidget *hbox;
336 GtkWidget *label;
337 */
338 GtkWidget *image;
339
340 if (icon == NULL)
341 menuitem = gtk_menu_item_new_with_mnemonic(str);
342 else
343 menuitem = gtk_image_menu_item_new_with_mnemonic(str);
344
345 if (menu)
346 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
347
348 if (sf)
349 g_signal_connect(G_OBJECT(menuitem), "activate", sf, data);
350
351 if (icon != NULL) {
352 image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU);
353 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
354 }
355 /* FIXME: this isn't right
356 if (mod) {
357 label = gtk_label_new(mod);
358 gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 2);
359 gtk_widget_show(label);
360 }
361 */
362 /*
363 if (accel_key) {
364 gtk_widget_add_accelerator(menuitem, "activate", accel, accel_key,
365 accel_mods, GTK_ACCEL_LOCKED);
366 }
367 */
368
369 gtk_widget_show_all(menuitem);
370
371 return menuitem;
372 }
373
374 GtkWidget *
375 gaim_gtk_make_frame(GtkWidget *parent, const char *title)
376 {
377 GtkWidget *vbox, *label, *hbox;
378 char *labeltitle;
379
380 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE);
381 gtk_box_pack_start(GTK_BOX(parent), vbox, FALSE, FALSE, 0);
382 gtk_widget_show(vbox);
383
384 label = gtk_label_new(NULL);
385
386 labeltitle = g_strdup_printf("<span weight=\"bold\">%s</span>", title);
387 gtk_label_set_markup(GTK_LABEL(label), labeltitle);
388 g_free(labeltitle);
389
390 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
391 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
392 gtk_widget_show(label);
393 gaim_set_accessible_label (vbox, label);
394
395 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE);
396 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
397 gtk_widget_show(hbox);
398
399 label = gtk_label_new(" ");
400 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
401 gtk_widget_show(label);
402
403 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE);
404 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
405 gtk_widget_show(vbox);
406
407 return vbox;
408 }
409
410 static void
411 protocol_menu_cb(GtkWidget *optmenu, GCallback cb)
412 {
413 GtkWidget *menu;
414 GtkWidget *item;
415 const char *protocol;
416 gpointer user_data;
417
418 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu));
419 item = gtk_menu_get_active(GTK_MENU(menu));
420
421 protocol = g_object_get_data(G_OBJECT(item), "protocol");
422 user_data = (g_object_get_data(G_OBJECT(optmenu), "user_data"));
423
424 if (cb != NULL)
425 ((void (*)(GtkWidget *, const char *, gpointer))cb)(item, protocol,
426 user_data);
427 }
428
429 GtkWidget *
430 gaim_gtk_protocol_option_menu_new(const char *id, GCallback cb,
431 gpointer user_data)
432 {
433 GaimPluginProtocolInfo *prpl_info;
434 GaimPlugin *plugin;
435 GtkWidget *hbox;
436 GtkWidget *label;
437 GtkWidget *optmenu;
438 GtkWidget *menu;
439 GtkWidget *item;
440 GtkWidget *image;
441 GdkPixbuf *pixbuf;
442 GdkPixbuf *scale;
443 GList *p;
444 GtkSizeGroup *sg;
445 char *filename;
446 const char *proto_name;
447 char buf[256];
448 int i, selected_index = -1;
449
450 optmenu = gtk_option_menu_new();
451 gtk_widget_show(optmenu);
452
453 g_object_set_data(G_OBJECT(optmenu), "user_data", user_data);
454
455 menu = gtk_menu_new();
456 gtk_widget_show(menu);
457
458 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
459
460 for (p = gaim_plugins_get_protocols(), i = 0;
461 p != NULL;
462 p = p->next, i++) {
463
464 plugin = (GaimPlugin *)p->data;
465 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
466
467 /* Create the item. */
468 item = gtk_menu_item_new();
469
470 /* Create the hbox. */
471 hbox = gtk_hbox_new(FALSE, 4);
472 gtk_container_add(GTK_CONTAINER(item), hbox);
473 gtk_widget_show(hbox);
474
475 /* Load the image. */
476 proto_name = prpl_info->list_icon(NULL, NULL);
477 g_snprintf(buf, sizeof(buf), "%s.png", proto_name);
478
479 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status",
480 "default", buf, NULL);
481 pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
482 g_free(filename);
483
484 if (pixbuf != NULL) {
485 /* Scale and insert the image */
486 scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16,
487 GDK_INTERP_BILINEAR);
488 image = gtk_image_new_from_pixbuf(scale);
489
490 g_object_unref(G_OBJECT(pixbuf));
491 g_object_unref(G_OBJECT(scale));
492 }
493 else
494 image = gtk_image_new();
495
496 gtk_size_group_add_widget(sg, image);
497
498 gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
499 gtk_widget_show(image);
500
501 /* Create the label. */
502 label = gtk_label_new(plugin->info->name);
503 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
504 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
505 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
506 gtk_widget_show(label);
507
508 g_object_set_data(G_OBJECT(item), "protocol", plugin->info->id);
509
510 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
511 gtk_widget_show(item);
512 gaim_set_accessible_label (item, label);
513
514 if (id != NULL && !strcmp(plugin->info->id, id))
515 selected_index = i;
516 }
517
518 gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
519
520 if (selected_index != -1)
521 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), selected_index);
522
523 g_signal_connect(G_OBJECT(optmenu), "changed",
524 G_CALLBACK(protocol_menu_cb), cb);
525
526 g_object_unref(sg);
527
528 return optmenu;
529 }
530
531 GaimAccount *
532 gaim_gtk_account_option_menu_get_selected(GtkWidget *optmenu)
533 {
534 GtkWidget *menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu));
535 GtkWidget *item = gtk_menu_get_active(GTK_MENU(menu));
536 return g_object_get_data(G_OBJECT(item), "account");
537 }
538
539 static void
540 account_menu_cb(GtkWidget *optmenu, GCallback cb)
541 {
542 GtkWidget *menu;
543 GtkWidget *item;
544 GaimAccount *account;
545 gpointer user_data;
546
547 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu));
548 item = gtk_menu_get_active(GTK_MENU(menu));
549
550 account = g_object_get_data(G_OBJECT(item), "account");
551 user_data = g_object_get_data(G_OBJECT(optmenu), "user_data");
552
553 if (cb != NULL)
554 ((void (*)(GtkWidget *, GaimAccount *, gpointer))cb)(item, account,
555 user_data);
556 }
557
558 static void
559 create_account_menu(GtkWidget *optmenu, GaimAccount *default_account,
560 GaimFilterAccountFunc filter_func, gboolean show_all)
561 {
562 GaimAccount *account;
563 GtkWidget *menu;
564 GtkWidget *item;
565 GtkWidget *image;
566 GtkWidget *hbox;
567 GtkWidget *label;
568 GdkPixbuf *pixbuf;
569 GdkPixbuf *scale;
570 GList *list;
571 GList *p;
572 GtkSizeGroup *sg;
573 char *filename;
574 const char *proto_name;
575 char buf[256];
576 int i, selected_index = -1;
577
578 if (show_all)
579 list = gaim_accounts_get_all();
580 else
581 list = gaim_connections_get_all();
582
583 menu = gtk_menu_new();
584 gtk_widget_show(menu);
585
586 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
587
588 for (p = list, i = 0; p != NULL; p = p->next, i++) {
589 GaimPluginProtocolInfo *prpl_info = NULL;
590 GaimPlugin *plugin;
591
592 if (show_all)
593 account = (GaimAccount *)p->data;
594 else {
595 GaimConnection *gc = (GaimConnection *)p->data;
596
597 account = gaim_connection_get_account(gc);
598 }
599
600 if (filter_func && !filter_func(account)) {
601 i--;
602 continue;
603 }
604
605 plugin = gaim_find_prpl(gaim_account_get_protocol_id(account));
606
607 if (plugin != NULL)
608 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
609
610 /* Create the item. */
611 item = gtk_menu_item_new();
612
613 /* Create the hbox. */
614 hbox = gtk_hbox_new(FALSE, 4);
615 gtk_container_add(GTK_CONTAINER(item), hbox);
616 gtk_widget_show(hbox);
617
618 /* Load the image. */
619 if (prpl_info != NULL) {
620 proto_name = prpl_info->list_icon(account, NULL);
621 g_snprintf(buf, sizeof(buf), "%s.png", proto_name);
622
623 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status",
624 "default", buf, NULL);
625 pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
626 g_free(filename);
627
628 if (pixbuf != NULL) {
629 /* Scale and insert the image */
630 scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16,
631 GDK_INTERP_BILINEAR);
632
633 if (gaim_account_is_disconnected(account) && show_all &&
634 gaim_connections_get_all())
635 gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE);
636
637 image = gtk_image_new_from_pixbuf(scale);
638
639 g_object_unref(G_OBJECT(pixbuf));
640 g_object_unref(G_OBJECT(scale));
641 }
642 else
643 image = gtk_image_new();
644 }
645 else
646 image = gtk_image_new();
647
648 gtk_size_group_add_widget(sg, image);
649
650 gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
651 gtk_widget_show(image);
652
653 if (gaim_account_get_alias(account)) {
654 g_snprintf(buf, sizeof(buf), "%s (%s) (%s)",
655 gaim_account_get_username(account),
656 gaim_account_get_alias(account),
657 gaim_account_get_protocol_name(account));
658 } else {
659 g_snprintf(buf, sizeof(buf), "%s (%s)",
660 gaim_account_get_username(account),
661 gaim_account_get_protocol_name(account));
662 }
663
664 /* Create the label. */
665 label = gtk_label_new(buf);
666 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
667 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
668 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
669 gtk_widget_show(label);
670
671 g_object_set_data(G_OBJECT(item), "account", account);
672
673 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
674 gtk_widget_show(item);
675 gaim_set_accessible_label (item, label);
676
677 if (default_account != NULL && account == default_account)
678 selected_index = i;
679 }
680
681 g_object_unref(sg);
682
683 gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
684
685 /* Set the place we should be at. */
686 if (selected_index != -1)
687 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), selected_index);
688 }
689
690 static void
691 regenerate_account_menu(GtkWidget *optmenu)
692 {
693 GtkWidget *menu;
694 GtkWidget *item;
695 gboolean show_all;
696 GaimAccount *account;
697 GaimFilterAccountFunc filter_func;
698
699 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu));
700 item = gtk_menu_get_active(GTK_MENU(menu));
701 account = g_object_get_data(G_OBJECT(item), "account");
702
703 show_all = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(optmenu),
704 "show_all"));
705
706 filter_func = g_object_get_data(G_OBJECT(optmenu),
707 "filter_func");
708
709 gtk_option_menu_remove_menu(GTK_OPTION_MENU(optmenu));
710
711 create_account_menu(optmenu, account, filter_func, show_all);
712 }
713
714 static void
715 account_menu_sign_on_off_cb(GaimConnection *gc, GtkWidget *optmenu)
716 {
717 regenerate_account_menu(optmenu);
718 }
719
720 static void
721 account_menu_added_removed_cb(GaimAccount *account, GtkWidget *optmenu)
722 {
723 regenerate_account_menu(optmenu);
724 }
725
726 static gboolean
727 account_menu_destroyed_cb(GtkWidget *optmenu, GdkEvent *event,
728 void *user_data)
729 {
730 gaim_signals_disconnect_by_handle(optmenu);
731
732 return FALSE;
733 }
734
735 void
736 gaim_gtk_account_option_menu_set_selected(GtkWidget *optmenu, GaimAccount *account)
737 {
738 GtkWidget *menu;
739 GtkWidget *item;
740 gboolean show_all;
741 GaimAccount *curaccount;
742 GaimFilterAccountFunc filter_func;
743
744 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu));
745 item = gtk_menu_get_active(GTK_MENU(menu));
746 curaccount = g_object_get_data(G_OBJECT(item), "account");
747
748 if (account == curaccount)
749 return;
750
751 show_all = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(optmenu),
752 "show_all"));
753
754 filter_func = g_object_get_data(G_OBJECT(optmenu),
755 "filter_func");
756
757 gtk_option_menu_remove_menu(GTK_OPTION_MENU(optmenu));
758
759 create_account_menu(optmenu, account, filter_func, show_all);
760 }
761
762 GtkWidget *
763 gaim_gtk_account_option_menu_new(GaimAccount *default_account,
764 gboolean show_all, GCallback cb,
765 GaimFilterAccountFunc filter_func,
766 gpointer user_data)
767 {
768 GtkWidget *optmenu;
769
770 /* Create the option menu */
771 optmenu = gtk_option_menu_new();
772 gtk_widget_show(optmenu);
773
774 g_signal_connect(G_OBJECT(optmenu), "destroy",
775 G_CALLBACK(account_menu_destroyed_cb), NULL);
776
777 /* Register the gaim sign on/off event callbacks. */
778 gaim_signal_connect(gaim_connections_get_handle(), "signed-on",
779 optmenu, GAIM_CALLBACK(account_menu_sign_on_off_cb),
780 optmenu);
781 gaim_signal_connect(gaim_connections_get_handle(), "signed-off",
782 optmenu, GAIM_CALLBACK(account_menu_sign_on_off_cb),
783 optmenu);
784 gaim_signal_connect(gaim_accounts_get_handle(), "account-added",
785 optmenu, GAIM_CALLBACK(account_menu_added_removed_cb),
786 optmenu);
787 gaim_signal_connect(gaim_accounts_get_handle(), "account-removed",
788 optmenu, GAIM_CALLBACK(account_menu_added_removed_cb),
789 optmenu);
790
791 /* Set some data. */
792 g_object_set_data(G_OBJECT(optmenu), "user_data", user_data);
793 g_object_set_data(G_OBJECT(optmenu), "show_all", GINT_TO_POINTER(show_all));
794 g_object_set_data(G_OBJECT(optmenu), "filter_func",
795 filter_func);
796
797 /* Create and set the actual menu. */
798 create_account_menu(optmenu, default_account, filter_func, show_all);
799
800 /* And now the last callback. */
801 g_signal_connect(G_OBJECT(optmenu), "changed",
802 G_CALLBACK(account_menu_cb), cb);
803
804 return optmenu;
805 }
806
807 gboolean
808 gaim_gtk_check_if_dir(const char *path, GtkFileSelection *filesel)
809 {
810 char *dirname;
811
812 if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
813 /* append a / if needed */
814 if (path[strlen(path) - 1] != G_DIR_SEPARATOR) {
815 dirname = g_strconcat(path, G_DIR_SEPARATOR_S, NULL);
816 } else {
817 dirname = g_strdup(path);
818 }
819 gtk_file_selection_set_filename(filesel, dirname);
820 g_free(dirname);
821 return TRUE;
822 }
823
824 return FALSE;
825 }
826
827 void
828 gaim_gtk_setup_gtkspell(GtkTextView *textview)
829 {
830 #ifdef USE_GTKSPELL
831 GError *error = NULL;
832 char *locale = NULL;
833
834 g_return_if_fail(textview != NULL);
835 g_return_if_fail(GTK_IS_TEXT_VIEW(textview));
836
837 if (gtkspell_new_attach(textview, locale, &error) == NULL && error)
838 {
839 gaim_debug_warning("gtkspell", "Failed to setup GtkSpell: %s\n",
840 error->message);
841 g_error_free(error);
842 }
843 #endif /* USE_GTKSPELL */
844 }
845
846 void
847 gaim_gtk_save_accels_cb(GtkAccelGroup *accel_group, guint arg1,
848 GdkModifierType arg2, GClosure *arg3,
849 gpointer data)
850 {
851 gaim_debug(GAIM_DEBUG_MISC, "accels", "accel changed, scheduling save.\n");
852
853 if (!accels_save_timer)
854 accels_save_timer = g_timeout_add(5000, gaim_gtk_save_accels, NULL);
855 }
856
857 gboolean
858 gaim_gtk_save_accels(gpointer data)
859 {
860 char *filename = NULL;
861
862 filename = g_build_filename(gaim_user_dir(), G_DIR_SEPARATOR_S,
863 "accels", NULL);
864 gaim_debug(GAIM_DEBUG_MISC, "accels", "saving accels to %s\n", filename);
865 gtk_accel_map_save(filename);
866 g_free(filename);
867
868 accels_save_timer = 0;
869 return FALSE;
870 }
871
872 void
873 gaim_gtk_load_accels()
874 {
875 char *filename = NULL;
876
877 filename = g_build_filename(gaim_user_dir(), G_DIR_SEPARATOR_S,
878 "accels", NULL);
879 gtk_accel_map_load(filename);
880 g_free(filename);
881 }
882
883 gboolean
884 gaim_gtk_parse_x_im_contact(const char *msg, gboolean all_accounts,
885 GaimAccount **ret_account, char **ret_protocol,
886 char **ret_username, char **ret_alias)
887 {
888 char *protocol = NULL;
889 char *username = NULL;
890 char *alias = NULL;
891 char *str;
892 char *c, *s;
893 gboolean valid;
894
895 g_return_val_if_fail(msg != NULL, FALSE);
896 g_return_val_if_fail(ret_protocol != NULL, FALSE);
897 g_return_val_if_fail(ret_username != NULL, FALSE);
898
899 s = str = g_strdup(msg);
900
901 while (*s != '\r' && *s != '\n' && *s != '\0')
902 {
903 char *key, *value;
904
905 key = s;
906
907 /* Grab the key */
908 while (*s != '\r' && *s != '\n' && *s != '\0' && *s != ' ')
909 s++;
910
911 if (*s == '\r') s++;
912
913 if (*s == '\n')
914 {
915 s++;
916 continue;
917 }
918
919 if (*s != '\0') *s++ = '\0';
920
921 /* Clear past any whitespace */
922 while (*s != '\0' && *s == ' ')
923 s++;
924
925 /* Now let's grab until the end of the line. */
926 value = s;
927
928 while (*s != '\r' && *s != '\n' && *s != '\0')
929 s++;
930
931 if (*s == '\r') *s++ = '\0';
932 if (*s == '\n') *s++ = '\0';
933
934 if ((c = strchr(key, ':')) != NULL)
935 {
936 if (!g_ascii_strcasecmp(key, "X-IM-Username:"))
937 username = g_strdup(value);
938 else if (!g_ascii_strcasecmp(key, "X-IM-Protocol:"))
939 protocol = g_strdup(value);
940 else if (!g_ascii_strcasecmp(key, "X-IM-Alias:"))
941 alias = g_strdup(value);
942 }
943 }
944
945 if (username != NULL && protocol != NULL)
946 {
947 valid = TRUE;
948
949 *ret_username = username;
950 *ret_protocol = protocol;
951
952 if (ret_alias != NULL)
953 *ret_alias = alias;
954
955 /* Check for a compatible account. */
956 if (ret_account != NULL)
957 {
958 GList *list;
959 GaimAccount *account = NULL;
960 GList *l;
961 const char *protoname;
962
963 if (all_accounts)
964 list = gaim_accounts_get_all();
965 else
966 list = gaim_connections_get_all();
967
968 for (l = list; l != NULL; l = l->next)
969 {
970 GaimConnection *gc;
971 GaimPluginProtocolInfo *prpl_info = NULL;
972 GaimPlugin *plugin;
973
974 if (all_accounts)
975 {
976 account = (GaimAccount *)l->data;
977
978 plugin = gaim_plugins_find_with_id(
979 gaim_account_get_protocol_id(account));
980
981 if (plugin == NULL)
982 {
983 account = NULL;
984
985 continue;
986 }
987
988 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
989 }
990 else
991 {
992 gc = (GaimConnection *)l->data;
993 account = gaim_connection_get_account(gc);
994
995 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
996 }
997
998 protoname = prpl_info->list_icon(account, NULL);
999
1000 if (!strcmp(protoname, protocol))
1001 break;
1002
1003 account = NULL;
1004 }
1005
1006 /* Special case for AIM and ICQ */
1007 if (account == NULL && (!strcmp(protocol, "aim") ||
1008 !strcmp(protocol, "icq")))
1009 {
1010 for (l = list; l != NULL; l = l->next)
1011 {
1012 GaimConnection *gc;
1013 GaimPluginProtocolInfo *prpl_info = NULL;
1014 GaimPlugin *plugin;
1015
1016 if (all_accounts)
1017 {
1018 account = (GaimAccount *)l->data;
1019
1020 plugin = gaim_plugins_find_with_id(
1021 gaim_account_get_protocol_id(account));
1022
1023 if (plugin == NULL)
1024 {
1025 account = NULL;
1026
1027 continue;
1028 }
1029
1030 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
1031 }
1032 else
1033 {
1034 gc = (GaimConnection *)l->data;
1035 account = gaim_connection_get_account(gc);
1036
1037 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
1038 }
1039
1040 protoname = prpl_info->list_icon(account, NULL);
1041
1042 if (!strcmp(protoname, "aim") || !strcmp(protoname, "icq"))
1043 break;
1044
1045 account = NULL;
1046 }
1047 }
1048
1049 *ret_account = account;
1050 }
1051 }
1052 else
1053 {
1054 valid = FALSE;
1055
1056 g_free(username);
1057 g_free(protocol);
1058 g_free(alias);
1059 }
1060
1061 g_free(str);
1062
1063 return valid;
1064 }
1065
1066 void
1067 gaim_set_accessible_label (GtkWidget *w, GtkWidget *l)
1068 {
1069 AtkObject *acc, *label;
1070 AtkObject *rel_obj[1];
1071 AtkRelationSet *set;
1072 AtkRelation *relation;
1073 const gchar *label_text;
1074 const gchar *existing_name;
1075
1076 acc = gtk_widget_get_accessible (w);
1077 label = gtk_widget_get_accessible (l);
1078
1079 /* If this object has no name, set it's name with the label text */
1080 existing_name = atk_object_get_name (acc);
1081 if (!existing_name) {
1082 label_text = gtk_label_get_text (GTK_LABEL(l));
1083 if (label_text)
1084 atk_object_set_name (acc, label_text);
1085 }
1086
1087 /* Create the labeled-by relation */
1088 set = atk_object_ref_relation_set (acc);
1089 rel_obj[0] = label;
1090 relation = atk_relation_new (rel_obj, 1, ATK_RELATION_LABELLED_BY);
1091 atk_relation_set_add (set, relation);
1092 g_object_unref (relation);
1093
1094 /* Create the label-for relation */
1095 set = atk_object_ref_relation_set (label);
1096 rel_obj[0] = acc;
1097 relation = atk_relation_new (rel_obj, 1, ATK_RELATION_LABEL_FOR);
1098 atk_relation_set_add (set, relation);
1099 g_object_unref (relation);
1100 }
1101
1102 #if GTK_CHECK_VERSION(2,2,0)
1103 static void
1104 gaim_gtk_menu_position_func(GtkMenu *menu,
1105 gint *x,
1106 gint *y,
1107 gboolean *push_in,
1108 gpointer data)
1109 {
1110 GtkWidget *widget;
1111 GtkRequisition requisition;
1112 GdkScreen *screen;
1113 GdkRectangle monitor;
1114 gint monitor_num;
1115 gint space_left, space_right, space_above, space_below;
1116 gint needed_width;
1117 gint needed_height;
1118 gint xthickness;
1119 gint ythickness;
1120 gboolean rtl;
1121
1122 g_return_if_fail(GTK_IS_MENU(menu));
1123
1124 widget = GTK_WIDGET(menu);
1125 screen = gtk_widget_get_screen(widget);
1126 xthickness = widget->style->xthickness;
1127 ythickness = widget->style->ythickness;
1128 rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
1129
1130 /*
1131 * We need the requisition to figure out the right place to
1132 * popup the menu. In fact, we always need to ask here, since
1133 * if a size_request was queued while we weren't popped up,
1134 * the requisition won't have been recomputed yet.
1135 */
1136 gtk_widget_size_request (widget, &requisition);
1137
1138 monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
1139
1140 push_in = FALSE;
1141
1142 /*
1143 * The placement of popup menus horizontally works like this (with
1144 * RTL in parentheses)
1145 *
1146 * - If there is enough room to the right (left) of the mouse cursor,
1147 * position the menu there.
1148 *
1149 * - Otherwise, if if there is enough room to the left (right) of the
1150 * mouse cursor, position the menu there.
1151 *
1152 * - Otherwise if the menu is smaller than the monitor, position it
1153 * on the side of the mouse cursor that has the most space available
1154 *
1155 * - Otherwise (if there is simply not enough room for the menu on the
1156 * monitor), position it as far left (right) as possible.
1157 *
1158 * Positioning in the vertical direction is similar: first try below
1159 * mouse cursor, then above.
1160 */
1161 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1162
1163 space_left = *x - monitor.x;
1164 space_right = monitor.x + monitor.width - *x - 1;
1165 space_above = *y - monitor.y;
1166 space_below = monitor.y + monitor.height - *y - 1;
1167
1168 /* position horizontally */
1169
1170 /* the amount of space we need to position the menu. Note the
1171 * menu is offset "xthickness" pixels
1172 */
1173 needed_width = requisition.width - xthickness;
1174
1175 if (needed_width <= space_left ||
1176 needed_width <= space_right)
1177 {
1178 if ((rtl && needed_width <= space_left) ||
1179 (!rtl && needed_width > space_right))
1180 {
1181 /* position left */
1182 *x = *x + xthickness - requisition.width + 1;
1183 }
1184 else
1185 {
1186 /* position right */
1187 *x = *x - xthickness;
1188 }
1189
1190 /* x is clamped on-screen further down */
1191 }
1192 else if (requisition.width <= monitor.width)
1193 {
1194 /* the menu is too big to fit on either side of the mouse
1195 * cursor, but smaller than the monitor. Position it on
1196 * the side that has the most space
1197 */
1198 if (space_left > space_right)
1199 {
1200 /* left justify */
1201 *x = monitor.x;
1202 }
1203 else
1204 {
1205 /* right justify */
1206 *x = monitor.x + monitor.width - requisition.width;
1207 }
1208 }
1209 else /* menu is simply too big for the monitor */
1210 {
1211 if (rtl)
1212 {
1213 /* right justify */
1214 *x = monitor.x + monitor.width - requisition.width;
1215 }
1216 else
1217 {
1218 /* left justify */
1219 *x = monitor.x;
1220 }
1221 }
1222
1223 /* Position vertically. The algorithm is the same as above, but
1224 * simpler because we don't have to take RTL into account.
1225 */
1226 needed_height = requisition.height - ythickness;
1227
1228 if (needed_height <= space_above ||
1229 needed_height <= space_below)
1230 {
1231 if (needed_height <= space_below)
1232 *y = *y - ythickness;
1233 else
1234 *y = *y + ythickness - requisition.height + 1;
1235
1236 *y = CLAMP (*y, monitor.y,
1237 monitor.y + monitor.height - requisition.height);
1238 }
1239 else if (needed_height > space_below && needed_height > space_above)
1240 {
1241 if (space_below >= space_above)
1242 *y = monitor.y + monitor.height - requisition.height;
1243 else
1244 *y = monitor.y;
1245 }
1246 else
1247 {
1248 *y = monitor.y;
1249 }
1250 }
1251
1252 #endif
1253
1254 void
1255 gaim_gtk_treeview_popup_menu_position_func(GtkMenu *menu,
1256 gint *x,
1257 gint *y,
1258 gboolean *push_in,
1259 gpointer data)
1260 {
1261 GtkWidget *widget = GTK_WIDGET(data);
1262 GtkTreeView *tv = GTK_TREE_VIEW(data);
1263 GtkTreePath *path;
1264 GtkTreeViewColumn *col;
1265 GdkRectangle rect;
1266 gint ythickness = GTK_WIDGET(menu)->style->ythickness;
1267
1268 gdk_window_get_origin (widget->window, x, y);
1269 gtk_tree_view_get_cursor (tv, &path, &col);
1270 gtk_tree_view_get_cell_area (tv, path, col, &rect);
1271
1272 *x += rect.x+rect.width;
1273 *y += rect.y+rect.height+ythickness;
1274 #if GTK_CHECK_VERSION(2,2,0)
1275 gaim_gtk_menu_position_func (menu, x, y, push_in, data);
1276 #endif
1277 }
1278
1279 enum {
1280 DND_FILE_TRANSFER,
1281 DND_IM_IMAGE,
1282 DND_BUDDY_ICON
1283 };
1284
1285 typedef struct {
1286 char *filename;
1287 GaimAccount *account;
1288 char *who;
1289 } _DndData;
1290
1291 static void dnd_image_ok_callback(_DndData *data, int choice)
1292 {
1293 char *filedata;
1294 size_t size;
1295 GError *err = NULL;
1296 GaimConversation *conv;
1297 GaimGtkConversation *gtkconv;
1298 GtkTextIter iter;
1299 int id;
1300 switch (choice) {
1301 case DND_BUDDY_ICON:
1302 if (!g_file_get_contents(data->filename, &filedata, &size,
1303 &err)) {
1304 char *str;
1305
1306 str = g_strdup_printf(_("The following error has occurred loading %s: %s"), data->filename, err->message);
1307 gaim_notify_error(NULL, NULL,
1308 _("Failed to load image"),
1309 str);
1310
1311 g_error_free(err);
1312 g_free(str);
1313
1314 return;
1315 }
1316
1317 gaim_buddy_icons_set_for_user(data->account, data->who, filedata, size);
1318 g_free(filedata);
1319 break;
1320 case DND_FILE_TRANSFER:
1321 serv_send_file(gaim_account_get_connection(data->account), data->who, data->filename);
1322 break;
1323 case DND_IM_IMAGE:
1324 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, data->account, data->who);
1325 gtkconv = GAIM_GTK_CONVERSATION(conv);
1326
1327 if (!g_file_get_contents(data->filename, &filedata, &size,
1328 &err)) {
1329 char *str;
1330
1331 str = g_strdup_printf(_("The following error has occurred loading %s: %s"), data->filename, err->message);
1332 gaim_notify_error(NULL, NULL,
1333 _("Failed to load image"),
1334 str);
1335
1336 g_error_free(err);
1337 g_free(str);
1338
1339 return;
1340 }
1341 id = gaim_imgstore_add(filedata, size, data->filename);
1342 g_free(filedata);
1343
1344 gtk_text_buffer_get_iter_at_mark(GTK_IMHTML(gtkconv->entry)->text_buffer, &iter,
1345 gtk_text_buffer_get_insert(GTK_IMHTML(gtkconv->entry)->text_buffer));
1346 gtk_imhtml_insert_image_at_iter(GTK_IMHTML(gtkconv->entry), id, &iter);
1347 gaim_imgstore_unref(id);
1348
1349 break;
1350 }
1351 free(data->filename);
1352 free(data->who);
1353 free(data);
1354 }
1355
1356 static void dnd_image_cancel_callback(_DndData *data, int choice)
1357 {
1358 free(data->filename);
1359 free(data->who);
1360 free(data);
1361 }
1362
1363 static void dnd_set_icon_ok_cb(_DndData *data)
1364 {
1365 dnd_image_ok_callback(data, DND_BUDDY_ICON);
1366 }
1367
1368 static void dnd_set_icon_cancel_cb(_DndData *data)
1369 {
1370 free(data->filename);
1371 free(data->who);
1372 free(data);
1373 }
1374
1375 void
1376 gaim_dnd_file_manage(GtkSelectionData *sd, GaimAccount *account, const char *who)
1377 {
1378 GList *tmp;
1379 GdkPixbuf *pb;
1380 GList *files = gaim_uri_list_extract_filenames((const gchar *)sd->data);
1381 GaimConnection *gc = gaim_account_get_connection(account);
1382 GaimPluginProtocolInfo *prpl_info = NULL;
1383 gboolean file_send_ok = FALSE;
1384 #ifndef _WIN32
1385 GaimDesktopItem *item;
1386 #endif
1387
1388 g_return_if_fail(account != NULL);
1389 g_return_if_fail(who != NULL);
1390
1391 for(tmp = files; tmp != NULL ; tmp = g_list_next(tmp)) {
1392 gchar *filename = tmp->data;
1393 gchar *basename = g_path_get_basename(filename);
1394
1395 /* Set the default action: don't send anything */
1396 file_send_ok = FALSE;
1397
1398 /* XXX - Make ft API support creating a transfer with more than one file */
1399 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
1400 continue;
1401 }
1402
1403 /* XXX - make ft api suupport sending a directory */
1404 /* Are we dealing with a directory? */
1405 if (g_file_test(filename, G_FILE_TEST_IS_DIR)) {
1406 char *str;
1407
1408 str = g_strdup_printf(_("Cannot send folder %s."), basename);
1409 gaim_notify_error(NULL, NULL,
1410 str,_("Gaim cannot transfer a folder. You will need to send the files within individually"));
1411
1412 g_free(str);
1413
1414 continue;
1415 }
1416
1417 /* Are we dealing with an image? */
1418 pb = gdk_pixbuf_new_from_file(filename, NULL);
1419 if (pb) {
1420 _DndData *data = g_malloc(sizeof(_DndData));
1421 gboolean ft = FALSE, im = FALSE;
1422
1423 data->who = g_strdup(who);
1424 data->filename = g_strdup(filename);
1425 data->account = account;
1426
1427 if (gc)
1428 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
1429
1430 if (prpl_info && prpl_info->options & OPT_PROTO_IM_IMAGE)
1431 im = TRUE;
1432
1433 if (prpl_info && prpl_info->can_receive_file)
1434 ft = prpl_info->can_receive_file(gc, who);
1435
1436 if (im && ft)
1437 gaim_request_choice(NULL, NULL,
1438 _("You have dragged an image"),
1439 _("You can send this image as a file transfer, "
1440 "embed it into this message, or use it as the buddy icon for this user."),
1441 DND_FILE_TRANSFER, "OK", (GCallback)dnd_image_ok_callback,
1442 "Cancel", (GCallback)dnd_image_cancel_callback, data,
1443 _("Set as buddy icon"), DND_BUDDY_ICON,
1444 _("Send image file"), DND_FILE_TRANSFER,
1445 _("Insert in message"), DND_IM_IMAGE, NULL);
1446 else if (!(im || ft))
1447 gaim_request_yes_no(NULL, NULL, _("You have dragged an image"),
1448 _("Would you like to set it as the buddy icon for this user?"),
1449 0, data, (GCallback)dnd_set_icon_ok_cb, (GCallback)dnd_set_icon_cancel_cb);
1450 else
1451 gaim_request_choice(NULL, NULL,
1452 _("You have dragged an image"),
1453 ft ? _("You can send this image as a file transfer or "
1454 "embed it into this message, or use it as the buddy icon for this user.") :
1455 _("You can insert this image into this message, or use it as the buddy icon for this user"),
1456 ft ? DND_FILE_TRANSFER : DND_IM_IMAGE, "OK", (GCallback)dnd_image_ok_callback,
1457 "Cancel", (GCallback)dnd_image_cancel_callback, data,
1458 _("Set as buddy icon"), DND_BUDDY_ICON,
1459 ft ? _("Send image file") : _("Insert in message"), ft ? DND_FILE_TRANSFER : DND_IM_IMAGE, NULL);
1460 return;
1461 }
1462
1463 #ifndef _WIN32
1464 /* Are we trying to send a .desktop file? */
1465 else if (gaim_str_has_suffix(basename, ".desktop") && (item = gaim_desktop_item_new_from_file(filename))) {
1466 GaimDesktopItemType dtype;
1467 char key[64];
1468 const char *itemname = NULL;
1469
1470 #if GTK_CHECK_VERSION(2,6,0)
1471 const char * const *langs;
1472 int i;
1473 langs = g_get_language_names();
1474 for (i = 0; langs[i]; i++) {
1475 g_snprintf(key, sizeof(key), "Name[%s]", langs[i]);
1476 itemname = gaim_desktop_item_get_string(item, key);
1477 break;
1478 }
1479 #else
1480 const char *lang = g_getenv("LANG");
1481 char *dot;
1482 dot = strchr(lang, '.');
1483 if (dot)
1484 *dot = '\0';
1485 g_snprintf(key, sizeof(key), "Name[%s]", lang);
1486 itemname = gaim_desktop_item_get_string(item, key);
1487 #endif
1488 if (!itemname)
1489 itemname = gaim_desktop_item_get_string(item, "Name");
1490
1491 dtype = gaim_desktop_item_get_entry_type(item);
1492 switch (dtype) {
1493 GaimConversation *conv;
1494 GaimGtkConversation *gtkconv;
1495
1496 case GAIM_DESKTOP_ITEM_TYPE_LINK:
1497 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, who);
1498 gtkconv = GAIM_GTK_CONVERSATION(conv);
1499 gtk_imhtml_insert_link(GTK_IMHTML(gtkconv->entry),
1500 gtk_text_buffer_get_insert(GTK_IMHTML(gtkconv->entry)->text_buffer),
1501 gaim_desktop_item_get_string(item, "URL"), itemname);
1502 break;
1503 default:
1504 /* I don't know if we really want to do anything here. Most of the desktop item types are crap like
1505 * "MIME Type" (I have no clue how that would be a desktop item) and "Comment"... nothing we can really
1506 * send. The only logical one is "Application," but do we really want to send a binary and nothing else?
1507 * Probably not. I'll just give an error and return. */
1508 /* The original patch sent the icon used by the launcher. That's probably wrong */
1509 gaim_notify_error(NULL, NULL, _("Cannot send launcher"), _("You dragged a desktop launcher. "
1510 "Most likely you wanted to send whatever this launcher points to instead of this launcher"
1511 " itself."));
1512 break;
1513 }
1514 gaim_desktop_item_unref(item);
1515 return;
1516 }
1517 #endif /* _WIN32 */
1518
1519 /* Everything is fine, let's send */
1520 serv_send_file(gc, who, filename);
1521 g_free(filename);
1522 }
1523 g_list_free(files);
1524 }
1525
1526 void gaim_gtk_buddy_icon_get_scale_size(GdkPixbuf *buf, GaimBuddyIconSpec *spec, int *width, int *height)
1527 {
1528 *width = gdk_pixbuf_get_width(buf);
1529 *height = gdk_pixbuf_get_height(buf);
1530
1531 gaim_buddy_icon_get_scale_size(spec, width, height);
1532
1533 /* and now for some arbitrary sanity checks */
1534 if(*width > 100)
1535 *width = 100;
1536 if(*height > 100)
1537 *height = 100;
1538 }
1539
1540 GdkPixbuf *
1541 gaim_gtk_create_prpl_icon(GaimAccount *account, double scale_factor)
1542 {
1543 GaimPlugin *prpl;
1544 GaimPluginProtocolInfo *prpl_info;
1545 const char *protoname = NULL;
1546 char buf[256]; /* TODO: We should use a define for max file length */
1547 char *filename = NULL;
1548 GdkPixbuf *pixbuf, *scaled;
1549
1550 g_return_val_if_fail(account != NULL, NULL);
1551
1552 prpl = gaim_find_prpl(gaim_account_get_protocol_id(account));
1553 if (prpl == NULL)
1554 return NULL;
1555
1556 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
1557 if (prpl_info->list_icon == NULL)
1558 return NULL;
1559
1560 protoname = prpl_info->list_icon(account, NULL);
1561 if (protoname == NULL)
1562 return NULL;
1563
1564 /*
1565 * Status icons will be themeable too, and then it will look up
1566 * protoname from the theme
1567 */
1568 g_snprintf(buf, sizeof(buf), "%s.png", protoname);
1569
1570 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status",
1571 "default", buf, NULL);
1572 pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
1573 g_free(filename);
1574
1575 scaled = gdk_pixbuf_scale_simple(pixbuf, 32*scale_factor,
1576 32*scale_factor, GDK_INTERP_BILINEAR);
1577 g_object_unref(pixbuf);
1578
1579 return scaled;
1580 }
1581
1582 static GdkPixbuf *
1583 overlay_status_onto_icon(GdkPixbuf *pixbuf, GaimStatusPrimitive primitive)
1584 {
1585 const char *type_name;
1586 char basename[256];
1587 char *filename;
1588 GdkPixbuf *emblem;
1589
1590 type_name = gaim_primitive_get_id_from_type(primitive);
1591
1592 g_snprintf(basename, sizeof(basename), "%s.png", type_name);
1593 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status",
1594 "default", basename, NULL);
1595 emblem = gdk_pixbuf_new_from_file(filename, NULL);
1596 g_free(filename);
1597
1598 if (emblem != NULL) {
1599 int width, height, emblem_width, emblem_height;
1600 int new_emblem_width, new_emblem_height;
1601
1602 width = gdk_pixbuf_get_width(pixbuf);
1603 height = gdk_pixbuf_get_height(pixbuf);
1604 emblem_width = gdk_pixbuf_get_width(emblem);
1605 emblem_height = gdk_pixbuf_get_height(emblem);
1606
1607 /*
1608 * Figure out how big to make the emblem. Normally the emblem
1609 * will have half the width of the pixbuf. But we don't make
1610 * an emblem any smaller than 10 pixels because it becomes
1611 * unrecognizable, unless the width of the pixbuf is less than
1612 * 10 pixels, in which case we make the emblem width the same
1613 * as the pixbuf width.
1614 */
1615 new_emblem_width = MAX(width / 2, MIN(width, 10));
1616 new_emblem_height = MAX(height / 2, MIN(height, 10));
1617
1618 /* Overlay emblem onto the bottom right corner of pixbuf */
1619 gdk_pixbuf_composite(emblem, pixbuf,
1620 width - new_emblem_width, height - new_emblem_height,
1621 new_emblem_width, new_emblem_height,
1622 width - new_emblem_width, height - new_emblem_height,
1623 (double)new_emblem_width / (double)emblem_width,
1624 (double)new_emblem_height / (double)emblem_height,
1625 GDK_INTERP_BILINEAR,
1626 255);
1627 g_object_unref(emblem);
1628 }
1629
1630 return pixbuf;
1631 }
1632
1633 GdkPixbuf *
1634 gaim_gtk_create_prpl_icon_with_status(GaimAccount *account, GaimStatusType *status_type, double scale_factor)
1635 {
1636 GdkPixbuf *pixbuf;
1637
1638 pixbuf = gaim_gtk_create_prpl_icon(account, scale_factor);
1639 if (pixbuf == NULL)
1640 return NULL;
1641
1642 /*
1643 * TODO: Let the prpl pick the emblem on a per status basis,
1644 * and only use the primitive as a fallback?
1645 */
1646
1647 return overlay_status_onto_icon(pixbuf,
1648 gaim_status_type_get_primitive(status_type));
1649 }
1650
1651 GdkPixbuf *
1652 gaim_gtk_create_gaim_icon_with_status(GaimStatusPrimitive primitive, double scale_factor)
1653 {
1654 gchar *filename;
1655 GdkPixbuf *orig, *pixbuf;
1656
1657 filename = g_build_filename(DATADIR, "pixmaps", "gaim.png", NULL);
1658 orig = gdk_pixbuf_new_from_file(filename, NULL);
1659 g_free(filename);
1660 if (orig == NULL)
1661 return NULL;
1662
1663 pixbuf = gdk_pixbuf_scale_simple(orig, 32*scale_factor,
1664 32*scale_factor, GDK_INTERP_BILINEAR);
1665 g_object_unref(G_OBJECT(orig));
1666
1667 return overlay_status_onto_icon(pixbuf, primitive);
1668 }
1669
1670 static void
1671 menu_action_cb(GtkMenuItem *item, gpointer object)
1672 {
1673 gpointer data;
1674 void (*callback)(gpointer, gpointer);
1675
1676 callback = g_object_get_data(G_OBJECT(item), "gaimcallback");
1677 data = g_object_get_data(G_OBJECT(item), "gaimcallbackdata");
1678
1679 if (callback)
1680 callback(object, data);
1681 }
1682
1683 void
1684 gaim_gtk_append_menu_action(GtkWidget *menu, GaimMenuAction *act,
1685 gpointer object)
1686 {
1687 if (act == NULL) {
1688 gaim_separator(menu);
1689 } else {
1690 GtkWidget *menuitem;
1691
1692 if (act->children == NULL) {
1693 menuitem = gtk_menu_item_new_with_mnemonic(act->label);
1694
1695 if (act->callback != NULL) {
1696 g_object_set_data(G_OBJECT(menuitem),
1697 "gaimcallback",
1698 act->callback);
1699 g_object_set_data(G_OBJECT(menuitem),
1700 "gaimcallbackdata",
1701 act->data);
1702 g_signal_connect(G_OBJECT(menuitem), "activate",
1703 G_CALLBACK(menu_action_cb),
1704 object);
1705 } else {
1706 gtk_widget_set_sensitive(menuitem, FALSE);
1707 }
1708
1709 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1710 } else {
1711 GList *l = NULL;
1712 GtkWidget *submenu = NULL;
1713 GtkAccelGroup *group;
1714
1715 menuitem = gtk_menu_item_new_with_mnemonic(act->label);
1716 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1717
1718 submenu = gtk_menu_new();
1719 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
1720
1721 group = gtk_menu_get_accel_group(GTK_MENU(menu));
1722 if (group) {
1723 char *path = g_strdup_printf("%s/%s", GTK_MENU_ITEM(menuitem)->accel_path, act->label);
1724 gtk_menu_set_accel_path(GTK_MENU(submenu), path);
1725 g_free(path);
1726 gtk_menu_set_accel_group(GTK_MENU(submenu), group);
1727 }
1728
1729 for (l = act->children; l; l = l->next) {
1730 GaimMenuAction *act = (GaimMenuAction *)l->data;
1731
1732 gaim_gtk_append_menu_action(submenu, act, object);
1733 }
1734 g_list_free(act->children);
1735 act->children = NULL;
1736 }
1737 gaim_menu_action_free(act);
1738 }
1739 }
1740
1741 #if GTK_CHECK_VERSION(2,3,0)
1742 # define NEW_STYLE_COMPLETION
1743 #endif
1744
1745 #ifndef NEW_STYLE_COMPLETION
1746 typedef struct
1747 {
1748 GCompletion *completion;
1749
1750 gboolean completion_started;
1751 gboolean all;
1752
1753 } GaimGtkCompletionData;
1754 #endif
1755
1756 #ifndef NEW_STYLE_COMPLETION
1757 static gboolean
1758 completion_entry_event(GtkEditable *entry, GdkEventKey *event,
1759 GaimGtkCompletionData *data)
1760 {
1761 int pos, end_pos;
1762
1763 if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Tab)
1764 {
1765 gtk_editable_get_selection_bounds(entry, &pos, &end_pos);
1766
1767 if (data->completion_started &&
1768 pos != end_pos && pos > 1 &&
1769 end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry))))
1770 {
1771 gtk_editable_select_region(entry, 0, 0);
1772 gtk_editable_set_position(entry, -1);
1773
1774 return TRUE;
1775 }
1776 }
1777 else if (event->type == GDK_KEY_PRESS && event->length > 0)
1778 {
1779 char *prefix, *nprefix;
1780
1781 gtk_editable_get_selection_bounds(entry, &pos, &end_pos);
1782
1783 if (data->completion_started &&
1784 pos != end_pos && pos > 1 &&
1785 end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry))))
1786 {
1787 char *temp;
1788
1789 temp = gtk_editable_get_chars(entry, 0, pos);
1790 prefix = g_strconcat(temp, event->string, NULL);
1791 g_free(temp);
1792 }
1793 else if (pos == end_pos && pos > 1 &&
1794 end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry))))
1795 {
1796 prefix = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)),
1797 event->string, NULL);
1798 }
1799 else
1800 return FALSE;
1801
1802 pos = strlen(prefix);
1803 nprefix = NULL;
1804
1805 g_completion_complete(data->completion, prefix, &nprefix);
1806
1807 if (nprefix != NULL)
1808 {
1809 gtk_entry_set_text(GTK_ENTRY(entry), nprefix);
1810 gtk_editable_set_position(entry, pos);
1811 gtk_editable_select_region(entry, pos, -1);
1812
1813 data->completion_started = TRUE;
1814
1815 g_free(nprefix);
1816 g_free(prefix);
1817
1818 return TRUE;
1819 }
1820
1821 g_free(prefix);
1822 }
1823
1824 return FALSE;
1825 }
1826
1827 static void
1828 destroy_completion_data(GtkWidget *w, GaimGtkCompletionData *data)
1829 {
1830 g_list_foreach(data->completion->items, (GFunc)g_free, NULL);
1831 g_completion_free(data->completion);
1832
1833 g_free(data);
1834 }
1835 #endif /* !NEW_STYLE_COMPLETION */
1836
1837 #ifdef NEW_STYLE_COMPLETION
1838 static gboolean screenname_completion_match_func(GtkEntryCompletion *completion,
1839 const gchar *key, GtkTreeIter *iter, gpointer user_data)
1840 {
1841 GtkTreeModel *model;
1842 GValue val1;
1843 GValue val2;
1844 const char *tmp;
1845
1846 model = gtk_entry_completion_get_model (completion);
1847
1848 val1.g_type = 0;
1849 gtk_tree_model_get_value(model, iter, 2, &val1);
1850 tmp = g_value_get_string(&val1);
1851 if (tmp != NULL && gaim_str_has_prefix(tmp, key))
1852 {
1853 g_value_unset(&val1);
1854 return TRUE;
1855 }
1856 g_value_unset(&val1);
1857
1858 val2.g_type = 0;
1859 gtk_tree_model_get_value(model, iter, 3, &val2);
1860 tmp = g_value_get_string(&val2);
1861 if (tmp != NULL && gaim_str_has_prefix(tmp, key))
1862 {
1863 g_value_unset(&val2);
1864 return TRUE;
1865 }
1866 g_value_unset(&val2);
1867
1868 return FALSE;
1869 }
1870
1871 static gboolean screenname_completion_match_selected_cb(GtkEntryCompletion *completion,
1872 GtkTreeModel *model, GtkTreeIter *iter, gpointer *user_data)
1873 {
1874 GValue val;
1875 GtkWidget *optmenu = user_data[1];
1876 GaimAccount *account;
1877
1878 val.g_type = 0;
1879 gtk_tree_model_get_value(model, iter, 1, &val);
1880 gtk_entry_set_text(GTK_ENTRY(user_data[0]), g_value_get_string(&val));
1881 g_value_unset(&val);
1882
1883 gtk_tree_model_get_value(model, iter, 4, &val);
1884 account = g_value_get_pointer(&val);
1885 g_value_unset(&val);
1886
1887 if (account == NULL)
1888 return TRUE;
1889
1890 if (optmenu != NULL) {
1891 GList *items;
1892 guint index = 0;
1893 gaim_gtk_account_option_menu_set_selected(optmenu, account);
1894 items = GTK_MENU_SHELL(gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu)))->children;
1895
1896 do {
1897 if (account == g_object_get_data(G_OBJECT(items->data), "account")) {
1898 /* Set the account in the GUI. */
1899 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), index);
1900 return TRUE;
1901 }
1902 index++;
1903 } while ((items = items->next) != NULL);
1904 }
1905
1906 return TRUE;
1907 }
1908
1909 static void
1910 add_screenname_autocomplete_entry(GtkListStore *store, const char *buddy_alias, const char *contact_alias,
1911 const GaimAccount *account, const char *screenname)
1912 {
1913 GtkTreeIter iter;
1914 gboolean completion_added = FALSE;
1915 gchar *normalized_screenname;
1916 gchar *tmp;
1917
1918 tmp = g_utf8_normalize(screenname, -1, G_NORMALIZE_DEFAULT);
1919 normalized_screenname = g_utf8_casefold(tmp, -1);
1920 g_free(tmp);
1921
1922 /* There's no sense listing things like: 'xxx "xxx"'
1923 when the screenname and buddy alias match. */
1924 if (buddy_alias && strcmp(buddy_alias, screenname)) {
1925 char *completion_entry = g_strdup_printf("%s \"%s\"", screenname, buddy_alias);
1926 char *tmp2 = g_utf8_normalize(buddy_alias, -1, G_NORMALIZE_DEFAULT);
1927
1928 tmp = g_utf8_casefold(tmp2, -1);
1929 g_free(tmp2);
1930
1931 gtk_list_store_append(store, &iter);
1932 gtk_list_store_set(store, &iter,
1933 0, completion_entry,
1934 1, screenname,
1935 2, normalized_screenname,
1936 3, tmp,
1937 4, account,
1938 -1);
1939 g_free(completion_entry);
1940 g_free(tmp);
1941 completion_added = TRUE;
1942 }
1943
1944 /* There's no sense listing things like: 'xxx "xxx"'
1945 when the screenname and contact alias match. */
1946 if (contact_alias && strcmp(contact_alias, screenname)) {
1947 /* We don't want duplicates when the contact and buddy alias match. */
1948 if (!buddy_alias || strcmp(contact_alias, buddy_alias)) {
1949 char *completion_entry = g_strdup_printf("%s \"%s\"",
1950 screenname, contact_alias);
1951 char *tmp2 = g_utf8_normalize(contact_alias, -1, G_NORMALIZE_DEFAULT);
1952
1953 tmp = g_utf8_casefold(tmp2, -1);
1954 g_free(tmp2);
1955
1956 gtk_list_store_append(store, &iter);
1957 gtk_list_store_set(store, &iter,
1958 0, completion_entry,
1959 1, screenname,
1960 2, normalized_screenname,
1961 3, tmp,
1962 4, account,
1963 -1);
1964 g_free(completion_entry);
1965 g_free(tmp);
1966 completion_added = TRUE;
1967 }
1968 }
1969
1970 if (completion_added == FALSE) {
1971 /* Add the buddy's screenname. */
1972 gtk_list_store_append(store, &iter);
1973 gtk_list_store_set(store, &iter,
1974 0, screenname,
1975 1, screenname,
1976 2, normalized_screenname,
1977 3, NULL,
1978 4, account,
1979 -1);
1980 }
1981
1982 g_free(normalized_screenname);
1983 }
1984 #endif /* NEW_STYLE_COMPLETION */
1985
1986 static void get_log_set_name(GaimLogSet *set, gpointer value, gpointer **set_hash_data)
1987 {
1988 /* 1. Don't show buddies because we will have gotten them already.
1989 * 2. Only show those with non-NULL accounts that are currently connected.
1990 * 3. The boxes that use this autocomplete code handle only IMs. */
1991 if (!set->buddy &&
1992 (GPOINTER_TO_INT(set_hash_data[1]) ||
1993 (set->account != NULL && gaim_account_is_connected(set->account))) &&
1994 set->type == GAIM_LOG_IM) {
1995 #ifdef NEW_STYLE_COMPLETION
1996 add_screenname_autocomplete_entry((GtkListStore *)set_hash_data[0],
1997 NULL, NULL, set->account, set->name);
1998 #else
1999 GList **items = ((GList **)set_hash_data[0]);
2000 /* Steal the name for the GCompletion. */
2001 *items = g_list_append(*items, set->name);
2002 set->name = set->normalized_name = NULL;
2003 #endif /* NEW_STYLE_COMPLETION */
2004 }
2005 }
2006
2007 #ifdef NEW_STYLE_COMPLETION
2008 static void
2009 add_completion_list(GtkListStore *store)
2010 {
2011 GaimBlistNode *gnode, *cnode, *bnode;
2012 gboolean all = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(store), "screenname-all"));
2013 GHashTable *sets;
2014 gpointer set_hash_data[] = {store, GINT_TO_POINTER(all)};
2015
2016 gtk_list_store_clear(store);
2017
2018 for (gnode = gaim_get_blist()->root; gnode != NULL; gnode = gnode->next)
2019 {
2020 if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
2021 continue;
2022
2023 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
2024 {
2025 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
2026 continue;
2027
2028 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
2029 {
2030 GaimBuddy *buddy = (GaimBuddy *)bnode;
2031
2032 if (!all && !gaim_account_is_connected(buddy->account))
2033 continue;
2034
2035 add_screenname_autocomplete_entry(store,
2036 ((GaimContact *)cnode)->alias,
2037 gaim_buddy_get_contact_alias(buddy),
2038 buddy->account,
2039 buddy->name
2040 );
2041 }
2042 }
2043 }
2044
2045 sets = gaim_log_get_log_sets();
2046 g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data);
2047 g_hash_table_destroy(sets);
2048 }
2049 #else
2050 static void
2051 add_completion_list(GaimGtkCompletionData *data)
2052 {
2053 GaimBlistNode *gnode, *cnode, *bnode;
2054 GCompletion *completion;
2055 GList *item = g_list_append(NULL, NULL);
2056 GHashTable *sets;
2057 gpointer set_hash_data[2];
2058
2059 completion = data->completion;
2060
2061 g_list_foreach(completion->items, (GFunc)g_free, NULL);
2062 g_completion_clear_items(completion);
2063
2064 for (gnode = gaim_get_blist()->root; gnode != NULL; gnode = gnode->next)
2065 {
2066 if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
2067 continue;
2068
2069 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
2070 {
2071 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
2072 continue;
2073
2074 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
2075 {
2076 GaimBuddy *buddy = (GaimBuddy *)bnode;
2077
2078 if (!data->all && !gaim_account_is_connected(buddy->account))
2079 continue;
2080
2081 item->data = g_strdup(buddy->name);
2082 g_completion_add_items(data->completion, item);
2083 }
2084 }
2085 }
2086 g_list_free(item);
2087
2088 sets = gaim_log_get_log_sets();
2089 item = NULL;
2090 set_hash_data[0] = &item;
2091 set_hash_data[1] = GINT_TO_POINTER(data->all);
2092 g_hash_table_foreach(sets, (GHFunc)get_log_set_name, &set_hash_data);
2093 g_hash_table_destroy(sets);
2094 g_completion_add_items(data->completion, item);
2095 g_list_free(item);
2096 }
2097 #endif
2098
2099 static void
2100 screenname_autocomplete_destroyed_cb(GtkWidget *widget, gpointer data)
2101 {
2102 gaim_signals_disconnect_by_handle(widget);
2103 }
2104
2105 static void
2106 repopulate_autocomplete(gpointer something, gpointer data)
2107 {
2108 add_completion_list(data);
2109 }
2110
2111 void
2112 gaim_gtk_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *accountopt, gboolean all)
2113 {
2114 gpointer cb_data = NULL;
2115
2116 #ifdef NEW_STYLE_COMPLETION
2117 /* Store the displayed completion value, the screenname, the UTF-8 normalized & casefolded screenname,
2118 * the UTF-8 normalized & casefolded value for comparison, and the account. */
2119 GtkListStore *store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
2120
2121 GtkEntryCompletion *completion;
2122 gpointer *data;
2123
2124 g_object_set_data(G_OBJECT(store), "screenname-all", GINT_TO_POINTER(all));
2125 add_completion_list(store);
2126
2127 cb_data = store;
2128
2129 /* Sort the completion list by screenname. */
2130 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
2131 1, GTK_SORT_ASCENDING);
2132
2133 completion = gtk_entry_completion_new();
2134 gtk_entry_completion_set_match_func(completion, screenname_completion_match_func, NULL, NULL);
2135
2136 data = g_new0(gpointer, 2);
2137 data[0] = entry;
2138 data[1] = accountopt;
2139 g_signal_connect(G_OBJECT(completion), "match-selected",
2140 G_CALLBACK(screenname_completion_match_selected_cb), data);
2141
2142 gtk_entry_set_completion(GTK_ENTRY(entry), completion);
2143 g_object_unref(completion);
2144
2145 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store));
2146 g_object_unref(store);
2147
2148 gtk_entry_completion_set_text_column(completion, 0);
2149
2150 #else /* !NEW_STYLE_COMPLETION */
2151 GaimGtkCompletionData *data;
2152
2153 data = g_new0(GaimGtkCompletionData, 1);
2154
2155 data->completion = g_completion_new(NULL);
2156 data->all = all;
2157
2158 g_completion_set_compare(data->completion, g_ascii_strncasecmp);
2159
2160 add_completion_list(data);
2161 cb_data = data;
2162
2163 g_signal_connect(G_OBJECT(entry), "event",
2164 G_CALLBACK(completion_entry_event), data);
2165 g_signal_connect(G_OBJECT(entry), "destroy",
2166 G_CALLBACK(destroy_completion_data), data);
2167
2168 #endif /* !NEW_STYLE_COMPLETION */
2169
2170 if (!all)
2171 {
2172 gaim_signal_connect(gaim_connections_get_handle(), "signed-on", entry,
2173 GAIM_CALLBACK(repopulate_autocomplete), cb_data);
2174 gaim_signal_connect(gaim_connections_get_handle(), "signed-off", entry,
2175 GAIM_CALLBACK(repopulate_autocomplete), cb_data);
2176 }
2177
2178 gaim_signal_connect(gaim_accounts_get_handle(), "account-added", entry,
2179 GAIM_CALLBACK(repopulate_autocomplete), cb_data);
2180 gaim_signal_connect(gaim_accounts_get_handle(), "account-removed", entry,
2181 GAIM_CALLBACK(repopulate_autocomplete), cb_data);
2182
2183 g_signal_connect(G_OBJECT(entry), "destroy", G_CALLBACK(screenname_autocomplete_destroyed_cb), NULL);
2184 }
2185
2186 void gaim_gtk_set_cursor(GtkWidget *widget, GdkCursorType cursor_type)
2187 {
2188 GdkCursor *cursor;
2189
2190 g_return_if_fail(widget != NULL);
2191 if (widget->window == NULL)
2192 return;
2193
2194 cursor = gdk_cursor_new(GDK_WATCH);
2195 gdk_window_set_cursor(widget->window, cursor);
2196 gdk_cursor_unref(cursor);
2197
2198 #if GTK_CHECK_VERSION(2,4,0)
2199 gdk_display_flush(gdk_drawable_get_display(GDK_DRAWABLE(widget->window)));
2200 #else
2201 gdk_flush();
2202 #endif
2203 }
2204
2205 void gaim_gtk_clear_cursor(GtkWidget *widget)
2206 {
2207 g_return_if_fail(widget != NULL);
2208 if (widget->window == NULL)
2209 return;
2210
2211 gdk_window_set_cursor(widget->window, NULL);
2212 }
2213
2214 struct _icon_chooser {
2215 GtkWidget *icon_filesel;
2216 GtkWidget *icon_preview;
2217 GtkWidget *icon_text;
2218
2219 void (*callback)(const char*,gpointer);
2220 gpointer data;
2221 };
2222
2223 #if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
2224 static void
2225 icon_filesel_delete_cb(GtkWidget *w, struct _icon_chooser *dialog)
2226 {
2227 if (dialog->icon_filesel != NULL)
2228 gtk_widget_destroy(dialog->icon_filesel);
2229
2230 if (dialog->callback)
2231 dialog->callback(NULL, data);
2232
2233 g_free(dialog);
2234 }
2235 #endif /* FILECHOOSER */
2236
2237
2238
2239 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
2240 static void
2241 icon_filesel_choose_cb(GtkWidget *widget, gint response, struct _icon_chooser *dialog)
2242 {
2243 char *filename, *current_folder;
2244
2245 if (response != GTK_RESPONSE_ACCEPT) {
2246 if (response == GTK_RESPONSE_CANCEL) {
2247 gtk_widget_destroy(dialog->icon_filesel);
2248 }
2249 dialog->icon_filesel = NULL;
2250 if (dialog->callback)
2251 dialog->callback(NULL, dialog->data);
2252 g_free(dialog);
2253 return;
2254 }
2255
2256 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog->icon_filesel));
2257 current_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel));
2258 if (current_folder != NULL) {
2259 gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder);
2260 g_free(current_folder);
2261 }
2262
2263 #else /* FILECHOOSER */
2264 static void
2265 icon_filesel_choose_cb(GtkWidget *w, AccountPrefsDialog *dialog)
2266 {
2267 char *filename, *current_folder;
2268
2269 filename = g_strdup(gtk_file_selection_get_filename(
2270 GTK_FILE_SELECTION(dialog->icon_filesel)));
2271
2272 /* If they typed in a directory, change there */
2273 if (gaim_gtk_check_if_dir(filename,
2274 GTK_FILE_SELECTION(dialog->icon_filesel)))
2275 {
2276 g_free(filename);
2277 return;
2278 }
2279
2280 current_folder = g_path_get_dirname(filename);
2281 if (current_folder != NULL) {
2282 gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder);
2283 g_free(current_folder);
2284 }
2285
2286 #endif /* FILECHOOSER */
2287 if (dialog->callback)
2288 dialog->callback(filename, dialog->data);
2289 gtk_widget_destroy(dialog->icon_filesel);
2290 g_free(filename);
2291 g_free(dialog);
2292 }
2293
2294
2295 static void
2296 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
2297 icon_preview_change_cb(GtkFileChooser *widget, struct _icon_chooser *dialog)
2298 #else /* FILECHOOSER */
2299 icon_preview_change_cb(GtkTreeSelection *sel, struct _icon_chooser *dialog)
2300 #endif /* FILECHOOSER */
2301 {
2302 GdkPixbuf *pixbuf, *scale;
2303 int height, width;
2304 char *basename, *markup, *size;
2305 struct stat st;
2306 char *filename;
2307
2308 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
2309 filename = gtk_file_chooser_get_preview_filename(
2310 GTK_FILE_CHOOSER(dialog->icon_filesel));
2311 #else /* FILECHOOSER */
2312 filename = g_strdup(gtk_file_selection_get_filename(
2313 GTK_FILE_SELECTION(dialog->icon_filesel)));
2314 #endif /* FILECHOOSER */
2315
2316 if (!filename || g_stat(filename, &st))
2317 {
2318 g_free(filename);
2319 return;
2320 }
2321
2322 pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
2323 if (!pixbuf) {
2324 gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL);
2325 gtk_label_set_markup(GTK_LABEL(dialog->icon_text), "");
2326 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
2327 gtk_file_chooser_set_preview_widget_active(
2328 GTK_FILE_CHOOSER(dialog->icon_filesel), FALSE);
2329 #endif /* FILECHOOSER */
2330 g_free(filename);
2331 return;
2332 }
2333
2334 width = gdk_pixbuf_get_width(pixbuf);
2335 height = gdk_pixbuf_get_height(pixbuf);
2336 basename = g_path_get_basename(filename);
2337 size = gaim_str_size_to_units(st.st_size);
2338 markup = g_strdup_printf(_("<b>File:</b> %s\n"
2339 "<b>File size:</b> %s\n"
2340 "<b>Image size:</b> %dx%d"),
2341 basename, size, width, height);
2342
2343 scale = gdk_pixbuf_scale_simple(pixbuf, width * 50 / height,
2344 50, GDK_INTERP_BILINEAR);
2345 gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), scale);
2346 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
2347 gtk_file_chooser_set_preview_widget_active(
2348 GTK_FILE_CHOOSER(dialog->icon_filesel), TRUE);
2349 #endif /* FILECHOOSER */
2350 gtk_label_set_markup(GTK_LABEL(dialog->icon_text), markup);
2351
2352 g_object_unref(G_OBJECT(pixbuf));
2353 g_object_unref(G_OBJECT(scale));
2354 g_free(filename);
2355 g_free(basename);
2356 g_free(size);
2357 g_free(markup);
2358 }
2359
2360
2361 GtkWidget *gaim_gtk_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char *, gpointer), gpointer data) {
2362 struct _icon_chooser *dialog = g_new0(struct _icon_chooser, 1);
2363
2364 #if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
2365 GtkWidget *hbox;
2366 GtkWidget *tv;
2367 GtkTreeSelection *sel;
2368 #endif /* FILECHOOSER */
2369 const char *current_folder;
2370
2371 dialog->callback = callback;
2372 dialog->data = data;
2373
2374 if (dialog->icon_filesel != NULL) {
2375 gtk_window_present(GTK_WINDOW(dialog->icon_filesel));
2376 return NULL;
2377 }
2378
2379 current_folder = gaim_prefs_get_string("/gaim/gtk/filelocations/last_icon_folder");
2380 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
2381
2382 dialog->icon_filesel = gtk_file_chooser_dialog_new(_("Buddy Icon"),
2383 parent,
2384 GTK_FILE_CHOOSER_ACTION_OPEN,
2385 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2386 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2387 NULL);
2388 gtk_dialog_set_default_response(GTK_DIALOG(dialog->icon_filesel), GTK_RESPONSE_ACCEPT);
2389 if ((current_folder != NULL) && (*current_folder != '\0'))
2390 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel),
2391 current_folder);
2392
2393 dialog->icon_preview = gtk_image_new();
2394 dialog->icon_text = gtk_label_new(NULL);
2395 gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50);
2396 gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog->icon_filesel),
2397 GTK_WIDGET(dialog->icon_preview));
2398 g_signal_connect(G_OBJECT(dialog->icon_filesel), "update-preview",
2399 G_CALLBACK(icon_preview_change_cb), dialog);
2400 g_signal_connect(G_OBJECT(dialog->icon_filesel), "response",
2401 G_CALLBACK(icon_filesel_choose_cb), dialog);
2402 icon_preview_change_cb(NULL, dialog);
2403 #else /* FILECHOOSER */
2404 dialog->icon_filesel = gtk_file_selection_new(_("Buddy Icon"));
2405 dialog->icon_preview = gtk_image_new();
2406 dialog->icon_text = gtk_label_new(NULL);
2407 if ((current_folder != NULL) && (*current_folder != '\0'))
2408 gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog->icon_filesel),
2409 current_folder);
2410
2411 gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50);
2412 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE);
2413 gtk_box_pack_start(
2414 GTK_BOX(GTK_FILE_SELECTION(dialog->icon_filesel)->main_vbox),
2415 hbox, FALSE, FALSE, 0);
2416 gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_preview,
2417 FALSE, FALSE, 0);
2418 gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_text, FALSE, FALSE, 0);
2419
2420 tv = GTK_FILE_SELECTION(dialog->icon_filesel)->file_list;
2421 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
2422
2423 g_signal_connect(G_OBJECT(sel), "changed",
2424 G_CALLBACK(icon_preview_change_cb), dialog);
2425 g_signal_connect(
2426 G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->ok_button),
2427 "clicked",
2428 G_CALLBACK(icon_filesel_choose_cb), dialog);
2429 g_signal_connect(
2430 G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->cancel_button),
2431 "clicked",
2432 G_CALLBACK(icon_filesel_delete_cb), dialog);
2433 g_signal_connect(G_OBJECT(dialog->icon_filesel), "destroy",
2434 G_CALLBACK(icon_filesel_delete_cb), dialog);
2435 #endif /* FILECHOOSER */
2436 return dialog->icon_filesel;
2437 }
2438
2439
2440 #if GTK_CHECK_VERSION(2,2,0)
2441 static gboolean
2442 str_array_match(char **a, char **b)
2443 {
2444 int i, j;
2445
2446 if (!a || !b)
2447 return FALSE;
2448 for (i = 0; a[i] != NULL; i++)
2449 for (j = 0; b[j] != NULL; j++)
2450 if (!g_ascii_strcasecmp(a[i], b[j]))
2451 return TRUE;
2452 return FALSE;
2453 }
2454 #endif
2455
2456 char *
2457 gaim_gtk_convert_buddy_icon(GaimPlugin *plugin, const char *path)
2458 {
2459 #if GTK_CHECK_VERSION(2,2,0)
2460 int width, height;
2461 char **pixbuf_formats = NULL;
2462 GdkPixbufFormat *format;
2463 GdkPixbuf *pixbuf;
2464 GaimPluginProtocolInfo *prpl_info;
2465 char **prpl_formats;
2466 #if !GTK_CHECK_VERSION(2,4,0)
2467 GdkPixbufLoader *loader;
2468 FILE *file;
2469 struct stat st;
2470 void *data = NULL;
2471 #endif
2472 #endif
2473 const char *dirname;
2474 char *random;
2475 char *filename;
2476
2477 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
2478
2479 g_return_val_if_fail(prpl_info->icon_spec.format != NULL, NULL);
2480
2481 prpl_formats = g_strsplit(prpl_info->icon_spec.format,",",0);
2482 dirname = gaim_buddy_icons_get_cache_dir();
2483 random = g_strdup_printf("%x", g_random_int());
2484 filename = g_build_filename(dirname, random, NULL);
2485
2486 if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
2487 gaim_debug_info("buddyicon", "Creating icon cache directory.\n");
2488
2489 if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
2490 gaim_debug_error("buddyicon",
2491 "Unable to create directory %s: %s\n",
2492 dirname, strerror(errno));
2493 #if GTK_CHECK_VERSION(2,2,0)
2494 g_strfreev(prpl_formats);
2495 #endif
2496 g_free(random);
2497 g_free(filename);
2498 return NULL;
2499 }
2500 }
2501
2502 #if GTK_CHECK_VERSION(2,2,0)
2503 #if GTK_CHECK_VERSION(2,4,0)
2504 format = gdk_pixbuf_get_file_info (path, &width, &height);
2505 #else
2506 loader = gdk_pixbuf_loader_new();
2507 if (!g_stat(path, &st) && (file = g_fopen(path, "rb")) != NULL) {
2508 data = g_malloc(st.st_size);
2509 fread(data, 1, st.st_size, file);
2510 fclose(file);
2511 gdk_pixbuf_loader_write(loader, data, st.st_size, NULL);
2512 g_free(data);
2513 }
2514 gdk_pixbuf_loader_close(loader, NULL);
2515 pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
2516 width = gdk_pixbuf_get_width(pixbuf);
2517 height = gdk_pixbuf_get_height(pixbuf);
2518 format = gdk_pixbuf_loader_get_format(loader);
2519 g_object_unref(G_OBJECT(loader));
2520 #endif
2521 if (format == NULL)
2522 return NULL;
2523 pixbuf_formats = gdk_pixbuf_format_get_extensions(format);
2524
2525 if (str_array_match(pixbuf_formats, prpl_formats) && /* This is an acceptable format AND */
2526 (!(prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) || /* The prpl doesn't scale before it sends OR */
2527 (prpl_info->icon_spec.min_width <= width &&
2528 prpl_info->icon_spec.max_width >= width &&
2529 prpl_info->icon_spec.min_height <= height &&
2530 prpl_info->icon_spec.max_height >= height))) /* The icon is the correct size */
2531 #endif
2532 {
2533 gchar *contents;
2534 gsize length;
2535 FILE *image;
2536
2537 #if GTK_CHECK_VERSION(2,2,0)
2538 g_strfreev(prpl_formats);
2539 g_strfreev(pixbuf_formats);
2540 #endif
2541
2542 /* Copy the image to the cache folder as "filename". */
2543
2544 if (!g_file_get_contents(path, &contents, &length, NULL) ||
2545 (image = g_fopen(filename, "wb")) == NULL)
2546 {
2547 g_free(random);
2548 g_free(filename);
2549 #if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
2550 g_object_unref(G_OBJECT(pixbuf));
2551 #endif
2552 return NULL;
2553 }
2554
2555 if (fwrite(contents, 1, length, image) != length)
2556 {
2557 fclose(image);
2558 g_unlink(filename);
2559
2560 g_free(random);
2561 g_free(filename);
2562 #if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
2563 g_object_unref(G_OBJECT(pixbuf));
2564 #endif
2565 return NULL;
2566 }
2567 fclose(image);
2568
2569 #if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
2570 g_object_unref(G_OBJECT(pixbuf));
2571 #endif
2572
2573 g_free(filename);
2574 return random;
2575 }
2576 #if GTK_CHECK_VERSION(2,2,0)
2577 else
2578 {
2579 int i;
2580 GError *error = NULL;
2581 GdkPixbuf *scale;
2582 pixbuf = gdk_pixbuf_new_from_file(path, &error);
2583 g_strfreev(pixbuf_formats);
2584 if (!error && (prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) &&
2585 (width < prpl_info->icon_spec.min_width ||
2586 width > prpl_info->icon_spec.max_width ||
2587 height < prpl_info->icon_spec.min_height ||
2588 height > prpl_info->icon_spec.max_height))
2589 {
2590 int new_width = width;
2591 int new_height = height;
2592
2593 if(new_width > prpl_info->icon_spec.max_width)
2594 new_width = prpl_info->icon_spec.max_width;
2595 else if(new_width < prpl_info->icon_spec.min_width)
2596 new_width = prpl_info->icon_spec.min_width;
2597 if(new_height > prpl_info->icon_spec.max_height)
2598 new_height = prpl_info->icon_spec.max_height;
2599 else if(new_height < prpl_info->icon_spec.min_height)
2600 new_height = prpl_info->icon_spec.min_height;
2601
2602 /* preserve aspect ratio */
2603 if ((double)height * (double)new_width >
2604 (double)width * (double)new_height) {
2605 new_width = 0.5 + (double)width * (double)new_height / (double)height;
2606 } else {
2607 new_height = 0.5 + (double)height * (double)new_width / (double)width;
2608 }
2609
2610 scale = gdk_pixbuf_scale_simple (pixbuf, new_width, new_height,
2611 GDK_INTERP_HYPER);
2612 g_object_unref(G_OBJECT(pixbuf));
2613 pixbuf = scale;
2614 }
2615 if (error) {
2616 g_free(random);
2617 g_free(filename);
2618 gaim_debug_error("buddyicon", "Could not open icon for conversion: %s\n", error->message);
2619 g_error_free(error);
2620 g_strfreev(prpl_formats);
2621 return NULL;
2622 }
2623
2624 for (i = 0; prpl_formats[i]; i++) {
2625 gaim_debug_info("buddyicon", "Converting buddy icon to %s as %s\n", prpl_formats[i], filename);
2626 /* The gdk-pixbuf documentation is wrong. gdk_pixbuf_save returns TRUE if it was successful,
2627 * FALSE if an error was set. */
2628 if (gdk_pixbuf_save (pixbuf, filename, prpl_formats[i], &error, NULL) == TRUE)
2629 break;
2630 gaim_debug_warning("buddyicon", "Could not convert to %s: %s\n", prpl_formats[i], error->message);
2631 g_error_free(error);
2632 error = NULL;
2633 }
2634 g_strfreev(prpl_formats);
2635 if (!error) {
2636 g_object_unref(G_OBJECT(pixbuf));
2637 g_free(filename);
2638 return random;
2639 } else {
2640 gaim_debug_error("buddyicon", "Could not convert icon to usable format: %s\n", error->message);
2641 g_error_free(error);
2642 }
2643 g_free(random);
2644 g_free(filename);
2645 g_object_unref(G_OBJECT(pixbuf));
2646 }
2647 return NULL;
2648 #endif
2649 }