| 3328 gtkblist->mouseover_contact = NULL; |
3235 gtkblist->mouseover_contact = NULL; |
| 3329 } |
3236 } |
| 3330 return FALSE; |
3237 return FALSE; |
| 3331 } |
3238 } |
| 3332 |
3239 |
| 3333 static void |
|
| 3334 toggle_debug(void) |
|
| 3335 { |
|
| 3336 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/enabled", |
|
| 3337 !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")); |
|
| 3338 } |
|
| 3339 |
|
| 3340 static char *get_mood_icon_path(const char *mood) |
|
| 3341 { |
|
| 3342 char *path; |
|
| 3343 |
|
| 3344 if (purple_strequal(mood, "busy")) { |
|
| 3345 path = g_build_filename(PURPLE_DATADIR, "pidgin", "icons", |
|
| 3346 "hicolor", "16x16", "status", "user-busy.png", NULL); |
|
| 3347 } else if (purple_strequal(mood, "hiptop")) { |
|
| 3348 path = g_build_filename(PURPLE_DATADIR, "pidgin", "icons", |
|
| 3349 "hicolor", "16x16", "emblems", "emblem-hiptop.png", |
|
| 3350 NULL); |
|
| 3351 } else { |
|
| 3352 char *filename = g_strdup_printf("%s.png", mood); |
|
| 3353 path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin", |
|
| 3354 "emotes", "small", filename, NULL); |
|
| 3355 g_free(filename); |
|
| 3356 } |
|
| 3357 return path; |
|
| 3358 } |
|
| 3359 |
|
| 3360 static void |
|
| 3361 update_status_with_mood(PurpleAccount *account, const gchar *mood, |
|
| 3362 const gchar *text) |
|
| 3363 { |
|
| 3364 if (mood && *mood) { |
|
| 3365 if (text) { |
|
| 3366 purple_account_set_status(account, "mood", TRUE, |
|
| 3367 PURPLE_MOOD_NAME, mood, |
|
| 3368 PURPLE_MOOD_COMMENT, text, |
|
| 3369 NULL); |
|
| 3370 } else { |
|
| 3371 purple_account_set_status(account, "mood", TRUE, |
|
| 3372 PURPLE_MOOD_NAME, mood, |
|
| 3373 NULL); |
|
| 3374 } |
|
| 3375 } else { |
|
| 3376 purple_account_set_status(account, "mood", FALSE, NULL); |
|
| 3377 } |
|
| 3378 } |
|
| 3379 |
|
| 3380 static void |
|
| 3381 edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields) |
|
| 3382 { |
|
| 3383 PurpleRequestField *mood_field; |
|
| 3384 GList *l; |
|
| 3385 |
|
| 3386 mood_field = purple_request_fields_get_field(fields, "mood"); |
|
| 3387 l = purple_request_field_list_get_selected(mood_field); |
|
| 3388 |
|
| 3389 if (l) { |
|
| 3390 const char *mood = purple_request_field_list_get_data(mood_field, l->data); |
|
| 3391 |
|
| 3392 if (gc) { |
|
| 3393 const char *text; |
|
| 3394 PurpleAccount *account = purple_connection_get_account(gc); |
|
| 3395 |
|
| 3396 if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES) { |
|
| 3397 PurpleRequestField *text_field; |
|
| 3398 text_field = purple_request_fields_get_field(fields, "text"); |
|
| 3399 text = purple_request_field_string_get_value(text_field); |
|
| 3400 } else { |
|
| 3401 text = NULL; |
|
| 3402 } |
|
| 3403 |
|
| 3404 update_status_with_mood(account, mood, text); |
|
| 3405 } else { |
|
| 3406 GList *accounts = purple_accounts_get_all_active(); |
|
| 3407 |
|
| 3408 for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) { |
|
| 3409 PurpleAccount *account = (PurpleAccount *) accounts->data; |
|
| 3410 PurpleConnection *gc = purple_account_get_connection(account); |
|
| 3411 |
|
| 3412 if (gc && (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) { |
|
| 3413 update_status_with_mood(account, mood, NULL); |
|
| 3414 } |
|
| 3415 } |
|
| 3416 } |
|
| 3417 } |
|
| 3418 } |
|
| 3419 |
|
| 3420 static void |
|
| 3421 global_moods_for_each(gpointer key, gpointer value, gpointer user_data) |
|
| 3422 { |
|
| 3423 GList **out_moods = (GList **) user_data; |
|
| 3424 PurpleMood *mood = (PurpleMood *) value; |
|
| 3425 |
|
| 3426 *out_moods = g_list_append(*out_moods, mood); |
|
| 3427 } |
|
| 3428 |
|
| 3429 static PurpleMood * |
|
| 3430 get_global_moods(void) |
|
| 3431 { |
|
| 3432 GHashTable *global_moods = |
|
| 3433 g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); |
|
| 3434 GHashTable *mood_counts = |
|
| 3435 g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); |
|
| 3436 GList *accounts = purple_accounts_get_all_active(); |
|
| 3437 PurpleMood *result = NULL; |
|
| 3438 GList *out_moods = NULL; |
|
| 3439 int i = 0; |
|
| 3440 int num_accounts = 0; |
|
| 3441 |
|
| 3442 for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) { |
|
| 3443 PurpleAccount *account = (PurpleAccount *) accounts->data; |
|
| 3444 if (purple_account_is_connected(account)) { |
|
| 3445 PurpleConnection *gc = purple_account_get_connection(account); |
|
| 3446 |
|
| 3447 if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS) { |
|
| 3448 PurpleProtocol *protocol = purple_connection_get_protocol(gc); |
|
| 3449 PurpleMood *mood = NULL; |
|
| 3450 |
|
| 3451 for (mood = purple_protocol_client_iface_get_moods(protocol, account) ; |
|
| 3452 mood->mood != NULL ; mood++) { |
|
| 3453 int mood_count = |
|
| 3454 GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood)); |
|
| 3455 |
|
| 3456 if (!g_hash_table_lookup(global_moods, mood->mood)) { |
|
| 3457 g_hash_table_insert(global_moods, (gpointer)mood->mood, mood); |
|
| 3458 } |
|
| 3459 g_hash_table_insert(mood_counts, (gpointer)mood->mood, |
|
| 3460 GINT_TO_POINTER(mood_count + 1)); |
|
| 3461 } |
|
| 3462 |
|
| 3463 num_accounts++; |
|
| 3464 } |
|
| 3465 } |
|
| 3466 } |
|
| 3467 |
|
| 3468 g_hash_table_foreach(global_moods, global_moods_for_each, &out_moods); |
|
| 3469 result = g_new0(PurpleMood, g_hash_table_size(global_moods) + 1); |
|
| 3470 |
|
| 3471 while (out_moods) { |
|
| 3472 PurpleMood *mood = (PurpleMood *) out_moods->data; |
|
| 3473 int in_num_accounts = |
|
| 3474 GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood)); |
|
| 3475 |
|
| 3476 if (in_num_accounts == num_accounts) { |
|
| 3477 /* mood is present in all accounts supporting moods */ |
|
| 3478 result[i].mood = mood->mood; |
|
| 3479 result[i].description = mood->description; |
|
| 3480 i++; |
|
| 3481 } |
|
| 3482 out_moods = g_list_delete_link(out_moods, out_moods); |
|
| 3483 } |
|
| 3484 |
|
| 3485 g_hash_table_destroy(global_moods); |
|
| 3486 g_hash_table_destroy(mood_counts); |
|
| 3487 |
|
| 3488 return result; |
|
| 3489 } |
|
| 3490 |
|
| 3491 /* get current set mood for all mood-supporting accounts, or NULL if not set |
|
| 3492 or not set to the same on all */ |
|
| 3493 static const gchar * |
|
| 3494 get_global_mood_status(void) |
|
| 3495 { |
|
| 3496 GList *accounts = purple_accounts_get_all_active(); |
|
| 3497 const gchar *found_mood = NULL; |
|
| 3498 |
|
| 3499 for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) { |
|
| 3500 PurpleAccount *account = (PurpleAccount *) accounts->data; |
|
| 3501 |
|
| 3502 if (purple_account_is_connected(account) && |
|
| 3503 (purple_connection_get_flags(purple_account_get_connection(account)) & |
|
| 3504 PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) { |
|
| 3505 PurplePresence *presence = purple_account_get_presence(account); |
|
| 3506 PurpleStatus *status = purple_presence_get_status(presence, "mood"); |
|
| 3507 const gchar *curr_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); |
|
| 3508 |
|
| 3509 if (found_mood != NULL && !purple_strequal(curr_mood, found_mood)) { |
|
| 3510 /* found a different mood */ |
|
| 3511 found_mood = NULL; |
|
| 3512 break; |
|
| 3513 } else { |
|
| 3514 found_mood = curr_mood; |
|
| 3515 } |
|
| 3516 } |
|
| 3517 } |
|
| 3518 |
|
| 3519 return found_mood; |
|
| 3520 } |
|
| 3521 |
|
| 3522 static void |
|
| 3523 set_mood_cb(GtkWidget *widget, PurpleAccount *account) |
|
| 3524 { |
|
| 3525 const char *current_mood; |
|
| 3526 PurpleRequestFields *fields; |
|
| 3527 PurpleRequestFieldGroup *g; |
|
| 3528 PurpleRequestField *f; |
|
| 3529 PurpleConnection *gc = NULL; |
|
| 3530 PurpleProtocol *protocol = NULL; |
|
| 3531 PurpleMood *mood; |
|
| 3532 PurpleMood *global_moods = NULL; |
|
| 3533 |
|
| 3534 if (account) { |
|
| 3535 PurplePresence *presence = purple_account_get_presence(account); |
|
| 3536 PurpleStatus *status = purple_presence_get_status(presence, "mood"); |
|
| 3537 gc = purple_account_get_connection(account); |
|
| 3538 g_return_if_fail(purple_connection_get_protocol(gc) != NULL); |
|
| 3539 protocol = purple_connection_get_protocol(gc); |
|
| 3540 current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); |
|
| 3541 } else { |
|
| 3542 current_mood = get_global_mood_status(); |
|
| 3543 } |
|
| 3544 |
|
| 3545 fields = purple_request_fields_new(); |
|
| 3546 g = purple_request_field_group_new(NULL); |
|
| 3547 f = purple_request_field_list_new("mood", _("Please select your mood from the list")); |
|
| 3548 |
|
| 3549 purple_request_field_list_add_icon(f, _("None"), NULL, ""); |
|
| 3550 if (current_mood == NULL) |
|
| 3551 purple_request_field_list_add_selected(f, _("None")); |
|
| 3552 |
|
| 3553 /* TODO: rlaager wants this sorted. */ |
|
| 3554 /* TODO: darkrain wants it sorted post-translation */ |
|
| 3555 if (account && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods)) { |
|
| 3556 mood = purple_protocol_client_iface_get_moods(protocol, account); |
|
| 3557 } else { |
|
| 3558 mood = global_moods = get_global_moods(); |
|
| 3559 } |
|
| 3560 for ( ; mood->mood != NULL ; mood++) { |
|
| 3561 char *path; |
|
| 3562 |
|
| 3563 if (mood->description == NULL) { |
|
| 3564 continue; |
|
| 3565 } |
|
| 3566 |
|
| 3567 path = get_mood_icon_path(mood->mood); |
|
| 3568 purple_request_field_list_add_icon(f, _(mood->description), |
|
| 3569 path, (gpointer)mood->mood); |
|
| 3570 g_free(path); |
|
| 3571 |
|
| 3572 if (current_mood && purple_strequal(current_mood, mood->mood)) |
|
| 3573 purple_request_field_list_add_selected(f, _(mood->description)); |
|
| 3574 } |
|
| 3575 purple_request_field_group_add_field(g, f); |
|
| 3576 |
|
| 3577 purple_request_fields_add_group(fields, g); |
|
| 3578 |
|
| 3579 /* if the connection allows setting a mood message */ |
|
| 3580 if (gc && (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES)) { |
|
| 3581 g = purple_request_field_group_new(NULL); |
|
| 3582 f = purple_request_field_string_new("text", |
|
| 3583 _("Message (optional)"), NULL, FALSE); |
|
| 3584 purple_request_field_group_add_field(g, f); |
|
| 3585 purple_request_fields_add_group(fields, g); |
|
| 3586 } |
|
| 3587 |
|
| 3588 purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"), |
|
| 3589 NULL, fields, |
|
| 3590 _("OK"), G_CALLBACK(edit_mood_cb), |
|
| 3591 _("Cancel"), NULL, |
|
| 3592 purple_request_cpar_from_connection(gc), gc); |
|
| 3593 |
|
| 3594 g_free(global_moods); |
|
| 3595 } |
|
| 3596 |
|
| 3597 static void |
|
| 3598 set_mood_show(void) |
|
| 3599 { |
|
| 3600 set_mood_cb(NULL, NULL); |
|
| 3601 } |
|
| 3602 |
|
| 3603 /*************************************************** |
|
| 3604 * Crap * |
|
| 3605 ***************************************************/ |
|
| 3606 static void |
|
| 3607 pidgin_blist_plugins_dialog_cb(GtkAction *action, GtkWidget *window) { |
|
| 3608 GtkWidget *dialog = pidgin_plugins_dialog_new(); |
|
| 3609 |
|
| 3610 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window)); |
|
| 3611 |
|
| 3612 gtk_widget_show_all(dialog); |
|
| 3613 } |
|
| 3614 |
|
| 3615 static void |
|
| 3616 _pidgin_about_cb(GtkAction *action, GtkWidget *window) { |
|
| 3617 GtkWidget *about = pidgin_about_dialog_new(); |
|
| 3618 |
|
| 3619 gtk_window_set_transient_for(GTK_WINDOW(about), GTK_WINDOW(window)); |
|
| 3620 |
|
| 3621 gtk_widget_show_all(about); |
|
| 3622 } |
|
| 3623 |
|
| 3624 /* TODO: fill out tooltips... */ |
|
| 3625 static const GtkActionEntry blist_menu_entries[] = { |
|
| 3626 /* NOTE: Do not set any accelerator to Control+O. It is mapped by |
|
| 3627 gtk_blist_key_press_cb to "Get User Info" on the selected buddy. */ |
|
| 3628 /* Buddies menu */ |
|
| 3629 { "BuddiesMenu", NULL, N_("_Buddies"), NULL, NULL, NULL }, |
|
| 3630 { "NewInstantMessage", PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, N_("New Instant _Message..."), "<control>M", NULL, pidgin_dialogs_im }, |
|
| 3631 { "JoinAChat", PIDGIN_STOCK_CHAT, N_("Join a _Chat..."), "<control>C", NULL, pidgin_blist_joinchat_show }, |
|
| 3632 { "GetUserInfo", PIDGIN_STOCK_TOOLBAR_USER_INFO, N_("Get User _Info..."), "<control>I", NULL, pidgin_dialogs_info }, |
|
| 3633 { "ViewUserLog", NULL, N_("View User _Log..."), "<control>L", NULL, pidgin_dialogs_log }, |
|
| 3634 { "ShowMenu", NULL, N_("Sh_ow"), NULL, NULL, NULL }, |
|
| 3635 { "SortMenu", NULL, N_("_Sort Buddies"), NULL, NULL, NULL }, |
|
| 3636 { "AddBuddy", GTK_STOCK_ADD, N_("_Add Buddy..."), "<control>B", NULL, pidgin_blist_add_buddy_cb }, |
|
| 3637 { "AddChat", GTK_STOCK_ADD, N_("Add C_hat..."), NULL, NULL, pidgin_blist_add_chat_cb }, |
|
| 3638 { "AddGroup", GTK_STOCK_ADD, N_("Add _Group..."), NULL, NULL, purple_blist_request_add_group }, |
|
| 3639 { "Quit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q", NULL, purple_core_quit }, |
|
| 3640 |
|
| 3641 /* Accounts menu */ |
|
| 3642 { "AccountsMenu", NULL, N_("_Accounts"), NULL, NULL, NULL }, |
|
| 3643 { "ManageAccounts", NULL, N_("Manage Accounts"), "<control>A", NULL, pidgin_accounts_window_show }, |
|
| 3644 |
|
| 3645 /* Tools */ |
|
| 3646 { "ToolsMenu", NULL, N_("_Tools"), NULL, NULL, NULL }, |
|
| 3647 { "BuddyPounces", NULL, N_("Buddy _Pounces"), NULL, NULL, pidgin_pounces_manager_show }, |
|
| 3648 { "CustomSmileys", PIDGIN_STOCK_TOOLBAR_SMILEY, N_("Custom Smile_ys"), "<control>Y", NULL, pidgin_smiley_manager_show }, |
|
| 3649 { "Plugins", PIDGIN_STOCK_TOOLBAR_PLUGINS, N_("Plu_gins"), "<control>U", NULL, pidgin_blist_plugins_dialog_cb }, |
|
| 3650 { "Preferences", GTK_STOCK_PREFERENCES, N_("Pr_eferences"), "<control>P", NULL, pidgin_prefs_show }, |
|
| 3651 { "Privacy", NULL, N_("Pr_ivacy"), NULL, NULL, pidgin_privacy_dialog_show }, |
|
| 3652 { "SetMood", NULL, N_("Set _Mood"), "<control>D", NULL, set_mood_show }, |
|
| 3653 { "FileTransfers", PIDGIN_STOCK_TOOLBAR_TRANSFER, N_("_File Transfers"), "<control>T", NULL, G_CALLBACK(gtk_blist_show_xfer_dialog_cb) }, |
|
| 3654 { "RoomList", NULL, N_("R_oom List"), NULL, NULL, pidgin_roomlist_dialog_show }, |
|
| 3655 { "SystemLog", NULL, N_("System _Log"), NULL, NULL, gtk_blist_show_systemlog_cb }, |
|
| 3656 |
|
| 3657 /* Help */ |
|
| 3658 { "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL }, |
|
| 3659 { "OnlineHelp", GTK_STOCK_HELP, N_("Online _Help"), "F1", NULL, gtk_blist_show_onlinehelp_cb }, |
|
| 3660 { "DebugWindow", NULL, N_("_Debug Window"), NULL, NULL, toggle_debug }, |
|
| 3661 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, G_CALLBACK(_pidgin_about_cb) }, |
|
| 3662 }; |
|
| 3663 |
|
| 3664 /* Toggle items */ |
|
| 3665 static const GtkToggleActionEntry blist_menu_toggle_entries[] = { |
|
| 3666 /* Buddies->Show menu */ |
|
| 3667 { "ShowOffline", NULL, N_("_Offline Buddies"), NULL, NULL, G_CALLBACK(pidgin_blist_edit_mode_cb), FALSE }, |
|
| 3668 { "ShowEmptyGroups", NULL, N_("_Empty Groups"), NULL, NULL, G_CALLBACK(pidgin_blist_show_empty_groups_cb), FALSE }, |
|
| 3669 { "ShowBuddyDetails", NULL, N_("Buddy _Details"), NULL, NULL, G_CALLBACK(pidgin_blist_buddy_details_cb), FALSE }, |
|
| 3670 { "ShowIdleTimes", NULL, N_("Idle _Times"), NULL, NULL, G_CALLBACK(pidgin_blist_show_idle_time_cb), FALSE }, |
|
| 3671 { "ShowProtocolIcons", NULL, N_("_Protocol Icons"), NULL, NULL, G_CALLBACK(pidgin_blist_show_protocol_icons_cb), FALSE }, |
|
| 3672 |
|
| 3673 /* Tools menu */ |
|
| 3674 { "MuteSounds", NULL, N_("Mute _Sounds"), NULL, NULL, G_CALLBACK(pidgin_blist_mute_sounds_cb), FALSE }, |
|
| 3675 }; |
|
| 3676 |
|
| 3677 static const char *blist_menu = |
|
| 3678 "<ui>" |
|
| 3679 "<menubar name='BList'>" |
|
| 3680 "<menu action='BuddiesMenu'>" |
|
| 3681 "<menuitem action='NewInstantMessage'/>" |
|
| 3682 "<menuitem action='JoinAChat'/>" |
|
| 3683 "<menuitem action='GetUserInfo'/>" |
|
| 3684 "<menuitem action='ViewUserLog'/>" |
|
| 3685 "<separator/>" |
|
| 3686 "<menu action='ShowMenu'>" |
|
| 3687 "<menuitem action='ShowOffline'/>" |
|
| 3688 "<menuitem action='ShowEmptyGroups'/>" |
|
| 3689 "<menuitem action='ShowBuddyDetails'/>" |
|
| 3690 "<menuitem action='ShowIdleTimes'/>" |
|
| 3691 "<menuitem action='ShowProtocolIcons'/>" |
|
| 3692 "</menu>" |
|
| 3693 "<menu action='SortMenu'/>" |
|
| 3694 "<separator/>" |
|
| 3695 "<menuitem action='AddBuddy'/>" |
|
| 3696 "<menuitem action='AddChat'/>" |
|
| 3697 "<menuitem action='AddGroup'/>" |
|
| 3698 "<separator/>" |
|
| 3699 "<menuitem action='Quit'/>" |
|
| 3700 "</menu>" |
|
| 3701 "<menu action='AccountsMenu'>" |
|
| 3702 "<menuitem action='ManageAccounts'/>" |
|
| 3703 "</menu>" |
|
| 3704 "<menu action='ToolsMenu'>" |
|
| 3705 "<menuitem action='BuddyPounces'/>" |
|
| 3706 "<menuitem action='CustomSmileys'/>" |
|
| 3707 "<menuitem action='Plugins'/>" |
|
| 3708 "<menuitem action='Preferences'/>" |
|
| 3709 "<menuitem action='Privacy'/>" |
|
| 3710 "<menuitem action='SetMood'/>" |
|
| 3711 "<separator/>" |
|
| 3712 "<menuitem action='FileTransfers'/>" |
|
| 3713 "<menuitem action='RoomList'/>" |
|
| 3714 "<menuitem action='SystemLog'/>" |
|
| 3715 "<separator/>" |
|
| 3716 "<menuitem action='MuteSounds'/>" |
|
| 3717 "<placeholder name='PluginActions'/>" |
|
| 3718 "</menu>" |
|
| 3719 "<menu action='HelpMenu'>" |
|
| 3720 "<menuitem action='OnlineHelp'/>" |
|
| 3721 "<separator/>" |
|
| 3722 "<menuitem action='DebugWindow'/>" |
|
| 3723 "<separator/>" |
|
| 3724 "<menuitem action='About'/>" |
|
| 3725 "</menu>" |
|
| 3726 "</menubar>" |
|
| 3727 "</ui>"; |
|
| 3728 |
|
| 3729 /********************************************************* |
3240 /********************************************************* |
| 3730 * Private Utility functions * |
3241 * Private Utility functions * |
| 3731 *********************************************************/ |
3242 *********************************************************/ |
| 3732 |
3243 |
| 3733 static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full) |
3244 static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full) |
| 7873 gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); |
7238 gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); |
| 7874 return; |
7239 return; |
| 7875 } |
7240 } |
| 7876 } |
7241 } |
| 7877 |
7242 |
| 7878 static void |
|
| 7879 plugin_act(GSimpleAction *action, GVariant *param, PurplePluginAction *pam) |
|
| 7880 { |
|
| 7881 if (pam && pam->callback) |
|
| 7882 pam->callback(pam); |
|
| 7883 } |
|
| 7884 |
|
| 7885 static GtkWidget * |
|
| 7886 build_plugin_actions(GActionMap *action_map, const gchar *parent, |
|
| 7887 PurplePlugin *plugin) |
|
| 7888 { |
|
| 7889 GMenu *menu = NULL; |
|
| 7890 GMenu *section = NULL; |
|
| 7891 PurplePluginActionsCb actions_cb; |
|
| 7892 PurplePluginAction *action = NULL; |
|
| 7893 GList *actions, *l; |
|
| 7894 char *name; |
|
| 7895 int count = 0; |
|
| 7896 GtkWidget *ret; |
|
| 7897 |
|
| 7898 actions_cb = |
|
| 7899 purple_plugin_info_get_actions_cb(purple_plugin_get_info(plugin)); |
|
| 7900 |
|
| 7901 actions = actions_cb(plugin); |
|
| 7902 |
|
| 7903 if (actions == NULL) { |
|
| 7904 return NULL; |
|
| 7905 } |
|
| 7906 |
|
| 7907 menu = g_menu_new(); |
|
| 7908 |
|
| 7909 for (l = actions; l != NULL; l = l->next) { |
|
| 7910 GAction *menuaction; |
|
| 7911 |
|
| 7912 action = (PurplePluginAction *)l->data; |
|
| 7913 |
|
| 7914 if (action == NULL) { |
|
| 7915 if (section != NULL) { |
|
| 7916 /* Close and append section if any */ |
|
| 7917 g_menu_append_section(menu, NULL, |
|
| 7918 G_MENU_MODEL(section)); |
|
| 7919 g_clear_object(§ion); |
|
| 7920 } |
|
| 7921 |
|
| 7922 continue; |
|
| 7923 } |
|
| 7924 |
|
| 7925 action->plugin = plugin; |
|
| 7926 |
|
| 7927 name = g_strdup_printf("plugin.%s-action-%d", parent, count++); |
|
| 7928 /* +7 to skip "plugin." prefix */ |
|
| 7929 menuaction = G_ACTION(g_simple_action_new(name + 7, NULL)); |
|
| 7930 g_signal_connect_data(G_OBJECT(menuaction), "activate", |
|
| 7931 G_CALLBACK(plugin_act), action, |
|
| 7932 (GClosureNotify)purple_plugin_action_free, 0); |
|
| 7933 g_action_map_add_action(action_map, menuaction); |
|
| 7934 g_object_unref(menuaction); |
|
| 7935 |
|
| 7936 if (section == NULL) { |
|
| 7937 section = g_menu_new(); |
|
| 7938 } |
|
| 7939 |
|
| 7940 g_menu_append(section, action->label, name); |
|
| 7941 g_free(name); |
|
| 7942 } |
|
| 7943 |
|
| 7944 if (section != NULL) { |
|
| 7945 /* Close and append final section if any */ |
|
| 7946 g_menu_append_section(menu, NULL, G_MENU_MODEL(section)); |
|
| 7947 g_clear_object(§ion); |
|
| 7948 } |
|
| 7949 |
|
| 7950 g_list_free(actions); |
|
| 7951 |
|
| 7952 ret = gtk_menu_new_from_model(G_MENU_MODEL(menu)); |
|
| 7953 g_object_unref(menu); |
|
| 7954 return ret; |
|
| 7955 } |
|
| 7956 |
|
| 7957 |
|
| 7958 static void |
|
| 7959 modify_account_cb(GtkWidget *widget, gpointer data) |
|
| 7960 { |
|
| 7961 pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, data); |
|
| 7962 } |
|
| 7963 |
|
| 7964 static void |
|
| 7965 enable_account_cb(GtkCheckMenuItem *widget, gpointer data) |
|
| 7966 { |
|
| 7967 PurpleAccount *account = data; |
|
| 7968 const PurpleSavedStatus *saved_status; |
|
| 7969 |
|
| 7970 saved_status = purple_savedstatus_get_current(); |
|
| 7971 purple_savedstatus_activate_for_account(saved_status, account); |
|
| 7972 |
|
| 7973 purple_account_set_enabled(account, PIDGIN_UI, TRUE); |
|
| 7974 } |
|
| 7975 |
|
| 7976 static void |
|
| 7977 disable_account_cb(GtkCheckMenuItem *widget, gpointer data) |
|
| 7978 { |
|
| 7979 PurpleAccount *account = data; |
|
| 7980 |
|
| 7981 purple_account_set_enabled(account, PIDGIN_UI, FALSE); |
|
| 7982 } |
|
| 7983 |
|
| 7984 static void |
|
| 7985 protocol_act(GtkWidget *obj, PurpleProtocolAction *pam) |
|
| 7986 { |
|
| 7987 if (pam && pam->callback) |
|
| 7988 pam->callback(pam); |
|
| 7989 } |
|
| 7990 |
|
| 7991 void |
|
| 7992 pidgin_blist_update_accounts_menu(void) |
|
| 7993 { |
|
| 7994 GtkWidget *menuitem, *submenu; |
|
| 7995 GtkAccelGroup *accel_group; |
|
| 7996 GList *l, *accounts; |
|
| 7997 gboolean disabled_accounts = FALSE; |
|
| 7998 gboolean enabled_accounts = FALSE; |
|
| 7999 |
|
| 8000 if (accountmenu == NULL) |
|
| 8001 return; |
|
| 8002 |
|
| 8003 /* Clear the old Accounts menu */ |
|
| 8004 for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = g_list_delete_link(l, l)) { |
|
| 8005 menuitem = l->data; |
|
| 8006 |
|
| 8007 if (menuitem != gtk_ui_manager_get_widget(gtkblist->ui, "/BList/AccountsMenu/ManageAccounts")) |
|
| 8008 gtk_widget_destroy(menuitem); |
|
| 8009 } |
|
| 8010 |
|
| 8011 accel_group = gtk_menu_get_accel_group(GTK_MENU(accountmenu)); |
|
| 8012 |
|
| 8013 for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) { |
|
| 8014 char *buf = NULL; |
|
| 8015 GtkWidget *image = NULL; |
|
| 8016 PurpleAccount *account = NULL; |
|
| 8017 GdkPixbuf *pixbuf = NULL; |
|
| 8018 |
|
| 8019 account = accounts->data; |
|
| 8020 |
|
| 8021 if (!purple_account_get_enabled(account, PIDGIN_UI)) { |
|
| 8022 if (!disabled_accounts) { |
|
| 8023 menuitem = gtk_menu_item_new_with_label(_("Enable Account")); |
|
| 8024 gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem); |
|
| 8025 |
|
| 8026 submenu = gtk_menu_new(); |
|
| 8027 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group); |
|
| 8028 gtk_menu_set_accel_path(GTK_MENU(submenu), "<Actions>/BListActions/EnableAccount"); |
|
| 8029 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); |
|
| 8030 |
|
| 8031 disabled_accounts = TRUE; |
|
| 8032 } |
|
| 8033 |
|
| 8034 buf = g_strconcat(purple_account_get_username(account), " (", |
|
| 8035 purple_account_get_protocol_name(account), ")", NULL); |
|
| 8036 menuitem = gtk_image_menu_item_new_with_label(buf); |
|
| 8037 g_free(buf); |
|
| 8038 |
|
| 8039 pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); |
|
| 8040 if (pixbuf != NULL) { |
|
| 8041 if (!purple_account_is_connected(account)) |
|
| 8042 gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); |
|
| 8043 image = gtk_image_new_from_pixbuf(pixbuf); |
|
| 8044 g_object_unref(G_OBJECT(pixbuf)); |
|
| 8045 gtk_widget_show(image); |
|
| 8046 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); |
|
| 8047 } |
|
| 8048 |
|
| 8049 g_signal_connect(G_OBJECT(menuitem), "activate", |
|
| 8050 G_CALLBACK(enable_account_cb), account); |
|
| 8051 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
|
| 8052 |
|
| 8053 } else { |
|
| 8054 enabled_accounts = TRUE; |
|
| 8055 } |
|
| 8056 } |
|
| 8057 |
|
| 8058 if (!enabled_accounts) { |
|
| 8059 gtk_widget_show_all(accountmenu); |
|
| 8060 return; |
|
| 8061 } |
|
| 8062 |
|
| 8063 pidgin_separator(accountmenu); |
|
| 8064 |
|
| 8065 for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) { |
|
| 8066 char *buf = NULL; |
|
| 8067 char *accel_path_buf = NULL; |
|
| 8068 GtkWidget *image = NULL; |
|
| 8069 PurpleConnection *gc = NULL; |
|
| 8070 PurpleAccount *account = NULL; |
|
| 8071 GdkPixbuf *pixbuf = NULL; |
|
| 8072 PurpleProtocol *protocol; |
|
| 8073 |
|
| 8074 account = accounts->data; |
|
| 8075 |
|
| 8076 if (!purple_account_get_enabled(account, PIDGIN_UI)) |
|
| 8077 continue; |
|
| 8078 |
|
| 8079 buf = g_strconcat(purple_account_get_username(account), " (", |
|
| 8080 purple_account_get_protocol_name(account), ")", NULL); |
|
| 8081 menuitem = gtk_image_menu_item_new_with_label(buf); |
|
| 8082 accel_path_buf = g_strconcat("<Actions>/AccountActions/", buf, NULL); |
|
| 8083 g_free(buf); |
|
| 8084 |
|
| 8085 pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); |
|
| 8086 if (pixbuf != NULL) { |
|
| 8087 if (!purple_account_is_connected(account)) |
|
| 8088 gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, |
|
| 8089 0.0, FALSE); |
|
| 8090 image = gtk_image_new_from_pixbuf(pixbuf); |
|
| 8091 g_object_unref(G_OBJECT(pixbuf)); |
|
| 8092 gtk_widget_show(image); |
|
| 8093 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); |
|
| 8094 } |
|
| 8095 |
|
| 8096 gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem); |
|
| 8097 |
|
| 8098 submenu = gtk_menu_new(); |
|
| 8099 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group); |
|
| 8100 gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path_buf); |
|
| 8101 g_free(accel_path_buf); |
|
| 8102 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); |
|
| 8103 |
|
| 8104 menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Account")); |
|
| 8105 g_signal_connect(G_OBJECT(menuitem), "activate", |
|
| 8106 G_CALLBACK(modify_account_cb), account); |
|
| 8107 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
|
| 8108 |
|
| 8109 pidgin_separator(submenu); |
|
| 8110 |
|
| 8111 gc = purple_account_get_connection(account); |
|
| 8112 protocol = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? |
|
| 8113 purple_connection_get_protocol(gc) : NULL; |
|
| 8114 |
|
| 8115 if (protocol && |
|
| 8116 (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods) || |
|
| 8117 PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions))) { |
|
| 8118 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods) && |
|
| 8119 (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) { |
|
| 8120 |
|
| 8121 if (purple_account_get_status(account, "mood")) { |
|
| 8122 menuitem = gtk_menu_item_new_with_mnemonic(_("Set _Mood...")); |
|
| 8123 g_signal_connect(G_OBJECT(menuitem), "activate", |
|
| 8124 G_CALLBACK(set_mood_cb), account); |
|
| 8125 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
|
| 8126 } |
|
| 8127 } |
|
| 8128 |
|
| 8129 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions)) { |
|
| 8130 GtkWidget *menuitem; |
|
| 8131 PurpleProtocolAction *action = NULL; |
|
| 8132 GList *actions, *l; |
|
| 8133 |
|
| 8134 actions = purple_protocol_client_iface_get_actions(protocol, gc); |
|
| 8135 |
|
| 8136 for (l = actions; l != NULL; l = l->next) |
|
| 8137 { |
|
| 8138 if (l->data) |
|
| 8139 { |
|
| 8140 action = (PurpleProtocolAction *) l->data; |
|
| 8141 action->connection = gc; |
|
| 8142 |
|
| 8143 menuitem = gtk_menu_item_new_with_label(action->label); |
|
| 8144 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
|
| 8145 |
|
| 8146 g_signal_connect(G_OBJECT(menuitem), "activate", |
|
| 8147 G_CALLBACK(protocol_act), action); |
|
| 8148 g_object_set_data_full(G_OBJECT(menuitem), "protocol_action", |
|
| 8149 action, |
|
| 8150 (GDestroyNotify)purple_protocol_action_free); |
|
| 8151 gtk_widget_show(menuitem); |
|
| 8152 } |
|
| 8153 else |
|
| 8154 pidgin_separator(submenu); |
|
| 8155 } |
|
| 8156 |
|
| 8157 g_list_free(actions); |
|
| 8158 } |
|
| 8159 } else { |
|
| 8160 menuitem = gtk_menu_item_new_with_label(_("No actions available")); |
|
| 8161 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
|
| 8162 gtk_widget_set_sensitive(menuitem, FALSE); |
|
| 8163 } |
|
| 8164 |
|
| 8165 pidgin_separator(submenu); |
|
| 8166 |
|
| 8167 menuitem = gtk_menu_item_new_with_mnemonic(_("_Disable")); |
|
| 8168 g_signal_connect(G_OBJECT(menuitem), "activate", |
|
| 8169 G_CALLBACK(disable_account_cb), account); |
|
| 8170 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
|
| 8171 } |
|
| 8172 |
|
| 8173 gtk_widget_show_all(accountmenu); |
|
| 8174 } |
|
| 8175 |
|
| 8176 static GSList *plugin_menu_items; |
|
| 8177 |
|
| 8178 void |
|
| 8179 pidgin_blist_update_plugin_actions(void) |
|
| 8180 { |
|
| 8181 GtkWidget *toolsmenu; |
|
| 8182 GSimpleActionGroup *action_group; |
|
| 8183 PurplePlugin *plugin = NULL; |
|
| 8184 PurplePluginInfo *info; |
|
| 8185 GList *l; |
|
| 8186 |
|
| 8187 int count = 0; |
|
| 8188 |
|
| 8189 if ((gtkblist == NULL) || (gtkblist->ui == NULL)) |
|
| 8190 return; |
|
| 8191 |
|
| 8192 /* Clear the old menu */ |
|
| 8193 g_slist_free_full(plugin_menu_items, |
|
| 8194 (GDestroyNotify)gtk_widget_destroy); |
|
| 8195 plugin_menu_items = NULL; |
|
| 8196 |
|
| 8197 toolsmenu = gtk_ui_manager_get_widget(gtkblist->ui, |
|
| 8198 "/BList/ToolsMenu"); |
|
| 8199 toolsmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(toolsmenu)); |
|
| 8200 |
|
| 8201 action_group = g_simple_action_group_new(); |
|
| 8202 |
|
| 8203 /* Add a submenu for each plugin with custom actions */ |
|
| 8204 for (l = purple_plugins_get_loaded(); l; l = l->next) { |
|
| 8205 char *name; |
|
| 8206 GtkWidget *submenu; |
|
| 8207 GtkWidget *menuitem; |
|
| 8208 |
|
| 8209 plugin = (PurplePlugin *)l->data; |
|
| 8210 info = purple_plugin_get_info(plugin); |
|
| 8211 |
|
| 8212 if (!purple_plugin_info_get_actions_cb(info)) |
|
| 8213 continue; |
|
| 8214 |
|
| 8215 name = g_strdup_printf("plugin%d", count); |
|
| 8216 submenu = build_plugin_actions(G_ACTION_MAP(action_group), |
|
| 8217 name, plugin); |
|
| 8218 g_free(name); |
|
| 8219 |
|
| 8220 if (submenu == NULL) { |
|
| 8221 continue; |
|
| 8222 } |
|
| 8223 |
|
| 8224 menuitem = gtk_menu_item_new_with_mnemonic( |
|
| 8225 _(gplugin_plugin_info_get_name( |
|
| 8226 GPLUGIN_PLUGIN_INFO(info)))); |
|
| 8227 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); |
|
| 8228 gtk_widget_show(menuitem); |
|
| 8229 plugin_menu_items = g_slist_prepend(plugin_menu_items, |
|
| 8230 menuitem); |
|
| 8231 gtk_menu_shell_append(GTK_MENU_SHELL(toolsmenu), menuitem); |
|
| 8232 |
|
| 8233 count++; |
|
| 8234 } |
|
| 8235 |
|
| 8236 /* Replaces existing "plugin" group if any */ |
|
| 8237 gtk_widget_insert_action_group(toolsmenu, "plugin", |
|
| 8238 G_ACTION_GROUP(action_group)); |
|
| 8239 g_object_unref(action_group); |
|
| 8240 } |
|
| 8241 |
|
| 8242 static void |
|
| 8243 sortmethod_act(GtkRadioAction *action, GtkRadioAction *current, char *id) |
|
| 8244 { |
|
| 8245 if (action == current) |
|
| 8246 { |
|
| 8247 pidgin_set_cursor(gtkblist->window, GDK_WATCH); |
|
| 8248 /* This is redundant. I think. */ |
|
| 8249 /* pidgin_blist_sort_method_set(id); */ |
|
| 8250 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/sort_type", id); |
|
| 8251 |
|
| 8252 pidgin_clear_cursor(gtkblist->window); |
|
| 8253 } |
|
| 8254 } |
|
| 8255 |
|
| 8256 void |
7243 void |
| 8257 pidgin_blist_update_sort_methods(void) |
7244 pidgin_blist_update_sort_methods(void) |
| 8258 { |
7245 { |
| 8259 PidginBlistSortMethod *method = NULL; |
7246 GtkWidget *sort_item = NULL; |
| |
7247 GMenu *menu = NULL; |
| 8260 GList *l; |
7248 GList *l; |
| 8261 GSList *sl = NULL; |
7249 |
| 8262 const char *m = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type"); |
7250 if(gtkblist == NULL) |
| 8263 |
|
| 8264 GtkRadioAction *action; |
|
| 8265 GString *ui_string; |
|
| 8266 |
|
| 8267 if ((gtkblist == NULL) || (gtkblist->ui == NULL)) |
|
| 8268 return; |
7251 return; |
| 8269 |
7252 |
| 8270 /* Clear the old menu */ |
7253 /* create the gmenu */ |
| 8271 if (sort_action_group) { |
7254 menu = g_menu_new(); |
| 8272 gtk_ui_manager_remove_ui(gtkblist->ui, sort_merge_id); |
7255 |
| 8273 gtk_ui_manager_remove_action_group(gtkblist->ui, sort_action_group); |
7256 /* walk through the sort methods and update all the things */ |
| 8274 g_object_unref(G_OBJECT(sort_action_group)); |
|
| 8275 } |
|
| 8276 |
|
| 8277 sort_action_group = gtk_action_group_new("SortMethods"); |
|
| 8278 gtk_action_group_set_translation_domain(sort_action_group, PACKAGE); |
|
| 8279 |
|
| 8280 ui_string = g_string_new("<ui><menubar name='BList'>" |
|
| 8281 "<menu action='BuddiesMenu'><menu action='SortMenu'>"); |
|
| 8282 |
|
| 8283 for (l = pidgin_blist_sort_methods; l; l = l->next) { |
7257 for (l = pidgin_blist_sort_methods; l; l = l->next) { |
| |
7258 PidginBlistSortMethod *method = NULL; |
| |
7259 GMenuItem *item = NULL; |
| |
7260 gchar *action = NULL; |
| |
7261 |
| 8284 method = (PidginBlistSortMethod *)l->data; |
7262 method = (PidginBlistSortMethod *)l->data; |
| 8285 |
7263 |
| 8286 g_string_append_printf(ui_string, "<menuitem action='%s'/>", method->id); |
7264 action = g_action_print_detailed_name("blist.sort-method", |
| 8287 action = gtk_radio_action_new(method->id, |
7265 g_variant_new_string(method->id)); |
| 8288 method->name, |
7266 item = g_menu_item_new(method->name, action); |
| 8289 NULL, |
7267 g_free(action); |
| 8290 NULL, |
7268 |
| 8291 0); |
7269 g_menu_append_item(menu, item); |
| 8292 gtk_action_group_add_action_with_accel(sort_action_group, GTK_ACTION(action), NULL); |
7270 } |
| 8293 |
7271 |
| 8294 gtk_radio_action_set_group(action, sl); |
7272 /* replace the old submenu with a new one */ |
| 8295 sl = gtk_radio_action_get_group(action); |
7273 sort_item = pidgin_buddy_list_menu_get_sort_item(PIDGIN_BUDDY_LIST_MENU(gtkblist->menu)); |
| 8296 |
7274 gtk_menu_item_set_submenu(GTK_MENU_ITEM(sort_item), |
| 8297 if (purple_strequal(m, method->id)) |
7275 gtk_menu_new_from_model(G_MENU_MODEL(menu))); |
| 8298 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); |
7276 g_object_unref(G_OBJECT(menu)); |
| 8299 else |
7277 } |
| 8300 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), FALSE); |
|
| 8301 |
|
| 8302 g_signal_connect(G_OBJECT(action), "changed", |
|
| 8303 G_CALLBACK(sortmethod_act), method->id); |
|
| 8304 } |
|
| 8305 |
|
| 8306 g_string_append(ui_string, "</menu></menu></menubar></ui>"); |
|
| 8307 gtk_ui_manager_insert_action_group(gtkblist->ui, sort_action_group, 1); |
|
| 8308 sort_merge_id = gtk_ui_manager_add_ui_from_string(gtkblist->ui, ui_string->str, -1, NULL); |
|
| 8309 |
|
| 8310 g_string_free(ui_string, TRUE); |
|
| 8311 } |
|
| 8312 |
|