pidgin/gtkstatusbox.c

changeset 40676
50284e37698c
parent 40505
e2c4be9e3aa6
child 40682
edbc992e77be
equal deleted inserted replaced
40675:2fcf11e235bd 40676:50284e37698c
65 static void pidgin_status_box_changed(PidginStatusBox *box); 65 static void pidgin_status_box_changed(PidginStatusBox *box);
66 static void pidgin_status_box_get_preferred_height (GtkWidget *widget, 66 static void pidgin_status_box_get_preferred_height (GtkWidget *widget,
67 gint *minimum_height, gint *natural_height); 67 gint *minimum_height, gint *natural_height);
68 static gboolean pidgin_status_box_draw (GtkWidget *widget, cairo_t *cr); 68 static gboolean pidgin_status_box_draw (GtkWidget *widget, cairo_t *cr);
69 static void pidgin_status_box_size_allocate (GtkWidget *widget, GtkAllocation *allocation); 69 static void pidgin_status_box_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
70 static void pidgin_status_box_redisplay_buddy_icon(PidginStatusBox *status_box);
71 static void pidgin_status_box_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); 70 static void pidgin_status_box_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data);
72 static void pidgin_status_box_popup(PidginStatusBox *box, GdkEvent *event); 71 static void pidgin_status_box_popup(PidginStatusBox *box, GdkEvent *event);
73 static void pidgin_status_box_popdown(PidginStatusBox *box, GdkEvent *event); 72 static void pidgin_status_box_popdown(PidginStatusBox *box, GdkEvent *event);
74
75 static void do_colorshift (GdkPixbuf *dest, GdkPixbuf *src, int shift);
76 static void icon_choose_cb(const char *filename, gpointer data);
77 static void remove_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box);
78 static void choose_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box);
79 73
80 enum { 74 enum {
81 /* A PidginStatusBoxItemType */ 75 /* A PidginStatusBoxItemType */
82 TYPE_COLUMN, 76 TYPE_COLUMN,
83 77
123 }; 117 };
124 118
125 enum { 119 enum {
126 PROP_0, 120 PROP_0,
127 PROP_ACCOUNT, 121 PROP_ACCOUNT,
128 PROP_ICON_SEL,
129 }; 122 };
130 123
131 static char *typing_stock_ids[7] = { 124 static char *typing_stock_ids[7] = {
132 PIDGIN_STOCK_ANIMATION_TYPING0, 125 PIDGIN_STOCK_ANIMATION_TYPING0,
133 PIDGIN_STOCK_ANIMATION_TYPING1, 126 PIDGIN_STOCK_ANIMATION_TYPING1,
222 215
223 switch (param_id) { 216 switch (param_id) {
224 case PROP_ACCOUNT: 217 case PROP_ACCOUNT:
225 g_value_set_pointer(value, statusbox->account); 218 g_value_set_pointer(value, statusbox->account);
226 break; 219 break;
227 case PROP_ICON_SEL:
228 g_value_set_boolean(value, statusbox->icon_box != NULL);
229 break;
230 default: 220 default:
231 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, psec); 221 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, psec);
232 break; 222 break;
233 } 223 }
234 } 224 }
289 update_to_reflect_account_status(status_box, account, newstatus); 279 update_to_reflect_account_status(status_box, account, newstatus);
290 else if (status_box->token_status_account == account) 280 else if (status_box->token_status_account == account)
291 status_menu_refresh_iter(status_box, TRUE); 281 status_menu_refresh_iter(status_box, TRUE);
292 } 282 }
293 283
294 static gboolean
295 icon_box_press_cb(GtkWidget *widget, GdkEventButton *event, PidginStatusBox *box)
296 {
297 if (gdk_event_triggers_context_menu((GdkEvent *)event)) {
298 GtkWidget *menu_item;
299 const char *path;
300
301 if (box->icon_box_menu)
302 gtk_widget_destroy(box->icon_box_menu);
303
304 box->icon_box_menu = gtk_menu_new();
305
306 pidgin_new_menu_item(box->icon_box_menu,
307 _("Select Buddy Icon"), GTK_STOCK_ADD,
308 G_CALLBACK(choose_buddy_icon_cb), box);
309
310 menu_item = pidgin_new_menu_item(box->icon_box_menu, _("Remove"), GTK_STOCK_REMOVE,
311 G_CALLBACK(remove_buddy_icon_cb), box);
312 if (!(path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon"))
313 || !*path)
314 gtk_widget_set_sensitive(menu_item, FALSE);
315
316 gtk_menu_popup_at_pointer(GTK_MENU(box->icon_box_menu), (GdkEvent *)event);
317
318 } else {
319 choose_buddy_icon_cb(widget, box);
320 }
321 return FALSE;
322 }
323
324 static void
325 icon_box_dnd_cb(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
326 GtkSelectionData *sd, guint info, guint t, PidginStatusBox *box)
327 {
328 gchar *name = (gchar *) gtk_selection_data_get_data(sd);
329
330 if ((gtk_selection_data_get_length(sd) >= 0)
331 && (gtk_selection_data_get_format(sd) == 8)) {
332 /* Well, it looks like the drag event was cool.
333 * Let's do something with it */
334 if (!g_ascii_strncasecmp(name, "file://", 7)) {
335 GError *converr = NULL;
336 gchar *tmp, *rtmp;
337
338 if(!(tmp = g_filename_from_uri(name, NULL, &converr))) {
339 purple_debug(PURPLE_DEBUG_ERROR, "buddyicon", "%s\n",
340 (converr ? converr->message :
341 "g_filename_from_uri error"));
342 return;
343 }
344 if ((rtmp = strchr(tmp, '\r')) || (rtmp = strchr(tmp, '\n')))
345 *rtmp = '\0';
346 icon_choose_cb(tmp, box);
347 g_free(tmp);
348 }
349 gtk_drag_finish(dc, TRUE, FALSE, t);
350 }
351 gtk_drag_finish(dc, FALSE, FALSE, t);
352 }
353
354 static gboolean
355 icon_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, PidginStatusBox *box)
356 {
357 gdk_window_set_cursor(gtk_widget_get_window(widget), box->hand_cursor);
358 gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon_hover);
359 return FALSE;
360 }
361
362 static gboolean
363 icon_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event, PidginStatusBox *box)
364 {
365 gdk_window_set_cursor(gtk_widget_get_window(widget), box->arrow_cursor);
366 gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon) ;
367 return FALSE;
368 }
369
370
371 static const GtkTargetEntry dnd_targets[] = {
372 {"text/plain", 0, 0},
373 {"text/uri-list", 0, 1},
374 {"STRING", 0, 2}
375 };
376
377 static void
378 setup_icon_box(PidginStatusBox *status_box)
379 {
380 GdkDisplay *display;
381
382 if (status_box->icon_box != NULL)
383 return;
384
385 status_box->icon = gtk_image_new();
386 status_box->icon_box = gtk_event_box_new();
387 gtk_widget_set_parent(status_box->icon_box, GTK_WIDGET(status_box));
388 gtk_widget_show(status_box->icon_box);
389
390 gtk_widget_set_tooltip_text(status_box->icon_box,
391 status_box->account ? _("Click to change your buddyicon for this account.") :
392 _("Click to change your buddyicon for all accounts."));
393
394 if (status_box->account &&
395 !purple_account_get_bool(status_box->account, "use-global-buddyicon", TRUE))
396 {
397 PurpleImage *img = purple_buddy_icons_find_account_icon(status_box->account);
398 pidgin_status_box_set_buddy_icon(status_box, img);
399 g_object_unref(img);
400 }
401 else
402 {
403 const char *filename = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
404 PurpleImage *img = NULL;
405
406 if (filename && *filename)
407 img = purple_image_new_from_file(filename, NULL);
408
409 pidgin_status_box_set_buddy_icon(status_box, img);
410 if (img)
411 g_object_unref(img);
412 }
413
414 display = gtk_widget_get_display(status_box->icon_box);
415 status_box->hand_cursor = gdk_cursor_new_for_display(display, GDK_HAND2);
416 status_box->arrow_cursor = gdk_cursor_new_for_display(display, GDK_LEFT_PTR);
417
418 /* Set up DND */
419 gtk_drag_dest_set(status_box->icon_box,
420 GTK_DEST_DEFAULT_MOTION |
421 GTK_DEST_DEFAULT_DROP,
422 dnd_targets,
423 sizeof(dnd_targets) / sizeof(GtkTargetEntry),
424 GDK_ACTION_COPY);
425
426 g_signal_connect(G_OBJECT(status_box->icon_box), "drag_data_received", G_CALLBACK(icon_box_dnd_cb), status_box);
427 g_signal_connect(G_OBJECT(status_box->icon_box), "enter-notify-event", G_CALLBACK(icon_box_enter_cb), status_box);
428 g_signal_connect(G_OBJECT(status_box->icon_box), "leave-notify-event", G_CALLBACK(icon_box_leave_cb), status_box);
429 g_signal_connect(G_OBJECT(status_box->icon_box), "button-press-event", G_CALLBACK(icon_box_press_cb), status_box);
430
431 gtk_container_add(GTK_CONTAINER(status_box->icon_box), status_box->icon);
432 gtk_widget_show(status_box->icon);
433 }
434
435 static void
436 destroy_icon_box(PidginStatusBox *statusbox)
437 {
438 g_clear_pointer(&statusbox->icon_box, gtk_widget_destroy);
439
440 g_clear_object(&statusbox->hand_cursor);
441 g_clear_object(&statusbox->arrow_cursor);
442
443 g_clear_object(&statusbox->buddy_icon_img);
444
445 g_clear_object(&statusbox->buddy_icon);
446 g_clear_object(&statusbox->buddy_icon_hover);
447
448 g_clear_object(&statusbox->buddy_icon_sel);
449
450 g_clear_pointer(&statusbox->icon_box_menu, gtk_widget_destroy);
451
452 statusbox->icon = NULL;
453 }
454
455 static void 284 static void
456 pidgin_status_box_set_property(GObject *object, guint param_id, 285 pidgin_status_box_set_property(GObject *object, guint param_id,
457 const GValue *value, GParamSpec *pspec) 286 const GValue *value, GParamSpec *pspec)
458 { 287 {
459 PidginStatusBox *statusbox = PIDGIN_STATUS_BOX(object); 288 PidginStatusBox *statusbox = PIDGIN_STATUS_BOX(object);
460 289
461 switch (param_id) { 290 switch (param_id) {
462 case PROP_ICON_SEL:
463 if (g_value_get_boolean(value)) {
464 if (statusbox->account) {
465 PurpleBuddyIconSpec *icon_spec = NULL;
466 PurpleProtocol *protocol =
467 purple_protocols_find(purple_account_get_protocol_id(statusbox->account));
468 if (protocol)
469 icon_spec = purple_protocol_get_icon_spec(protocol);
470 if (icon_spec && icon_spec->format != NULL)
471 setup_icon_box(statusbox);
472 } else {
473 setup_icon_box(statusbox);
474 }
475 } else {
476 destroy_icon_box(statusbox);
477 }
478 break;
479 case PROP_ACCOUNT: 291 case PROP_ACCOUNT:
480 statusbox->account = g_value_get_pointer(value); 292 statusbox->account = g_value_get_pointer(value);
481 if (statusbox->account) 293 if (statusbox->account)
482 statusbox->token_status_account = NULL; 294 statusbox->token_status_account = NULL;
483 else 295 else
491 break; 303 break;
492 } 304 }
493 } 305 }
494 306
495 static void 307 static void
496 pidgin_status_box_dispose(GObject *obj)
497 {
498 PidginStatusBox *statusbox = PIDGIN_STATUS_BOX(obj);
499
500 destroy_icon_box(statusbox);
501 G_OBJECT_CLASS(parent_class)->dispose(obj);
502 }
503
504 static void
505 pidgin_status_box_finalize(GObject *obj) 308 pidgin_status_box_finalize(GObject *obj)
506 { 309 {
507 PidginStatusBox *statusbox = PIDGIN_STATUS_BOX(obj); 310 PidginStatusBox *statusbox = PIDGIN_STATUS_BOX(obj);
508 gsize i; 311 gsize i;
509 312
552 container_class->forall = pidgin_status_box_forall; 355 container_class->forall = pidgin_status_box_forall;
553 container_class->remove = NULL; 356 container_class->remove = NULL;
554 357
555 object_class = (GObjectClass *)klass; 358 object_class = (GObjectClass *)klass;
556 359
557 object_class->dispose = pidgin_status_box_dispose;
558 object_class->finalize = pidgin_status_box_finalize; 360 object_class->finalize = pidgin_status_box_finalize;
559 361
560 object_class->get_property = pidgin_status_box_get_property; 362 object_class->get_property = pidgin_status_box_get_property;
561 object_class->set_property = pidgin_status_box_set_property; 363 object_class->set_property = pidgin_status_box_set_property;
562 364
563 g_object_class_install_property(object_class, 365 g_object_class_install_property(object_class,
564 PROP_ACCOUNT, 366 PROP_ACCOUNT,
565 g_param_spec_pointer("account", 367 g_param_spec_pointer("account",
566 "Account", 368 "Account",
567 "The account, or NULL for all accounts", 369 "The account, or NULL for all accounts",
568 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
569 )
570 );
571 g_object_class_install_property(object_class,
572 PROP_ICON_SEL,
573 g_param_spec_boolean("iconsel",
574 "Icon Selector",
575 "Whether the icon selector should be displayed or not.",
576 FALSE,
577 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS 370 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
578 ) 371 )
579 ); 372 );
580 } 373 }
581 374
1317 if (!box->popup_in_progress) 1110 if (!box->popup_in_progress)
1318 pidgin_status_box_popup(box, (GdkEvent *)event); 1111 pidgin_status_box_popup(box, (GdkEvent *)event);
1319 else 1112 else
1320 pidgin_status_box_popdown(box, (GdkEvent *)event); 1113 pidgin_status_box_popdown(box, (GdkEvent *)event);
1321 return TRUE; 1114 return TRUE;
1322 }
1323
1324 static void
1325 buddy_icon_set_cb(const char *filename, PidginStatusBox *box)
1326 {
1327 PurpleImage *img = NULL;
1328 PurpleBuddyIconSpec *icon_spec = NULL;
1329
1330 if (box->account) {
1331 PurpleProtocol *protocol =
1332 purple_protocols_find(purple_account_get_protocol_id(box->account));
1333 if (protocol)
1334 icon_spec = purple_protocol_get_icon_spec(protocol);
1335 if (icon_spec && icon_spec->format) {
1336 gpointer data = NULL;
1337 size_t len = 0;
1338 if (filename)
1339 data = pidgin_convert_buddy_icon(protocol, filename, &len);
1340 img = purple_buddy_icons_set_account_icon(box->account, data, len);
1341 if (img) {
1342 /*
1343 * set_account_icon doesn't give us a reference, but we
1344 * unref one below (for the other code path)
1345 */
1346 g_object_ref(img);
1347 }
1348
1349 purple_account_set_buddy_icon_path(box->account, filename);
1350
1351 purple_account_set_bool(box->account, "use-global-buddyicon", (filename != NULL));
1352 }
1353 } else {
1354 GList *accounts;
1355 for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
1356 PurpleAccount *account = accounts->data;
1357 PurpleProtocol *protocol =
1358 purple_protocols_find(purple_account_get_protocol_id(account));
1359 if (protocol)
1360 icon_spec = purple_protocol_get_icon_spec(protocol);
1361 if (icon_spec && icon_spec->format &&
1362 purple_account_get_bool(account, "use-global-buddyicon", TRUE)) {
1363 gpointer data = NULL;
1364 size_t len = 0;
1365 if (filename)
1366 data = pidgin_convert_buddy_icon(protocol, filename, &len);
1367 purple_buddy_icons_set_account_icon(account, data, len);
1368 purple_account_set_buddy_icon_path(account, filename);
1369 }
1370 }
1371
1372 /* Even if no accounts were processed, load the icon that was set. */
1373 if (filename != NULL)
1374 img = purple_image_new_from_file(filename, NULL);
1375 }
1376
1377 pidgin_status_box_set_buddy_icon(box, img);
1378 if (img)
1379 g_object_unref(img);
1380 }
1381
1382 static void
1383 remove_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box)
1384 {
1385 if (box->account == NULL)
1386 /* The pref-connect callback does the actual work */
1387 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon", NULL);
1388 else
1389 buddy_icon_set_cb(NULL, box);
1390
1391 gtk_widget_destroy(box->icon_box_menu);
1392 box->icon_box_menu = NULL;
1393 }
1394
1395 static void
1396 choose_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box)
1397 {
1398 if (box->buddy_icon_sel == NULL) {
1399 box->buddy_icon_sel = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(w)), icon_choose_cb, box);
1400 }
1401 gtk_native_dialog_show(GTK_NATIVE_DIALOG(box->buddy_icon_sel));
1402 }
1403
1404 static void
1405 icon_choose_cb(const char *filename, gpointer data)
1406 {
1407 PidginStatusBox *box = data;
1408 if (filename) {
1409 if (box->account == NULL)
1410 /* The pref-connect callback does the actual work */
1411 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon", filename);
1412 else
1413 buddy_icon_set_cb(filename, box);
1414 }
1415
1416 g_clear_object(&box->buddy_icon_sel);
1417 }
1418
1419 static void
1420 update_buddyicon_cb(const char *name, PurplePrefType type,
1421 gconstpointer value, gpointer data)
1422 {
1423 buddy_icon_set_cb(value, (PidginStatusBox*) data);
1424 } 1115 }
1425 1116
1426 static void 1117 static void
1427 treeview_activate_current_selection(PidginStatusBox *status_box, GtkTreePath *path, GdkEvent *event) 1118 treeview_activate_current_selection(PidginStatusBox *status_box, GtkTreePath *path, GdkEvent *event)
1428 { 1119 {
1771 status_box); 1462 status_box);
1772 purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", status_box, 1463 purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", status_box,
1773 PURPLE_CALLBACK(account_status_changed_cb), 1464 PURPLE_CALLBACK(account_status_changed_cb),
1774 status_box); 1465 status_box);
1775 1466
1776 purple_prefs_connect_callback(status_box, PIDGIN_PREFS_ROOT "/accounts/buddyicon",
1777 update_buddyicon_cb, status_box);
1778
1779 g_signal_connect(G_OBJECT(g_network_monitor_get_default()), 1467 g_signal_connect(G_OBJECT(g_network_monitor_get_default()),
1780 "network-changed", 1468 "network-changed",
1781 G_CALLBACK(pidgin_status_box_network_changed_cb), 1469 G_CALLBACK(pidgin_status_box_network_changed_cb),
1782 status_box); 1470 status_box);
1783 } 1471 }
1806 if (box_nat_height > 1) 1494 if (box_nat_height > 1)
1807 *natural_height += box_nat_height + border_width * 2; 1495 *natural_height += box_nat_height + border_width * 2;
1808 } 1496 }
1809 } 1497 }
1810 1498
1811 /* From gnome-panel */
1812 static void
1813 do_colorshift (GdkPixbuf *dest, GdkPixbuf *src, int shift)
1814 {
1815 gint i, j;
1816 gint width, height, has_alpha, srcrowstride, destrowstride;
1817 guchar *target_pixels;
1818 guchar *original_pixels;
1819 guchar *pixsrc;
1820 guchar *pixdest;
1821 int val;
1822 guchar r,g,b;
1823
1824 has_alpha = gdk_pixbuf_get_has_alpha (src);
1825 width = gdk_pixbuf_get_width (src);
1826 height = gdk_pixbuf_get_height (src);
1827 srcrowstride = gdk_pixbuf_get_rowstride (src);
1828 destrowstride = gdk_pixbuf_get_rowstride (dest);
1829 target_pixels = gdk_pixbuf_get_pixels (dest);
1830 original_pixels = gdk_pixbuf_get_pixels (src);
1831
1832 for (i = 0; i < height; i++) {
1833 pixdest = target_pixels + i*destrowstride;
1834 pixsrc = original_pixels + i*srcrowstride;
1835 for (j = 0; j < width; j++) {
1836 r = *(pixsrc++);
1837 g = *(pixsrc++);
1838 b = *(pixsrc++);
1839 val = r + shift;
1840 *(pixdest++) = CLAMP(val, 0, 255);
1841 val = g + shift;
1842 *(pixdest++) = CLAMP(val, 0, 255);
1843 val = b + shift;
1844 *(pixdest++) = CLAMP(val, 0, 255);
1845 if (has_alpha)
1846 *(pixdest++) = *(pixsrc++);
1847 }
1848 }
1849 }
1850
1851 static void 1499 static void
1852 pidgin_status_box_size_allocate(GtkWidget *widget, 1500 pidgin_status_box_size_allocate(GtkWidget *widget,
1853 GtkAllocation *allocation) 1501 GtkAllocation *allocation)
1854 { 1502 {
1855 PidginStatusBox *status_box = PIDGIN_STATUS_BOX(widget); 1503 PidginStatusBox *status_box = PIDGIN_STATUS_BOX(widget);
1856 GtkRequisition req = {0,0}; 1504 GtkRequisition req = {0,0};
1857 GtkAllocation parent_alc, box_alc, icon_alc; 1505 GtkAllocation parent_alc, box_alc;
1858 gint border_width = gtk_container_get_border_width(GTK_CONTAINER (widget)); 1506 gint border_width = gtk_container_get_border_width(GTK_CONTAINER (widget));
1859 1507
1860 gtk_widget_get_preferred_size(status_box->toggle_button, NULL, &req); 1508 gtk_widget_get_preferred_size(status_box->toggle_button, NULL, &req);
1861 /* Make this icon the same size as other buddy icons in the list; unless it already wants to be bigger */ 1509 /* Make this icon the same size as other buddy icons in the list; unless it already wants to be bigger */
1862 1510
1875 parent_alc.height = MAX(1,req.height - (border_width *2)); 1523 parent_alc.height = MAX(1,req.height - (border_width *2));
1876 parent_alc.width -= (border_width * 2); 1524 parent_alc.width -= (border_width * 2);
1877 parent_alc.x += border_width; 1525 parent_alc.x += border_width;
1878 parent_alc.y += border_width; 1526 parent_alc.y += border_width;
1879 1527
1880 if (status_box->icon_box)
1881 {
1882 parent_alc.width -= (parent_alc.height + border_width);
1883 icon_alc = parent_alc;
1884 icon_alc.height = MAX(1, icon_alc.height) - 2;
1885 icon_alc.width = icon_alc.height;
1886 icon_alc.x += allocation->width - (icon_alc.width + border_width + 1);
1887 icon_alc.y += 1;
1888
1889 if (status_box->icon_size != icon_alc.height)
1890 {
1891 status_box->icon_size = icon_alc.height;
1892 pidgin_status_box_redisplay_buddy_icon(status_box);
1893 }
1894 gtk_widget_size_allocate(status_box->icon_box, &icon_alc);
1895 }
1896 gtk_widget_size_allocate(status_box->toggle_button, &parent_alc); 1528 gtk_widget_size_allocate(status_box->toggle_button, &parent_alc);
1897 gtk_widget_set_allocation(GTK_WIDGET(status_box), allocation); 1529 gtk_widget_set_allocation(GTK_WIDGET(status_box), allocation);
1898 } 1530 }
1899 1531
1900 static gboolean 1532 static gboolean
1903 PidginStatusBox *status_box = PIDGIN_STATUS_BOX(widget); 1535 PidginStatusBox *status_box = PIDGIN_STATUS_BOX(widget);
1904 gtk_widget_draw(status_box->toggle_button, cr); 1536 gtk_widget_draw(status_box->toggle_button, cr);
1905 1537
1906 gtk_container_propagate_draw(GTK_CONTAINER(widget), status_box->vbox, cr); 1538 gtk_container_propagate_draw(GTK_CONTAINER(widget), status_box->vbox, cr);
1907 1539
1908 if (status_box->icon_box) {
1909 gtk_container_propagate_draw(GTK_CONTAINER(widget),
1910 status_box->icon_box, cr);
1911
1912 if (status_box->icon_opaque) {
1913 GtkAllocation allocation;
1914 GtkStyleContext *context;
1915
1916 gtk_widget_get_allocation(status_box->icon_box, &allocation);
1917 context = gtk_widget_get_style_context(widget);
1918 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
1919 gtk_render_frame(context, cr, allocation.x-1, allocation.y-1, 34, 34);
1920 }
1921 }
1922 return FALSE; 1540 return FALSE;
1923 } 1541 }
1924 1542
1925 static void 1543 static void
1926 pidgin_status_box_forall(GtkContainer *container, 1544 pidgin_status_box_forall(GtkContainer *container,
1933 if (include_internals) 1551 if (include_internals)
1934 { 1552 {
1935 (* callback) (status_box->vbox, callback_data); 1553 (* callback) (status_box->vbox, callback_data);
1936 (* callback) (status_box->toggle_button, callback_data); 1554 (* callback) (status_box->toggle_button, callback_data);
1937 (* callback) (status_box->arrow, callback_data); 1555 (* callback) (status_box->arrow, callback_data);
1938 if (status_box->icon_box)
1939 (* callback) (status_box->icon_box, callback_data);
1940 } 1556 }
1941 } 1557 }
1942 1558
1943 GtkWidget * 1559 GtkWidget *
1944 pidgin_status_box_new() 1560 pidgin_status_box_new()
2062 return; 1678 return;
2063 status_box->connecting = connecting; 1679 status_box->connecting = connecting;
2064 pidgin_status_box_refresh(status_box); 1680 pidgin_status_box_refresh(status_box);
2065 } 1681 }
2066 1682
2067 static void
2068 pixbuf_size_prepared_cb(GdkPixbufLoader *loader, int width, int height, gpointer data)
2069 {
2070 int w, h;
2071 GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MEDIUM);
2072 gtk_icon_size_lookup(icon_size, &w, &h);
2073 if (height > width)
2074 w = width * h / height;
2075 else if (width > height)
2076 h = height * w / width;
2077 gdk_pixbuf_loader_set_size(loader, w, h);
2078 }
2079
2080 static void
2081 pidgin_status_box_redisplay_buddy_icon(PidginStatusBox *status_box)
2082 {
2083
2084 /* This is sometimes called before the box is shown, and we will not have a size */
2085 if (status_box->icon_size <= 0)
2086 return;
2087
2088 if (status_box->buddy_icon)
2089 g_object_unref(status_box->buddy_icon);
2090 if (status_box->buddy_icon_hover)
2091 g_object_unref(status_box->buddy_icon_hover);
2092 status_box->buddy_icon = NULL;
2093 status_box->buddy_icon_hover = NULL;
2094
2095 if (status_box->buddy_icon_img != NULL)
2096 {
2097 GdkPixbufLoader *loader;
2098 GError *error = NULL;
2099
2100 loader = gdk_pixbuf_loader_new();
2101
2102 g_signal_connect(G_OBJECT(loader), "size-prepared", G_CALLBACK(pixbuf_size_prepared_cb), NULL);
2103 if (!gdk_pixbuf_loader_write(loader,
2104 purple_image_get_data(status_box->buddy_icon_img),
2105 purple_image_get_data_size(status_box->buddy_icon_img),
2106 &error) || error)
2107 {
2108 purple_debug_warning("gtkstatusbox",
2109 "gdk_pixbuf_loader_write() failed with size=%"
2110 G_GSIZE_FORMAT ": %s", purple_image_get_data_size(
2111 status_box->buddy_icon_img),
2112 error ? error->message : "(no error message)");
2113 if (error)
2114 g_error_free(error);
2115 } else if (!gdk_pixbuf_loader_close(loader, &error) || error) {
2116 purple_debug_warning("gtkstatusbox",
2117 "gdk_pixbuf_loader_close() failed for image of "
2118 "size %" G_GSIZE_FORMAT ": %s",
2119 purple_image_get_data_size(status_box->buddy_icon_img),
2120 error ? error->message : "(no error message)");
2121 if (error)
2122 g_error_free(error);
2123 } else {
2124 GdkPixbuf *buf, *scale;
2125 int scale_width, scale_height;
2126
2127 buf = gdk_pixbuf_loader_get_pixbuf(loader);
2128 scale_width = gdk_pixbuf_get_width(buf);
2129 scale_height = gdk_pixbuf_get_height(buf);
2130 scale = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height);
2131 gdk_pixbuf_fill(scale, 0x00000000);
2132 gdk_pixbuf_copy_area(buf, 0, 0, scale_width, scale_height, scale, 0, 0);
2133 if (pidgin_gdk_pixbuf_is_opaque(scale))
2134 pidgin_gdk_pixbuf_make_round(scale);
2135 status_box->buddy_icon = scale;
2136 }
2137
2138 g_object_unref(loader);
2139 }
2140
2141 if (status_box->buddy_icon == NULL)
2142 {
2143 /* Show a placeholder icon */
2144 GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL);
2145 status_box->buddy_icon = gtk_widget_render_icon(GTK_WIDGET(status_box),
2146 PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR,
2147 icon_size, "PidginStatusBox");
2148 }
2149
2150 if (status_box->buddy_icon != NULL) {
2151 status_box->icon_opaque = pidgin_gdk_pixbuf_is_opaque(status_box->buddy_icon);
2152 gtk_image_set_from_pixbuf(GTK_IMAGE(status_box->icon), status_box->buddy_icon);
2153 status_box->buddy_icon_hover = gdk_pixbuf_copy(status_box->buddy_icon);
2154 do_colorshift(status_box->buddy_icon_hover, status_box->buddy_icon_hover, 32);
2155 gtk_widget_queue_resize(GTK_WIDGET(status_box));
2156 }
2157 }
2158
2159 void
2160 pidgin_status_box_set_buddy_icon(PidginStatusBox *status_box, PurpleImage *img)
2161 {
2162 if (status_box->buddy_icon_img)
2163 g_object_unref(status_box->buddy_icon_img);
2164 status_box->buddy_icon_img = img;
2165 if (status_box->buddy_icon_img != NULL)
2166 g_object_ref(status_box->buddy_icon_img);
2167
2168 pidgin_status_box_redisplay_buddy_icon(status_box);
2169 }
2170
2171 void 1683 void
2172 pidgin_status_box_pulse_connecting(PidginStatusBox *status_box) 1684 pidgin_status_box_pulse_connecting(PidginStatusBox *status_box)
2173 { 1685 {
2174 if (!status_box) 1686 if (!status_box)
2175 return; 1687 return;

mercurial