Fri, 17 Feb 2023 19:37:13 -0600
Remove PidginBlist
PidginContactList is functional enough for us to start adding features and
since GtkTreeView is being deprecated, now seemed like a great time to purge
PidginBlist.
There's a few remaining pieces in gtkblist.[ch] relating to the join chat and
im buddy dialogs, as well as the PurpleBlist subclass we need to work out yet.
I also removed the gtkbuddynote plugin as all it did was add the buddy note to
the tooltip. My future plans here will have this built in to the ui because a
few protocols support this already and we can mock it for those that don't.
Testing Done:
Launched Pidgin 3 and verified stuff worked!
Bugs closed: PIDGIN-17751
Reviewed at https://reviews.imfreedom.org/r/2228/
| ChangeLog.API | file | annotate | diff | comparison | revisions | |
| pidgin/gtkblist.c | file | annotate | diff | comparison | revisions | |
| pidgin/gtkblist.h | file | annotate | diff | comparison | revisions | |
| pidgin/gtkconv.c | file | annotate | diff | comparison | revisions | |
| pidgin/pidginapplication.c | file | annotate | diff | comparison | revisions | |
| pidgin/pidginui.c | file | annotate | diff | comparison | revisions | |
| pidgin/plugins/gtkbuddynote/gtkbuddynote.c | file | annotate | diff | comparison | revisions | |
| pidgin/plugins/gtkbuddynote/meson.build | file | annotate | diff | comparison | revisions | |
| pidgin/plugins/meson.build | file | annotate | diff | comparison | revisions | |
| pidgin/plugins/transparency/transparency.c | file | annotate | diff | comparison | revisions | |
| pidgin/win32/gtkwin32dep.c | file | annotate | diff | comparison | revisions |
--- a/ChangeLog.API Fri Feb 17 19:34:51 2023 -0600 +++ b/ChangeLog.API Fri Feb 17 19:37:13 2023 -0600 @@ -964,14 +964,29 @@ * pidgin_append_blist_node_privacy_menu * pidgin_append_blist_node_proto_menu * pidgin_append_menu_action + * PidginBlistSortMethod * pidgin_blist_add_alert * pidgin_blist_draw_tooltip + * pidgin_blist_get_default_gtk_blist + * pidgin_blist_get_emblem + * pidgin_blist_get_handle + * pidgin_blist_get_name_markup + * pidgin_blist_get_sort_methods * pidgin_blist_get_status_icon * pidgin_blist_get_theme + * pidgin_blist_init * pidgin_blist_layout_get_type * pidgin_blist_make_buddy_menu + * pidgin_blist_node_is_contact_expanded + * pidgin_blist_query_tooltip_for_node + * pidgin_blist_refresh * pidgin_blist_set_headline * pidgin_blist_set_theme + * pidgin_blist_setup_sort_methods + * pidgin_blist_sort_function + * pidgin_blist_sort_method_reg + * pidgin_blist_sort_method_set + * pidgin_blist_sort_method_unreg * pidgin_blist_theme_get_background_color * pidgin_blist_theme_get_collapsed_background_color * pidgin_blist_theme_get_collapsed_text_info @@ -1008,11 +1023,13 @@ * pidgin_blist_theme_set_unread_message_text_info * pidgin_blist_toggle_visibility * pidgin_blist_tooltip_destroy + * pidgin_blist_uninit * pidgin_blist_update_account_error_state * pidgin_blist_update_accounts_menu * pidgin_blist_update_columns * pidgin_blist_update_plugin_actions * pidgin_blist_update_refresh_timeout + * pidgin_blist_update_sort_methods * pidgin_blist_visibility_manager_add * pidgin_blist_visibility_manager_remove * PidginBlistLayout
--- a/pidgin/gtkblist.c Fri Feb 17 19:34:51 2023 -0600 +++ b/pidgin/gtkblist.c Fri Feb 17 19:37:13 2023 -0600 @@ -27,30 +27,14 @@ #include <purple.h> -#include "gtkaccount.h" #include "gtkblist.h" #include "gtkconv.h" -#include "gtkdialogs.h" -#include "gtkxfer.h" #include "gtkroomlist.h" #include "gtkutils.h" #include "pidgin/pidginaccountchooser.h" #include "pidgin/pidginaccountfilterconnected.h" -#include "pidgin/pidginactiongroup.h" #include "pidgin/pidginaddbuddydialog.h" #include "pidgin/pidginaddchatdialog.h" -#include "pidgin/pidgincontactlistwindow.h" -#include "pidgin/pidgincore.h" -#include "pidgin/pidgindebug.h" -#include "pidgin/pidginiconname.h" -#include "pidgin/pidginmooddialog.h" -#include "pidgin/pidginplugininfo.h" - -#include <gdk/gdkkeysyms.h> -#include <gtk/gtk.h> -#include <gdk/gdk.h> - -#define UI_DATA "ui-pidgin" typedef struct { @@ -68,126 +52,11 @@ GList *entries; } PidginChatData; -typedef struct -{ - PidginChatData chat_data; - - GtkWidget *alias_entry; - GtkWidget *group_combo; - GtkWidget *autojoin; - GtkWidget *persistent; -} PidginAddChatData; - G_DEFINE_TYPE(PidginBuddyList, pidgin_buddy_list, PURPLE_TYPE_BUDDY_LIST) -enum { - STATUS_ICON_COLUMN, - STATUS_ICON_VISIBLE_COLUMN, - NAME_COLUMN, - BUDDY_ICON_COLUMN, - NODE_COLUMN, - EMBLEM_COLUMN, - EMBLEM_VISIBLE_COLUMN, - PROTOCOL_ICON_NAME_COLUMN, - BLIST_COLUMNS -}; - -static gboolean editing_blist = FALSE; - -static GList *pidgin_blist_sort_methods = NULL; -static struct _PidginBlistSortMethod *current_sort_method = NULL; -static void sort_method_none(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter); - -static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter); -static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter); - -static PidginBuddyList *gtkblist = NULL; - -static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean status_change); -static void pidgin_blist_selection_changed(GtkTreeSelection *selection, gpointer data); -static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node); -static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node); -static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *node); -static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full); -static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter); -static gboolean buddy_is_displayable(PurpleBuddy *buddy); -static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender); -static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node); -static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded); -static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node); - -typedef struct { - GtkTreeRowReference *row; - gboolean contact_expanded; - gboolean recent_signonoff; - gint recent_signonoff_timer; - PurpleConversation *conv; -} PidginBlistNode; - -/*************************************************** - * Callbacks * - ***************************************************/ -static void -set_node_custom_icon_cb(GtkWidget *widget, gint response, gpointer data) -{ - if (response == GTK_RESPONSE_ACCEPT) { - PurpleBlistNode *node = (PurpleBlistNode*)data; - GFile *file = NULL; - gchar *filename = NULL; - - file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(widget)); - filename = g_file_get_path(file); - purple_buddy_icons_node_set_custom_icon_from_file(node, filename); - g_free(filename); - g_object_unref(file); - } - - g_object_set_data(G_OBJECT(data), "buddy-icon-chooser", NULL); -} - -static void -chat_components_edit_ok(PurpleChat *chat, PurpleRequestFields *allfields) -{ - GList *groups, *fields; - - for (groups = purple_request_fields_get_groups(allfields); groups; groups = groups->next) { - fields = purple_request_field_group_get_fields(groups->data); - for (; fields; fields = fields->next) { - PurpleRequestField *field = fields->data; - const char *id; - char *val; - - id = purple_request_field_get_id(field); - if (purple_request_field_get_field_type(field) == PURPLE_REQUEST_FIELD_INTEGER) - val = g_strdup_printf("%d", purple_request_field_int_get_value(field)); - else - val = g_strdup(purple_request_field_string_get_value(field)); - - if (!val) { - g_hash_table_remove(purple_chat_get_components(chat), id); - } else { - g_hash_table_replace(purple_chat_get_components(chat), g_strdup(id), val); /* val should not be free'd */ - } - } - } -} - /****************************************************************************** * Helpers *****************************************************************************/ -static PurpleBuddy * -pidgin_blist_get_selected_buddy(PidginBuddyList *blist) { - PurpleBuddy *buddy = NULL; - - if(PURPLE_IS_META_CONTACT(blist->selected_node)) { - buddy = purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(blist->selected_node)); - } else if(PURPLE_IS_BUDDY(blist->selected_node)) { - buddy = PURPLE_BUDDY(blist->selected_node); - } - - return buddy; -} - static void gtk_blist_join_chat(PurpleChat *chat) { PurpleAccount *account; @@ -226,589 +95,10 @@ g_free(chat_name); } -static void -pidgin_blist_toggle_action(GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - G_GNUC_UNUSED gpointer data) -{ - GVariant *state = NULL; - - - state = g_action_get_state(G_ACTION(action)); - g_action_change_state( - G_ACTION(action), - g_variant_new_boolean(!g_variant_get_boolean(state)) - ); - g_variant_unref(state); -} - /****************************************************************************** * Actions *****************************************************************************/ static void -pidgin_blist_add_buddy_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - GtkTreeSelection *sel = NULL; - GtkTreeIter iter; - PurpleBlistNode *node; - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(blist->treeview)); - - if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ - gtk_tree_model_get(GTK_TREE_MODEL(blist->treemodel), &iter, NODE_COLUMN, &node, -1); - if (PURPLE_IS_BUDDY(node)) { - PurpleGroup *group = purple_buddy_get_group(PURPLE_BUDDY(node)); - purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(group), NULL); - } else if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_CHAT(node)) { - PurpleGroup *group = purple_meta_contact_get_group(PURPLE_META_CONTACT(node)); - purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(group), NULL); - } else if (PURPLE_IS_GROUP(node)) { - purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(PURPLE_GROUP(node)), NULL); - } - } else { - purple_blist_request_add_buddy(NULL, NULL, NULL, NULL); - } -} - -static void -pidgin_blist_add_chat_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - GtkTreeSelection *sel = NULL; - GtkTreeIter iter; - PurpleBlistNode *node; - - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(blist->treeview)); - - if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ - gtk_tree_model_get(GTK_TREE_MODEL(blist->treemodel), &iter, NODE_COLUMN, &node, -1); - if (PURPLE_IS_BUDDY(node)) - purple_blist_request_add_chat(NULL, purple_buddy_get_group(PURPLE_BUDDY(node)), NULL, NULL); - if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_CHAT(node)) - purple_blist_request_add_chat(NULL, purple_meta_contact_get_group(PURPLE_META_CONTACT(node)), NULL, NULL); - else if (PURPLE_IS_GROUP(node)) - purple_blist_request_add_chat(NULL, (PurpleGroup*)node, NULL, NULL); - } else { - purple_blist_request_add_chat(NULL, NULL, NULL, NULL); - } -} - -static void -pidgin_blist_menu_alias_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - PurpleBlistNode *node = blist->selected_node; - GtkTreeIter iter; - GtkTreePath *path; - - if (!(get_iter_from_node(node, &iter))) { - /* This is either a bug, or the buddy is in a collapsed contact */ - node = purple_blist_node_get_parent(node); - if (!get_iter_from_node(node, &iter)) - /* Now it's definitely a bug */ - return; - } - - gtk_widget_trigger_tooltip_query(blist->treeview); - - path = gtk_tree_model_get_path(GTK_TREE_MODEL(blist->treemodel), &iter); - g_object_set(G_OBJECT(blist->text_rend), "editable", TRUE, NULL); - gtk_tree_view_set_enable_search (GTK_TREE_VIEW(blist->treeview), FALSE); - gtk_widget_grab_focus(blist->treeview); - gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(blist->treeview), path, - blist->text_column, blist->text_rend, - TRUE); - gtk_tree_path_free(path); -} - -static void -pidgin_blist_menu_autojoin_cb(GSimpleAction *action, GVariant *state, - gpointer data) -{ - PidginBuddyList *blist = data; - - purple_blist_node_set_bool(blist->selected_node, "gtk-autojoin", - g_variant_get_boolean(state)); - - g_simple_action_set_state(action, state); -} - -static void -pidgin_blist_menu_audio_call_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist); - - purple_protocol_initiate_media(purple_buddy_get_account(buddy), - purple_buddy_get_name(buddy), - PURPLE_MEDIA_AUDIO); -} - -static void -pidgin_blist_menu_chat_settings_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - PurpleRequestFields *fields = purple_request_fields_new(); - PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL); - PurpleRequestField *field; - GList *parts, *iter; - PurpleProtocolChatEntry *pce; - PurpleProtocol *protocol; - PurpleConnection *gc; - PurpleChat *chat = PURPLE_CHAT(blist->selected_node); - - purple_request_fields_add_group(fields, group); - - gc = purple_account_get_connection(purple_chat_get_account(chat)); - protocol = purple_connection_get_protocol(gc); - parts = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol), gc); - - for (iter = parts; iter; iter = iter->next) { - pce = iter->data; - if (pce->is_int) { - int val; - const char *str = g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier); - if (!str || sscanf(str, "%d", &val) != 1) - val = pce->min; - field = purple_request_field_int_new(pce->identifier, pce->label, val, INT_MIN, INT_MAX); - } else { - field = purple_request_field_string_new(pce->identifier, pce->label, - g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier), FALSE); - if (pce->secret) - purple_request_field_string_set_masked(field, TRUE); - } - - if (pce->required) - purple_request_field_set_required(field, TRUE); - - purple_request_field_group_add_field(group, field); - g_free(pce); - } - - g_list_free(parts); - - purple_request_fields(NULL, _("Edit Chat"), NULL, _("Please update the necessary fields."), - fields, _("Save"), G_CALLBACK(chat_components_edit_ok), _("Cancel"), NULL, - NULL, chat); -} - -static void -pidgin_blist_menu_im_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist); - - pidgin_dialogs_im_with_user(purple_buddy_get_account(buddy), - purple_buddy_get_name(buddy)); -} - -static void -pidgin_blist_menu_info_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist); - PurpleAccount *account = purple_buddy_get_account(buddy); - - pidgin_retrieve_user_info(purple_account_get_connection(account), - purple_buddy_get_name(buddy)); -} - -static void -pidgin_blist_menu_join_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - - gtk_blist_join_chat(PURPLE_CHAT(blist->selected_node)); -} - -static void -pidgin_blist_menu_persistent_cb(GSimpleAction *action, GVariant *state, - gpointer data) -{ - PidginBuddyList *blist = data; - - purple_blist_node_set_bool(blist->selected_node, "gtk-persistent", - g_variant_get_boolean(state)); - - g_simple_action_set_state(action, state); -} - -static void -pidgin_blist_menu_send_file_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist); - PurpleAccount *account = purple_buddy_get_account(buddy); - - purple_serv_send_file(purple_account_get_connection(account), - purple_buddy_get_name(buddy), NULL); -} - -static void -pidgin_blist_remove_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - PurpleBlistNode *node = blist->selected_node; - - if(PURPLE_IS_BUDDY(node)) { - pidgin_dialogs_remove_buddy(PURPLE_BUDDY(node)); - } else if(PURPLE_IS_CHAT(node)) { - pidgin_dialogs_remove_chat(PURPLE_CHAT(node)); - } else if(PURPLE_IS_GROUP(node)) { - pidgin_dialogs_remove_group(PURPLE_GROUP(node)); - } else if(PURPLE_IS_META_CONTACT(node)) { - pidgin_dialogs_remove_contact(PURPLE_META_CONTACT(node)); - } -} - -static void -pidgin_blist_remove_node_custom_icon(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - - purple_buddy_icons_node_set_custom_icon(blist->selected_node, NULL, 0); -} - -static void -pidgin_blist_set_node_custom_icon(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PidginBuddyList *blist = data; - PurpleBlistNode *node = blist->selected_node; - GtkFileChooserNative *win = NULL; - - win = g_object_get_data(G_OBJECT(node), "buddy-icon-chooser"); - if(win == NULL) { - win = gtk_file_chooser_native_new(_("Buddy Icon"), NULL, - GTK_FILE_CHOOSER_ACTION_OPEN, - _("_Open"), _("_Cancel")); - - g_signal_connect(win, "response", G_CALLBACK(set_node_custom_icon_cb), - node); - - g_object_set_data_full(G_OBJECT(node), "buddy-icon-chooser", win, - g_object_unref); - } - - gtk_native_dialog_show(GTK_NATIVE_DIALOG(win)); -} - -static void -pidgin_blist_menu_video_call_cb(G_GNUC_UNUSED GSimpleAction *action, - G_GNUC_UNUSED GVariant *parameter, - gpointer data) -{ - PurpleAccount *account = NULL; - PidginBuddyList *blist = data; - PurpleBuddy *buddy = pidgin_blist_get_selected_buddy(blist); - PurpleMediaCaps caps = 0; - const gchar *buddy_name = NULL; - - account = purple_buddy_get_account(buddy); - buddy_name = purple_buddy_get_name(buddy); - - /* if the buddy supports both audio and video, start a combined call, - otherwise start a pure video session */ - caps = purple_protocol_get_media_caps(account, buddy_name); - - if(caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) { - purple_protocol_initiate_media(account, buddy_name, - PURPLE_MEDIA_AUDIO | PURPLE_MEDIA_VIDEO); - } else { - purple_protocol_initiate_media(account, buddy_name, PURPLE_MEDIA_VIDEO); - } -} - -static GActionEntry menu_actions[] = { - { - .name = "add-buddy", - .activate = pidgin_blist_add_buddy_cb, - }, { - .name = "add-chat", - .activate = pidgin_blist_add_chat_cb, - }, { - .name = "alias", - .activate = pidgin_blist_menu_alias_cb, - }, { - .name = "auto-join-chat", - .activate = pidgin_blist_toggle_action, - .state = "false", - .change_state = pidgin_blist_menu_autojoin_cb, - }, { - .name = "buddy-audio-call", - .activate = pidgin_blist_menu_audio_call_cb, - }, { - .name = "buddy-get-info", - .activate = pidgin_blist_menu_info_cb, - }, { - .name = "buddy-im", - .activate = pidgin_blist_menu_im_cb, - }, { - .name = "buddy-send-file", - .activate = pidgin_blist_menu_send_file_cb, - }, { - .name = "buddy-video-call", - .activate = pidgin_blist_menu_video_call_cb, - }, { - .name = "chat-settings", - .activate = pidgin_blist_menu_chat_settings_cb, - }, { - .name = "join-chat", - .activate = pidgin_blist_menu_join_cb, - }, { - .name = "persistent-chat", - .activate = pidgin_blist_toggle_action, - .state = "false", - .change_state = pidgin_blist_menu_persistent_cb, - }, { - .name = "remove", - .activate = pidgin_blist_remove_cb, - }, { - .name = "remove-custom-icon", - .activate = pidgin_blist_remove_node_custom_icon, - }, { - .name = "set-custom-icon", - .activate = pidgin_blist_set_node_custom_icon, - } -}; - -static void gtk_blist_renderer_editing_cancelled_cb(GtkCellRenderer *renderer, PurpleBuddyList *list) -{ - editing_blist = FALSE; - g_object_set(G_OBJECT(renderer), "editable", FALSE, NULL); - pidgin_blist_refresh(list); -} - -static void -gtk_blist_renderer_editing_started_cb(G_GNUC_UNUSED GtkCellRenderer *renderer, - GtkCellEditable *editable, - gchar *path_str, gpointer user_data) -{ - PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(user_data); - GtkTreeIter iter; - GtkTreePath *path = NULL; - PurpleBlistNode *node; - const char *text = NULL; - - path = gtk_tree_path_new_from_string (path_str); - gtk_tree_model_get_iter (GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - gtk_tree_path_free (path); - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); - - if (PURPLE_IS_META_CONTACT(node)) - text = purple_meta_contact_get_alias(PURPLE_META_CONTACT(node)); - else if (PURPLE_IS_BUDDY(node)) - text = purple_buddy_get_alias(PURPLE_BUDDY(node)); - else if (PURPLE_IS_GROUP(node)) - text = purple_group_get_name(PURPLE_GROUP(node)); - else if (PURPLE_IS_CHAT(node)) - text = purple_chat_get_name(PURPLE_CHAT(node)); - else - g_return_if_reached(); - - if(GTK_IS_EDITABLE(editable)) { - gtk_editable_set_text(GTK_EDITABLE(editable), text); - } - - editing_blist = TRUE; -} - -static void -gtk_blist_do_personize(GList *merges) -{ - PurpleBlistNode *contact = NULL; - int max = 0; - GList *tmp; - - /* First, we find the contact to merge the rest of the buddies into. - * This will be the contact with the most buddies in it; ties are broken - * by which contact is higher in the list - */ - for (tmp = merges; tmp; tmp = tmp->next) { - PurpleBlistNode *node = tmp->data; - PurpleBlistNode *b; - int i = 0; - - if (PURPLE_IS_BUDDY(node)) - node = purple_blist_node_get_parent(node); - - if (!PURPLE_IS_META_CONTACT(node)) - continue; - - for (b = purple_blist_node_get_first_child(node); - b; - b = purple_blist_node_get_sibling_next(b)) - { - i++; - } - - if (i > max) { - contact = node; - max = i; - } - } - - if (contact == NULL) - return; - - /* Merge all those buddies into this contact */ - for (tmp = merges; tmp; tmp = tmp->next) { - PurpleBlistNode *node = tmp->data; - if (PURPLE_IS_BUDDY(node)) - node = purple_blist_node_get_parent(node); - - if (node == contact) - continue; - - purple_meta_contact_merge((PurpleMetaContact *)node, contact); - } - - /* And show the expanded contact, so the people know what's going on */ - pidgin_blist_expand_contact_cb(NULL, contact); - g_list_free(merges); -} - -static void -gtk_blist_auto_personize(PurpleBlistNode *group, const char *alias) -{ - PurpleBlistNode *contact; - PurpleBlistNode *buddy; - GList *merges = NULL; - int i = 0; - char *a = g_utf8_casefold(alias, -1); - - for (contact = purple_blist_node_get_first_child(group); - contact != NULL; - contact = purple_blist_node_get_sibling_next(contact)) { - char *node_alias; - if (!PURPLE_IS_META_CONTACT(contact)) - continue; - - node_alias = g_utf8_casefold(purple_meta_contact_get_alias((PurpleMetaContact *)contact), -1); - if (node_alias && !g_utf8_collate(node_alias, a)) { - merges = g_list_append(merges, contact); - i++; - g_free(node_alias); - continue; - } - g_free(node_alias); - - for (buddy = purple_blist_node_get_first_child(contact); - buddy; - buddy = purple_blist_node_get_sibling_next(buddy)) - { - if (!PURPLE_IS_BUDDY(buddy)) - continue; - - node_alias = g_utf8_casefold(purple_buddy_get_alias(PURPLE_BUDDY(buddy)), -1); - if (node_alias && !g_utf8_collate(node_alias, a)) { - merges = g_list_append(merges, buddy); - i++; - g_free(node_alias); - break; - } - g_free(node_alias); - } - } - g_free(a); - - if (i > 1) - { - char *msg = g_strdup_printf(ngettext("You have %d contact named %s. Would you like to merge them?", "You currently have %d contacts named %s. Would you like to merge them?", i), i, alias); - purple_request_action(NULL, NULL, msg, _("Merging these contacts will cause them to share a single entry on the buddy list and use a single conversation window. " - "You can separate them again by choosing 'Expand' from the contact's context menu"), 0, NULL, - merges, 2, _("_Yes"), G_CALLBACK(gtk_blist_do_personize), _("_No"), G_CALLBACK(g_list_free)); - g_free(msg); - } else - g_list_free(merges); -} - -static void -gtk_blist_renderer_edited_cb(G_GNUC_UNUSED GtkCellRendererText *text_rend, - char *arg1, char *arg2, PurpleBuddyList *list) -{ - PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(list); - GtkTreeIter iter; - GtkTreePath *path; - PurpleBlistNode *node; - PurpleGroup *dest; - gchar *alias = NULL; - - editing_blist = FALSE; - path = gtk_tree_path_new_from_string (arg1); - gtk_tree_model_get_iter (GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - gtk_tree_path_free (path); - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); - gtk_tree_view_set_enable_search (GTK_TREE_VIEW(gtkblist->treeview), TRUE); - g_object_set(G_OBJECT(gtkblist->text_rend), "editable", FALSE, NULL); - - if (PURPLE_IS_META_CONTACT(node)) { - PurpleMetaContact *contact = PURPLE_META_CONTACT(node); - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - - /* - * Using purple_meta_contact_get_alias here breaks because we - * specifically want to check the contact alias only (i.e. not - * the priority buddy, which purple_meta_contact_get_alias does). - * The "alias" GObject property gives us just the alias. - */ - g_object_get(contact, "alias", &alias, NULL); - - if (alias || gtknode->contact_expanded) { - purple_meta_contact_set_alias(contact, arg2); - gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2); - } else { - PurpleBuddy *buddy = purple_meta_contact_get_priority_buddy(contact); - purple_buddy_set_local_alias(buddy, arg2); - purple_serv_alias_buddy(buddy); - gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2); - } - } else if (PURPLE_IS_BUDDY(node)) { - PurpleGroup *group = purple_buddy_get_group(PURPLE_BUDDY(node)); - - purple_buddy_set_local_alias(PURPLE_BUDDY(node), arg2); - purple_serv_alias_buddy(PURPLE_BUDDY(node)); - gtk_blist_auto_personize(PURPLE_BLIST_NODE(group), arg2); - } else if (PURPLE_IS_GROUP(node)) { - dest = purple_blist_find_group(arg2); - if (dest != NULL && purple_utf8_strcasecmp(arg2, purple_group_get_name(PURPLE_GROUP(node)))) { - pidgin_dialogs_merge_groups(PURPLE_GROUP(node), arg2); - } else { - purple_group_set_name(PURPLE_GROUP(node), arg2); - } - } else if (PURPLE_IS_CHAT(node)) { - purple_chat_set_alias(PURPLE_CHAT(node), arg2); - } - - g_free(alias); - pidgin_blist_refresh(list); -} - -static void do_join_chat(PidginChatData *data) { if (data) @@ -900,16 +190,6 @@ gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), 1, sensitive); } -static void -pidgin_blist_update_privacy_cb(PurpleBuddy *buddy) -{ - PidginBlistNode *ui_data = g_object_get_data(G_OBJECT(buddy), UI_DATA); - if (ui_data == NULL || ui_data->row == NULL) - return; - pidgin_blist_update_buddy(purple_blist_get_default(), - PURPLE_BLIST_NODE(buddy), TRUE); -} - static gboolean chat_account_filter_func(gpointer item, G_GNUC_UNUSED gpointer data) { PurpleConnection *gc = NULL; @@ -958,19 +238,13 @@ GtkWidget *content_area; GtkWidget *hbox; GtkWidget *vbox; - GtkWindow *blist_window; GtkEveryFilter *every = NULL; GtkFilter *filter = NULL; - PidginBuddyList *gtkblist; data->account = account; - gtkblist = PIDGIN_BUDDY_LIST(purple_blist_get_default()); - blist_window = gtkblist ? GTK_WINDOW(gtkblist->window) : NULL; - data->window = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(data->window), title); - gtk_window_set_transient_for(GTK_WINDOW(data->window), blist_window); gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); content_area = gtk_dialog_get_content_area(GTK_DIALOG(data->window)); @@ -1170,2534 +444,6 @@ } static void -gtk_blist_row_expanded_cb(G_GNUC_UNUSED GtkTreeView *tv, GtkTreeIter *iter, - G_GNUC_UNUSED GtkTreePath *path, gpointer user_data) -{ - PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(user_data); - PurpleBlistNode *node; - - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &node, -1); - - if (PURPLE_IS_GROUP(node)) { - char *title; - - title = pidgin_get_group_title(node, TRUE); - - gtk_tree_store_set(gtkblist->treemodel, iter, - NAME_COLUMN, title, - -1); - - g_free(title); - - purple_blist_node_set_bool(node, "collapsed", FALSE); - gtk_widget_trigger_tooltip_query(gtkblist->treeview); - } -} - -static void -gtk_blist_row_collapsed_cb(G_GNUC_UNUSED GtkTreeView *tv, GtkTreeIter *iter, - G_GNUC_UNUSED GtkTreePath *path, gpointer user_data) -{ - PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(user_data); - PurpleBlistNode *node; - - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &node, -1); - - if (PURPLE_IS_GROUP(node)) { - char *title; - PidginBlistNode *gtknode; - PurpleBlistNode *cnode; - - title = pidgin_get_group_title(node, FALSE); - - gtk_tree_store_set(gtkblist->treemodel, iter, - NAME_COLUMN, title, - -1); - - g_free(title); - - purple_blist_node_set_bool(node, "collapsed", TRUE); - - for(cnode = purple_blist_node_get_first_child(node); cnode; cnode = purple_blist_node_get_sibling_next(cnode)) { - if (PURPLE_IS_META_CONTACT(cnode)) { - gtknode = g_object_get_data(G_OBJECT(cnode), UI_DATA); - if (!gtknode->contact_expanded) - continue; - gtknode->contact_expanded = FALSE; - pidgin_blist_update_contact(NULL, cnode); - } - } - gtk_widget_trigger_tooltip_query(gtkblist->treeview); - } else if(PURPLE_IS_META_CONTACT(node)) { - pidgin_blist_collapse_contact_cb(NULL, node); - } -} - -static void -gtk_blist_row_activated_cb(G_GNUC_UNUSED GtkTreeView *tv, GtkTreePath *path, - G_GNUC_UNUSED GtkTreeViewColumn *col, gpointer data) -{ - PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(data); - PurpleBlistNode *node; - GtkTreeIter iter; - - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); - - if(PURPLE_IS_META_CONTACT(node) || PURPLE_IS_BUDDY(node)) { - PurpleBuddy *buddy; - - if(PURPLE_IS_META_CONTACT(node)) - buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node); - else - buddy = (PurpleBuddy*)node; - - pidgin_dialogs_im_with_user(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy)); - } else if (PURPLE_IS_CHAT(node)) { - gtk_blist_join_chat((PurpleChat *)node); - } else if (PURPLE_IS_GROUP(node)) { -/* if (gtk_tree_view_row_expanded(tv, path)) - gtk_tree_view_collapse_row(tv, path); - else - gtk_tree_view_expand_row(tv,path,FALSE);*/ - } -} - -struct _expand { - GtkTreeView *treeview; - GtkTreePath *path; - PurpleBlistNode *node; -}; - -static gboolean -scroll_to_expanded_cell(gpointer data) -{ - struct _expand *ex = data; - gtk_tree_view_scroll_to_cell(ex->treeview, ex->path, NULL, FALSE, 0, 0); - pidgin_blist_update_contact(NULL, ex->node); - - gtk_tree_path_free(ex->path); - g_free(ex); - - return FALSE; -} - -static void -pidgin_blist_expand_contact_cb(G_GNUC_UNUSED GtkWidget *w, - PurpleBlistNode *node) -{ - PidginBlistNode *gtknode; - GtkTreeIter iter, parent; - PurpleBlistNode *bnode; - GtkTreePath *path; - - if(!PURPLE_IS_META_CONTACT(node)) { - return; - } - - gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - - gtknode->contact_expanded = TRUE; - - for(bnode = purple_blist_node_get_first_child(node); bnode; bnode = purple_blist_node_get_sibling_next(bnode)) { - pidgin_blist_update(NULL, bnode); - } - - /* This ensures that the bottom buddy is visible, i.e. not scrolled off the alignment */ - if (get_iter_from_node(node, &parent)) { - struct _expand *ex = g_new0(struct _expand, 1); - - gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(gtkblist->treemodel), &iter, &parent, - gtk_tree_model_iter_n_children(GTK_TREE_MODEL(gtkblist->treemodel), &parent) -1); - path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - - /* Let the treeview draw so it knows where to scroll */ - ex->treeview = GTK_TREE_VIEW(gtkblist->treeview); - ex->path = path; - ex->node = purple_blist_node_get_first_child(node); - g_idle_add(scroll_to_expanded_cell, ex); - } -} - -static void -pidgin_blist_collapse_contact_cb(G_GNUC_UNUSED GtkWidget *w, - PurpleBlistNode *node) -{ - PurpleBlistNode *bnode; - PidginBlistNode *gtknode; - - if(!PURPLE_IS_META_CONTACT(node)) - return; - - gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - - gtknode->contact_expanded = FALSE; - - for(bnode = purple_blist_node_get_first_child(node); bnode; bnode = purple_blist_node_get_sibling_next(bnode)) { - pidgin_blist_update(NULL, bnode); - } -} - -static gboolean -pidgin_blist_key_press_cb(G_GNUC_UNUSED GtkEventControllerKey *controller, - guint keyval, G_GNUC_UNUSED guint keycode, - GdkModifierType state, gpointer data) -{ - PidginBuddyList *blist = PIDGIN_BUDDY_LIST(data); - GtkTreeView *tv = NULL; - GtkTreeModel *model = NULL; - PurpleBlistNode *node; - GtkTreeIter iter; - GtkTreeSelection *sel; - - tv = GTK_TREE_VIEW(blist->treeview); - sel = gtk_tree_view_get_selection(tv); - - if(!gtk_tree_selection_get_selected(sel, NULL, &iter)) - return FALSE; - - model = GTK_TREE_MODEL(blist->treemodel); - gtk_tree_model_get(model, &iter, NODE_COLUMN, &node, -1); - - if(state & GDK_CONTROL_MASK && (keyval == 'o' || keyval == 'O')) { - PurpleBuddy *buddy; - - if(PURPLE_IS_META_CONTACT(node)) { - buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node); - } else if(PURPLE_IS_BUDDY(node)) { - buddy = (PurpleBuddy*)node; - } else { - return FALSE; - } - if(buddy) - pidgin_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy)); - } else { - GtkTreeIter parent; - GtkTreePath *path = NULL; - - switch (keyval) { - case GDK_KEY_F2: - /* FIXME: gk 2022-05-27 */ - /* - gtk_blist_menu_alias_cb(tv, node); - */ - break; - - case GDK_KEY_Left: - path = gtk_tree_model_get_path(model, &iter); - if (gtk_tree_view_row_expanded(tv, path)) { - /* Collapse the Group */ - gtk_tree_view_collapse_row(tv, path); - gtk_tree_path_free(path); - return TRUE; - } else { - /* Select the Parent */ - if (gtk_tree_model_get_iter(model, &iter, path)) { - if (gtk_tree_model_iter_parent(model, &parent, &iter)) { - gtk_tree_path_free(path); - path = gtk_tree_model_get_path(model, &parent); - gtk_tree_view_set_cursor(tv, path, NULL, FALSE); - gtk_tree_path_free(path); - return TRUE; - } - } - } - gtk_tree_path_free(path); - break; - - case GDK_KEY_Right: - path = gtk_tree_model_get_path(model, &iter); - if (!gtk_tree_view_row_expanded(tv, path)) { - /* Expand the Group */ - if (PURPLE_IS_META_CONTACT(node)) { - pidgin_blist_expand_contact_cb(NULL, node); - gtk_tree_path_free(path); - return TRUE; - } else if (!PURPLE_IS_BUDDY(node)) { - gtk_tree_view_expand_row(tv, path, FALSE); - gtk_tree_path_free(path); - return TRUE; - } - } else { - /* Select the First Child */ - if (gtk_tree_model_get_iter(model, &parent, path)) { - if (gtk_tree_model_iter_nth_child(model, &iter, &parent, 0)) { - gtk_tree_path_free(path); - path = gtk_tree_model_get_path(model, &iter); - gtk_tree_view_set_cursor(tv, path, NULL, FALSE); - gtk_tree_path_free(path); - return TRUE; - } - } - } - gtk_tree_path_free(path); - break; - } - } - - return FALSE; -} - -static gboolean -pidgin_blist_popover_closed_idle_cb(gpointer data) { - gtk_widget_unparent(GTK_WIDGET(data)); - return G_SOURCE_REMOVE; -} - -static void -pidgin_blist_popover_closed_cb(GtkPopover *self, G_GNUC_UNUSED gpointer data) { - /* We cannot unparent this immediately on close, as it will be garbage - * collected, and the selected action will not run. - */ - g_idle_add(G_SOURCE_FUNC(pidgin_blist_popover_closed_idle_cb), self); -} - -static gboolean -pidgin_blist_show_context_menu(GtkWidget *tv, PurpleBlistNode *node, - gdouble x, gdouble y) -{ - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - GAction *action = NULL; - GActionMap *action_map = NULL; - GtkApplication *gtk_application = NULL; - GMenu *menu = NULL; - gboolean enabled = FALSE; - gboolean handled = FALSE; - - gtk_application = GTK_APPLICATION(g_application_get_default()); - - action_map = G_ACTION_MAP(gtkblist->action_group); - - /* Create a menu based on the thing we right-clicked on */ - if (PURPLE_IS_GROUP(node)) { - menu = gtk_application_get_menu_by_id(gtk_application, "group"); - - /* Add buddy is only enabled if there is an online account. */ - action = g_action_map_lookup_action(action_map, "add-buddy"); - enabled = purple_connections_get_all() != NULL; - g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled); - - /* Add chat is only enabled if at least one protocol supports chats. */ - action = g_action_map_lookup_action(action_map, "add-chat"); - enabled = pidgin_blist_joinchat_is_showable(); - g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled); - } else if (PURPLE_IS_CHAT(node)) { - GVariant *variant = NULL; - - menu = gtk_application_get_menu_by_id(gtk_application, "chat"); - - /* Set the auto-join state to the correct value. */ - action = g_action_map_lookup_action(action_map, "auto-join-chat"); - enabled = purple_blist_node_get_bool(node, "gtk-autojoin"); - variant = g_variant_new_boolean(enabled); - g_simple_action_set_state(G_SIMPLE_ACTION(action), variant); - - /* Set the persistent state to the correct value. */ - action = g_action_map_lookup_action(action_map, "persistent-chat"); - enabled = purple_blist_node_get_bool(node, "gtk-persistent"); - variant = g_variant_new_boolean(enabled); - g_simple_action_set_state(G_SIMPLE_ACTION(action), variant); - } else if ((PURPLE_IS_META_CONTACT(node)) && (gtknode->contact_expanded)) { - menu = gtk_application_get_menu_by_id(gtk_application, "contact"); - } else if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_BUDDY(node)) { - PurpleAccount *account = NULL; - PurpleBuddy *buddy = NULL; - PurpleConnection *connection = NULL; - PurpleProtocol *protocol = NULL; - const gchar *buddy_name = NULL; - - menu = gtk_application_get_menu_by_id(gtk_application, "buddy"); - - if(PURPLE_IS_META_CONTACT(node)) { - buddy = purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node)); - } else { - buddy = PURPLE_BUDDY(node); - } - - account = purple_buddy_get_account(buddy); - buddy_name = purple_buddy_get_name(buddy); - connection = purple_account_get_connection(account); - protocol = purple_connection_get_protocol(connection); - - /* Get info is optional for protocols to support. */ - action = g_action_map_lookup_action(action_map, "buddy-get-info"); - enabled = protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, - get_info); - g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled); - - /* File transfers are protocol dependent. */ - action = g_action_map_lookup_action(action_map, - "buddy-send-file"); - if(protocol && PURPLE_IS_PROTOCOL_XFER(protocol)) { - enabled = purple_protocol_xfer_can_receive(PURPLE_PROTOCOL_XFER(protocol), - connection, buddy_name); - } else { - enabled = FALSE; - } - - g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled); - - if(protocol != NULL && - PURPLE_PROTOCOL_IMPLEMENTS(protocol, MEDIA, get_caps)) - { - PurpleMediaCaps caps = 0; - - caps = purple_protocol_get_media_caps(account, buddy_name); - - /* Voice calls are dependent on protocol. */ - action = g_action_map_lookup_action(action_map, "buddy-audio-call"); - enabled = (caps & PURPLE_MEDIA_CAPS_AUDIO); - g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled); - - /* Video calls are dependent on protocol. */ - action = g_action_map_lookup_action(action_map, "buddy-video-call"); - enabled = (caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) || - (caps & PURPLE_MEDIA_CAPS_VIDEO); - g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled); - } - } - - action = g_action_map_lookup_action(action_map, - "remove-custom-icon"); - enabled = purple_buddy_icons_node_has_custom_icon(node); - g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled); - - /* Now display the menu */ - if (menu != NULL && x >= 0 && y >= 0) { - GtkWidget *popover_menu = NULL; - - popover_menu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu)); - gtk_widget_set_parent(popover_menu, tv); - gtk_popover_set_position(GTK_POPOVER(popover_menu), GTK_POS_BOTTOM); - gtk_popover_set_pointing_to(GTK_POPOVER(popover_menu), - &(const GdkRectangle){(gint)x, (gint)y, 1, 1}); - - gtk_popover_popup(GTK_POPOVER(popover_menu)); - g_signal_connect(popover_menu, "closed", - G_CALLBACK(pidgin_blist_popover_closed_cb), NULL); - - handled = TRUE; - } - - return handled; -} - -static gboolean -gtk_blist_button_press_cb(GtkGestureClick *click, gint n_press, gdouble x, - gdouble y, gpointer data) -{ - PidginBuddyList *gtkblist = data; - GtkWidget *tv = NULL; - GdkEvent *event = NULL; - GdkModifierType state; - guint button = 0; - GtkTreePath *path; - PurpleBlistNode *node; - GtkTreeIter iter; - GtkTreeSelection *sel; - PurpleProtocol *protocol = NULL; - PidginBlistNode *gtknode; - gboolean handled = FALSE; - - tv = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(click)); - event = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(click)); - button = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(click)); - state = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(click)); - - /* Here we figure out which node was clicked */ - if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), x, y, &path, NULL, - NULL, NULL)) { - return FALSE; - } - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); - gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - - /* Right click draws a context menu */ - if (gdk_event_triggers_context_menu(event)) { - handled = pidgin_blist_show_context_menu(tv, node, x, y); - - /* CTRL+middle click expands or collapse a contact */ - } else if ((button == GDK_BUTTON_MIDDLE) && (n_press == 1) && - (state & GDK_CONTROL_MASK) && PURPLE_IS_META_CONTACT(node)) { - if (gtknode->contact_expanded) - pidgin_blist_collapse_contact_cb(NULL, node); - else - pidgin_blist_expand_contact_cb(NULL, node); - handled = TRUE; - - /* Double middle click gets info */ - } else if ((button == GDK_BUTTON_MIDDLE) && (n_press == 2) && - (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_BUDDY(node))) { - PurpleAccount *account; - PurpleBuddy *b; - if(PURPLE_IS_META_CONTACT(node)) - b = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node); - else - b = (PurpleBuddy *)node; - - account = purple_buddy_get_account(b); - protocol = purple_account_get_protocol(account); - - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info)) - pidgin_retrieve_user_info(purple_account_get_connection(account), purple_buddy_get_name(b)); - handled = TRUE; - } - -#if (1) - /* - * This code only exists because GTK doesn't work. If we return - * FALSE here, as would be normal the event propoagates down and - * somehow gets interpreted as the start of a drag event. - * - * Um, isn't it _normal_ to return TRUE here? Since the event - * was handled? --Mark - */ - if(handled) { - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); - gtk_tree_selection_select_path(sel, path); - gtk_tree_path_free(path); - return TRUE; - } -#endif - gtk_tree_path_free(path); - - return handled; -} - -/* Altered from do_colorshift in gnome-panel */ -static void -do_alphashift(GdkPixbuf *pixbuf, int shift) -{ - gint i, j; - gint width, height, padding; - guchar *pixels; - int val; - - if (!gdk_pixbuf_get_has_alpha(pixbuf)) - return; - - width = gdk_pixbuf_get_width(pixbuf); - height = gdk_pixbuf_get_height(pixbuf); - padding = gdk_pixbuf_get_rowstride(pixbuf) - width * 4; - pixels = gdk_pixbuf_get_pixels(pixbuf); - - for (i = 0; i < height; i++) { - for (j = 0; j < width; j++) { - pixels++; - pixels++; - pixels++; - val = *pixels - shift; - *(pixels++) = CLAMP(val, 0, 255); - } - pixels += padding; - } -} - - -static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node, - gboolean scaled, gboolean greyed) -{ - gsize len; - PurpleBuddy *buddy = NULL; - PurpleGroup *group = NULL; - const guchar *data = NULL; - GdkPixbuf *buf, *ret = NULL; - PurpleBuddyIcon *icon = NULL; - PurpleAccount *account = NULL; - PurpleMetaContact *contact = NULL; - PurpleImage *custom_img; - PurpleProtocol *protocol = NULL; - PurpleBuddyIconSpec *icon_spec = NULL; - gint orig_width, orig_height, scale_width, scale_height; - - if (PURPLE_IS_META_CONTACT(node)) { - buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node); - contact = (PurpleMetaContact*)node; - } else if (PURPLE_IS_BUDDY(node)) { - buddy = (PurpleBuddy*)node; - contact = purple_buddy_get_contact(buddy); - } else if (PURPLE_IS_GROUP(node)) { - group = (PurpleGroup*)node; - } else if (PURPLE_IS_CHAT(node)) { - /* We don't need to do anything here. We just need to not fall - * into the else block and return. */ - } else { - return NULL; - } - - if (buddy) { - account = purple_buddy_get_account(buddy); - } - - if(account && purple_account_get_connection(account)) { - protocol = purple_connection_get_protocol(purple_account_get_connection(account)); - } - - /* If we have a contact then this is either a contact or a buddy and - * we want to fetch the custom icon for the contact. If we don't have - * a contact then this is a group or some other type of node and we - * want to use that directly. */ - if (contact) { - custom_img = purple_buddy_icons_node_find_custom_icon(PURPLE_BLIST_NODE(contact)); - } else { - custom_img = purple_buddy_icons_node_find_custom_icon(node); - } - - if (custom_img) { - data = purple_image_get_data(custom_img); - len = purple_image_get_data_size(custom_img); - } - - if (data == NULL) { - if (buddy) { - /* Not sure I like this...*/ - if (!(icon = purple_buddy_icons_find(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy)))) - return NULL; - data = purple_buddy_icon_get_data(icon, &len); - } - - if(data == NULL) - return NULL; - } - - buf = purple_gdk_pixbuf_from_data(data, len); - purple_buddy_icon_unref(icon); - if (!buf) { - const char *account_name = "(no account)"; - - if(PURPLE_IS_ACCOUNT(account)) { - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account); - - account_name = purple_contact_info_get_username(info); - } - - purple_debug_warning("gtkblist", "Couldn't load buddy icon on " - "account %s (%s); buddyname=%s; custom_img_size=%" G_GSIZE_FORMAT, - account_name, - account ? purple_account_get_protocol_id(account) : "(no account)", - buddy ? purple_buddy_get_name(buddy) : "(no buddy)", - custom_img ? purple_image_get_data_size(custom_img) : 0); - if (custom_img) - g_object_unref(custom_img); - return NULL; - } - if (custom_img) - g_object_unref(custom_img); - - if (greyed) { - gboolean offline = FALSE, idle = FALSE; - - if (buddy) { - PurplePresence *presence = purple_buddy_get_presence(buddy); - if (!PURPLE_BUDDY_IS_ONLINE(buddy)) - offline = TRUE; - if (purple_presence_is_idle(presence)) - idle = TRUE; - } else if (group) { - if (purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)) == 0) - offline = TRUE; - } - - if (offline) - gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); - - if (idle) - gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); - } - - scale_width = orig_width = gdk_pixbuf_get_width(buf); - scale_height = orig_height = gdk_pixbuf_get_height(buf); - - if (protocol) - icon_spec = purple_protocol_get_icon_spec(protocol); - - if (icon_spec && (icon_spec->scale_rules & PURPLE_ICON_SCALE_DISPLAY)) { - purple_buddy_icon_spec_get_scaled_size(icon_spec, &scale_width, - &scale_height); - } - - purple_buddy_icon_spec_free(icon_spec); - - if (scaled || scale_height > 200 || scale_width > 200) { - GdkPixbuf *tmpbuf; - float scale_size = scaled ? 32.0 : 200.0; - if(scale_height > scale_width) { - scale_width = scale_size * (double)scale_width / (double)scale_height; - scale_height = scale_size; - } else { - scale_height = scale_size * (double)scale_height / (double)scale_width; - scale_width = scale_size; - } - /* Scale & round before making square, so rectangular (but - * non-square) images get rounded corners too. */ - tmpbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height); - gdk_pixbuf_fill(tmpbuf, 0x00000000); - gdk_pixbuf_scale(buf, tmpbuf, 0, 0, scale_width, scale_height, 0, 0, (double)scale_width/(double)orig_width, (double)scale_height/(double)orig_height, GDK_INTERP_BILINEAR); - if (purple_gdk_pixbuf_is_opaque(tmpbuf)) - purple_gdk_pixbuf_make_round(tmpbuf); - ret = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_size, scale_size); - gdk_pixbuf_fill(ret, 0x00000000); - gdk_pixbuf_copy_area(tmpbuf, 0, 0, scale_width, scale_height, ret, (scale_size-scale_width)/2, (scale_size-scale_height)/2); - g_object_unref(G_OBJECT(tmpbuf)); - } else { - ret = gdk_pixbuf_scale_simple(buf,scale_width,scale_height, GDK_INTERP_BILINEAR); - } - g_object_unref(G_OBJECT(buf)); - - return ret; -} - -static gboolean buddy_is_displayable(PurpleBuddy *buddy) -{ - if(!buddy) - return FALSE; - - return purple_account_is_connected(purple_buddy_get_account(buddy)); -} - -/* # - Status Icon - * P - Protocol Icon - * A - Buddy Icon - * [ - SMALL_SPACE - * = - LARGE_SPACE - * +--- STATUS_SIZE +--- td->avatar_width - * | +-- td->name_width | - * +----+ +-------+ +---------+ - * | | | | | | - * +-------------------------------------------+ - * | [ = [ |--- TOOLTIP_BORDER - *name_height --+-| ######[BuddyName = PP [ AAAAAAAAAAA |--+ - * | | ######[ = PP [ AAAAAAAAAAA | | - * STATUS SIZE -| | ######[[[[[[[[[[[[[[[[[[[[[ AAAAAAAAAAA | | - * +--+-| ######[Account: So-and-so [ AAAAAAAAAAA | |-- td->avatar_height - * | | [Idle: 4h 15m [ AAAAAAAAAAA | | - * height --+ | [Foo: Bar, Baz [ AAAAAAAAAAA | | - * | | [Status: Awesome [ AAAAAAAAAAA |--+ - * +----| [Stop: Hammer Time [ | - * | [ [ |--- TOOLTIP_BORDER - * +-------------------------------------------+ - * | | | | - * | +----------------+ | - * | | | - * | +-- td->width | - * | | - * +---- TOOLTIP_BORDER +---- TOOLTIP_BORDER - * - * - */ -#define STATUS_SIZE 16 -#define TOOLTIP_BORDER 12 -#define SMALL_SPACE 6 -#define LARGE_SPACE 12 - -static void -add_tip_for_account(GtkWidget *grid, gint row, PurpleAccount *account) -{ - PurpleContactInfo *info = NULL; - PurpleProtocol *protocol = NULL; - GtkWidget *image = NULL; - GtkWidget *name = NULL; - const gchar *icon_name = NULL; - - protocol = purple_account_get_protocol(account); - icon_name = purple_protocol_get_icon_name(protocol); - - image = gtk_image_new_from_icon_name(icon_name); - gtk_image_set_pixel_size(GTK_IMAGE(image), STATUS_SIZE); - gtk_grid_attach(GTK_GRID(grid), image, 0, row, 1, 1); - - info = PURPLE_CONTACT_INFO(account); - name = gtk_label_new(purple_contact_info_get_username(info)); - gtk_label_set_xalign(GTK_LABEL(name), 0); - gtk_label_set_yalign(GTK_LABEL(name), 0); - gtk_label_set_wrap(GTK_LABEL(name), TRUE); - gtk_label_set_max_width_chars(GTK_LABEL(name), 36); - gtk_grid_attach(GTK_GRID(grid), name, 1, row, 1, 1); -} - -static const gchar * -pidgin_blist_get_status_icon_name(PurpleBlistNode *node) { - const char *icon = NULL; - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - PidginBlistNode *gtkbuddynode = NULL; - PurpleBuddy *buddy = NULL; - PurpleChat *chat = NULL; - - if(PURPLE_IS_META_CONTACT(node)) { - if(!gtknode->contact_expanded) { - buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node); - if(buddy != NULL) { - gtkbuddynode = g_object_get_data(G_OBJECT(buddy), UI_DATA); - } - } - } else if(PURPLE_IS_BUDDY(node)) { - buddy = (PurpleBuddy*)node; - gtkbuddynode = g_object_get_data(G_OBJECT(node), UI_DATA); - } else if(PURPLE_IS_CHAT(node)) { - chat = (PurpleChat*)node; - } else { - return NULL; - } - - if(buddy || chat) { - PurpleAccount *account; - PurpleProtocol *protocol; - - if(buddy) - account = purple_buddy_get_account(buddy); - else - account = purple_chat_get_account(chat); - - protocol = purple_account_get_protocol(account); - if(!protocol) - return NULL; - } - - if(buddy) { - PurplePresence *p = purple_buddy_get_presence(buddy); - - if (PURPLE_BUDDY_IS_ONLINE(buddy) && gtkbuddynode && gtkbuddynode->recent_signonoff) { - icon = "log-in"; - } else if (gtkbuddynode && gtkbuddynode->recent_signonoff) { - icon = "log-out"; - } else { - icon = pidgin_icon_name_from_presence(p, "pidgin-user-available"); - } - } else if (chat) { - icon = "chat"; - } else { - icon = "person"; - } - - return icon; -} - -static void -add_tip_for_node(GtkWidget *grid, gint row, PurpleBlistNode *node, gboolean full) -{ - GtkWidget *image = NULL; - GtkWidget *name = NULL; - GdkPixbuf *avatar = NULL; - GtkWidget *avatar_image = NULL; - PurpleAccount *account = NULL; - char *tmp = NULL, *node_name = NULL, *tooltip_text = NULL; - const gchar *status_icon = NULL; - - if (PURPLE_IS_BUDDY(node)) { - account = purple_buddy_get_account(PURPLE_BUDDY(node)); - } else if (PURPLE_IS_CHAT(node)) { - account = purple_chat_get_account(PURPLE_CHAT(node)); - } - - status_icon = pidgin_blist_get_status_icon_name(node); - image = gtk_image_new_from_icon_name(status_icon); - gtk_image_set_pixel_size(GTK_IMAGE(image), STATUS_SIZE); - gtk_grid_attach(GTK_GRID(grid), image, 0, row, 1, 1); - - if (PURPLE_IS_BUDDY(node)) { - tmp = g_markup_escape_text(purple_buddy_get_name(PURPLE_BUDDY(node)), -1); - } else if (PURPLE_IS_CHAT(node)) { - tmp = g_markup_escape_text(purple_chat_get_name(PURPLE_CHAT(node)), -1); - } else if (PURPLE_IS_GROUP(node)) { - tmp = g_markup_escape_text(purple_group_get_name(PURPLE_GROUP(node)), -1); - } else { - /* I don't believe this can happen currently, I think - * everything that calls this function checks for one of the - * above node types first. */ - tmp = g_strdup(_("Unknown node type")); - } - node_name = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>", - tmp ? tmp : ""); - g_free(tmp); - - name = gtk_label_new(NULL); - gtk_label_set_xalign(GTK_LABEL(name), 0); - gtk_label_set_yalign(GTK_LABEL(name), 0); - gtk_label_set_markup(GTK_LABEL(name), node_name); - gtk_label_set_max_width_chars(GTK_LABEL(name), 36); - gtk_grid_attach(GTK_GRID(grid), name, 1, row, 1, 1); - - if (account != NULL) { - PurpleProtocol *protocol = purple_account_get_protocol(account); - const gchar *icon_name = purple_protocol_get_icon_name(protocol); - - image = gtk_image_new_from_icon_name(icon_name); - gtk_image_set_pixel_size(GTK_IMAGE(image), STATUS_SIZE); - gtk_widget_set_halign(image, GTK_ALIGN_END); - gtk_grid_attach(GTK_GRID(grid), image, 2, row, 1, 1); - } - - tooltip_text = pidgin_get_tooltip_text(node, full); - if (tooltip_text && *tooltip_text) { - GtkWidget *contents = gtk_label_new(NULL); - gtk_label_set_xalign(GTK_LABEL(contents), 0); - gtk_label_set_yalign(GTK_LABEL(contents), 0); - gtk_label_set_markup(GTK_LABEL(contents), tooltip_text); - gtk_label_set_wrap(GTK_LABEL(contents), TRUE); - gtk_label_set_max_width_chars(GTK_LABEL(contents), 36); - gtk_grid_attach(GTK_GRID(grid), contents, 1, row+1, 2, 1); - } - - avatar = pidgin_blist_get_buddy_icon(node, !full, FALSE); - if (avatar != NULL) { - avatar_image = gtk_image_new_from_pixbuf(avatar); - gtk_widget_set_halign(avatar_image, GTK_ALIGN_END); - gtk_widget_set_valign(avatar_image, GTK_ALIGN_START); - gtk_grid_attach(GTK_GRID(grid), avatar_image, 3, row, 1, 2); - g_object_unref(avatar); - } - - g_free(node_name); - g_free(tooltip_text); -} - -gboolean -pidgin_blist_query_tooltip_for_node(PurpleBlistNode *node, GtkTooltip *tooltip) -{ - GtkWidget *grid; - - grid = gtk_grid_new(); - - if (PURPLE_IS_CHAT(node) || PURPLE_IS_BUDDY(node)) { - add_tip_for_node(grid, 0, node, TRUE); - - } else if (PURPLE_IS_GROUP(node)) { - PurpleGroup *group = PURPLE_GROUP(node); - GSList *accounts; - add_tip_for_node(grid, 0, node, TRUE); - - /* Accounts with buddies in group */ - accounts = purple_group_get_accounts(group); - for (gint row = 2; accounts != NULL; - row++, accounts = g_slist_delete_link(accounts, accounts)) - { - PurpleAccount *account = PURPLE_ACCOUNT(accounts->data); - add_tip_for_account(grid, row, account); - } - - } else if (PURPLE_IS_META_CONTACT(node)) { - PurpleBlistNode *child; - PurpleBuddy *b = purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node)); - gint row = 0; - - for(child = node->child; child; child = child->next) { - if(PURPLE_IS_BUDDY(child) && buddy_is_displayable(PURPLE_BUDDY(child))) { - if (b == (PurpleBuddy *)child) { - /* Priority buddy goes first (-2) and is more detailed. */ - add_tip_for_node(grid, -2, child, TRUE); - } else { - add_tip_for_node(grid, row, child, FALSE); - row += 2; - } - } - } - - } else { - return FALSE; - } - - gtk_tooltip_set_custom(tooltip, grid); - - return TRUE; -} - -static gboolean -pidgin_blist_query_tooltip(GtkWidget *widget, int x, int y, - gboolean keyboard_mode, GtkTooltip *tooltip, - gpointer userdata) -{ - PidginBuddyList *blist = userdata; - GtkTreeIter iter; - PurpleBlistNode *node; - gboolean editable = FALSE; - GtkTreePath *path = NULL; - - /* If we're editing a cell (e.g. alias editing), don't show the tooltip */ - g_object_get(G_OBJECT(blist->text_rend), "editable", &editable, NULL); - if (editable) { - return FALSE; - } - - if (keyboard_mode) { - GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); - if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { - return FALSE; - } - path = gtk_tree_model_get_path(GTK_TREE_MODEL(blist->treemodel), &iter); - } else { - gint bx, by; - - gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(widget), - x, y, &bx, &by); - gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bx, by, &path, - NULL, NULL, NULL); - if (path == NULL) { - return FALSE; - } - if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(blist->treemodel), - &iter, path)) - { - gtk_tree_path_free(path); - return FALSE; - } - } - - gtk_tree_model_get(GTK_TREE_MODEL(blist->treemodel), &iter, - NODE_COLUMN, &node, -1); - if (pidgin_blist_query_tooltip_for_node(node, tooltip)) { - gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(widget), tooltip, path); - gtk_tree_path_free(path); - return TRUE; - } else { - gtk_tree_path_free(path); - return FALSE; - } -} - -/********************************************************* - * Private Utility functions * - *********************************************************/ - -static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full) -{ - GString *str = g_string_new(""); - PurpleProtocol *protocol = NULL; - char *tmp; - - if (PURPLE_IS_CHAT(node)) - { - PurpleAccount *account; - PurpleChat *chat; - GList *connections; - GList *cur = NULL; - PurpleProtocolChatEntry *pce; - char *name, *value; - PurpleChatConversation *conv = NULL; - PidginBlistNode *bnode = g_object_get_data(G_OBJECT(node), UI_DATA); - - chat = (PurpleChat *)node; - account = purple_chat_get_account(chat); - protocol = purple_account_get_protocol(account); - - connections = purple_connections_get_all(); - if (connections && connections->next) - { - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account); - tmp = g_markup_escape_text(purple_contact_info_get_username(info), - -1); - g_string_append_printf(str, _("<b>Account:</b> %s"), tmp); - g_free(tmp); - } - - if (bnode && bnode->conv) { - conv = PURPLE_CHAT_CONVERSATION(bnode->conv); - } else { - PurpleConversation *chat_conv; - PurpleConversationManager *manager; - char *chat_name; - - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, get_name)) { - chat_name = purple_protocol_chat_get_name(PURPLE_PROTOCOL_CHAT(protocol), - purple_chat_get_components(chat)); - } else { - chat_name = g_strdup(purple_chat_get_name(chat)); - } - - manager = purple_conversation_manager_get_default(); - chat_conv = purple_conversation_manager_find_chat(manager, account, - chat_name); - g_free(chat_name); - - if(PURPLE_IS_CHAT_CONVERSATION(chat_conv)) { - conv = PURPLE_CHAT_CONVERSATION(chat_conv); - } - } - - if (conv && !purple_chat_conversation_has_left(conv)) { - g_string_append_printf(str, _("\n<b>Occupants:</b> %d"), - purple_chat_conversation_get_users_count(conv)); - - if (protocol && (purple_protocol_get_options(protocol) & OPT_PROTO_CHAT_TOPIC)) { - const char *chattopic = purple_chat_conversation_get_topic(conv); - char *topic = chattopic ? g_markup_escape_text(chattopic, -1) : NULL; - g_string_append_printf(str, _("\n<b>Topic:</b> %s"), topic ? topic : _("(no topic set)")); - g_free(topic); - } - } - - if(protocol) { - cur = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol), - purple_account_get_connection(account)); - } - - while (cur != NULL) - { - pce = cur->data; - - if (!pce->secret) - { - tmp = purple_text_strip_mnemonic(pce->label); - name = g_markup_escape_text(tmp, -1); - g_free(tmp); - value = g_markup_escape_text(g_hash_table_lookup( - purple_chat_get_components(chat), pce->identifier), -1); - g_string_append_printf(str, "\n<b>%s</b> %s", - name ? name : "", - value ? value : ""); - g_free(name); - g_free(value); - } - - g_free(pce); - cur = g_list_delete_link(cur, cur); - } - } - else if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_BUDDY(node)) - { - /* NOTE: THIS FUNCTION IS NO LONGER CALLED FOR CONTACTS. - * It is only called by create_tip_for_node(), and create_tip_for_node() is never called for a contact. - */ - PurpleAccount *account; - PurpleMetaContact *c; - PurpleBuddy *b; - PurplePresence *presence; - PurpleNotifyUserInfo *user_info; - GDateTime *signon = NULL; - GList *connections; - char *tmp; - gchar *alias; - time_t idle_secs; - - if (PURPLE_IS_META_CONTACT(node)) - { - c = (PurpleMetaContact *)node; - b = purple_meta_contact_get_priority_buddy(c); - } - else - { - b = (PurpleBuddy *)node; - c = purple_buddy_get_contact(b); - } - - account = purple_buddy_get_account(b); - protocol = purple_account_get_protocol(account); - - presence = purple_buddy_get_presence(b); - user_info = purple_notify_user_info_new(); - - /* Account */ - connections = purple_connections_get_all(); - if (full && connections && connections->next) - { - PurpleContactInfo *info = PURPLE_CONTACT_INFO(account); - const char *username = purple_contact_info_get_username(info); - - purple_notify_user_info_add_pair_plaintext(user_info, _("Account"), - username); - } - - /* Alias */ - /* If there's not a contact alias, the node is being displayed with - * this alias, so there's no point in showing it in the tooltip. */ - g_object_get(c, "alias", &alias, NULL); - if (full && c && purple_buddy_get_local_alias(b) != NULL && purple_buddy_get_local_alias(b)[0] != '\0' && - (alias != NULL && alias[0] != '\0') && - !purple_strequal(alias, purple_buddy_get_local_alias(b))) - { - purple_notify_user_info_add_pair_plaintext(user_info, - _("Buddy Alias"), purple_buddy_get_local_alias(b)); - } - - /* Nickname/Server Alias */ - /* I'd like to only show this if there's a contact or buddy - * alias, but people often set long nicknames, which - * get ellipsized, so the only way to see the whole thing is - * to look at the tooltip. */ - if (full && purple_buddy_get_server_alias(b)) - { - purple_notify_user_info_add_pair_plaintext(user_info, - _("Nickname"), purple_buddy_get_server_alias(b)); - } - - /* Logged In */ - signon = purple_presence_get_login_time(presence); - if (full && PURPLE_BUDDY_IS_ONLINE(b) && signon != NULL) - { - GDateTime *now = g_date_time_new_now_utc(); - GTimeSpan duration = g_date_time_compare(now, signon); - - if(duration < 0) { - /* - * They signed on in the future?! Our local clock - * must be wrong, show the actual date instead of - * "4 days", etc. - */ - GDateTime *local = NULL; - - local = g_date_time_to_local(signon); - - tmp = g_date_time_format(local, _("%x %X")); - g_date_time_unref(local); - } else { - tmp = purple_str_seconds_to_string(duration / G_TIME_SPAN_SECOND); - } - - purple_notify_user_info_add_pair_plaintext(user_info, _("Logged In"), tmp); - g_free(tmp); - - g_date_time_unref(now); - } - - /* Idle */ - if (purple_presence_is_idle(presence)) - { - idle_secs = purple_presence_get_idle_time(presence); - if (idle_secs > 0) - { - tmp = purple_str_seconds_to_string(time(NULL) - idle_secs); - purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), tmp); - g_free(tmp); - } - } - - /* Last Seen */ - if (full && c && !PURPLE_BUDDY_IS_ONLINE(b)) - { - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(c), UI_DATA); - PurpleBlistNode *bnode; - int lastseen = 0; - - if (gtknode && (!gtknode->contact_expanded || PURPLE_IS_META_CONTACT(node))) - { - /* We're either looking at a buddy for a collapsed contact or - * an expanded contact itself so we show the most recent - * (largest) last_seen time for any of the buddies under - * the contact. */ - for (bnode = ((PurpleBlistNode *)c)->child ; bnode != NULL ; bnode = bnode->next) - { - int value = purple_blist_node_get_int(bnode, "last_seen"); - if (value > lastseen) - lastseen = value; - } - } - else - { - /* We're dealing with a buddy under an expanded contact, - * so we show the last_seen time for the buddy. */ - lastseen = purple_blist_node_get_int(&b->node, "last_seen"); - } - - if (lastseen > 0) - { - tmp = purple_str_seconds_to_string(time(NULL) - lastseen); - purple_notify_user_info_add_pair_plaintext(user_info, _("Last Seen"), tmp); - g_free(tmp); - } - } - - - /* Offline? */ - /* FIXME: Why is this status special-cased by the core? --rlaager - * FIXME: Alternatively, why not have the core do all of them? --rlaager */ - if (!PURPLE_BUDDY_IS_ONLINE(b)) { - purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), _("Offline")); - } - - if (purple_account_is_connected(account) && protocol) { - /* Additional text from the protocol */ - purple_protocol_client_tooltip_text(PURPLE_PROTOCOL_CLIENT(protocol), b, user_info, full); - } - - /* These are Easter Eggs. Patches to remove them will be rejected. */ - if (!g_ascii_strcasecmp(purple_buddy_get_name(b), "robflynn")) - purple_notify_user_info_add_pair_plaintext(user_info, _("Description"), _("Spooky")); - if (!g_ascii_strcasecmp(purple_buddy_get_name(b), "seanegn")) - purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), _("Awesome")); - if (!g_ascii_strcasecmp(purple_buddy_get_name(b), "chipx86")) - purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), _("Rockin'")); - - tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n"); - g_string_append(str, tmp); - g_free(tmp); - g_free(alias); - - purple_notify_user_info_destroy(user_info); - } else if (PURPLE_IS_GROUP(node)) { - gint count; - PurpleGroup *group = (PurpleGroup*)node; - PurpleNotifyUserInfo *user_info; - - user_info = purple_notify_user_info_new(); - - count = purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)); - if (count != 0) { - /* Online buddies in group */ - char tmp2[12]; - sprintf(tmp2, "%d", count); - purple_notify_user_info_add_pair_plaintext(user_info, - _("Online Buddies"), tmp2); - } - - count = purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group)); - if (count != 0) { - /* Total buddies (from online accounts) in group */ - char tmp2[12]; - sprintf(tmp2, "%d", count); - purple_notify_user_info_add_pair_html(user_info, - _("Total Buddies"), tmp2); - } - - tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n"); - g_string_append(str, tmp); - g_free(tmp); - - purple_notify_user_info_destroy(user_info); - } - - purple_signal_emit(pidgin_blist_get_handle(), "drawing-tooltip", - node, str, full); - - return g_string_free(str, FALSE); -} - -static GHashTable *cached_emblems; - -static void -_cleanup_cached_emblem(gpointer data, G_GNUC_UNUSED GObject *obj) { - g_hash_table_remove(cached_emblems, data); -} - -static GdkPixbuf * _pidgin_blist_get_cached_emblem(gchar *path) { - GdkPixbuf *pb = g_hash_table_lookup(cached_emblems, path); - - if (pb != NULL) { - /* The caller gets a reference */ - g_object_ref(pb); - g_free(path); - } else { - pb = purple_gdk_pixbuf_new_from_file(path); - if (pb != NULL) { - /* We don't want to own a ref to the pixbuf, but we need to keep clean up. */ - /* I'm not sure if it would be better to just keep our ref and not let the emblem ever be destroyed */ - g_object_weak_ref(G_OBJECT(pb), _cleanup_cached_emblem, path); - g_hash_table_insert(cached_emblems, path, pb); - } else - g_free(path); - } - - return pb; -} - -GdkPixbuf * -pidgin_blist_get_emblem(PurpleBlistNode *node) -{ - PurpleAccount *account; - PurpleBuddy *buddy = NULL; - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - PurpleProtocol *protocol; - const char *name = NULL; - char *filename, *path; - PurplePresence *p = NULL; - PurpleStatus *tune; - - if(PURPLE_IS_META_CONTACT(node)) { - if(!gtknode->contact_expanded) { - buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node); - } - } else if(PURPLE_IS_BUDDY(node)) { - PidginBlistNode *pidgin_node = NULL; - buddy = (PurpleBuddy*)node; - - p = purple_buddy_get_presence(buddy); - if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) { - /* This emblem comes from the small emoticon set now, - * to reduce duplication. */ - path = g_build_filename(PURPLE_DATADIR, "pixmaps", - "pidgin", "emotes", "small", "mobile.png", NULL); - return _pidgin_blist_get_cached_emblem(path); - } - - pidgin_node = g_object_get_data(G_OBJECT(node->parent), UI_DATA); - if(pidgin_node->contact_expanded) { - return NULL; - } - } else { - return NULL; - } - - g_return_val_if_fail(buddy != NULL, NULL); - - account = purple_buddy_get_account(buddy); - - /* If we came through the contact code flow above, we didn't need - * to get the presence until now. */ - if (p == NULL) - p = purple_buddy_get_presence(buddy); - - if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) { - /* This emblem comes from the small emoticon set now, to reduce duplication. */ - path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin", - "emotes", "small", "mobile.png", NULL); - return _pidgin_blist_get_cached_emblem(path); - } - - tune = purple_presence_get_status(p, "tune"); - if (tune && purple_status_is_active(tune)) { - /* TODO: Replace "Tune" with generalized "Media" in 3.0. */ - if (purple_status_get_attr_string(tune, "game") != NULL) { - path = g_build_filename(PURPLE_DATADIR, "pidgin", - "icons", "hicolor", "16x16", "emblems", - "emblem-game.png", NULL); - return _pidgin_blist_get_cached_emblem(path); - } - /* TODO: Replace "Tune" with generalized "Media" in 3.0. */ - if (purple_status_get_attr_string(tune, "office") != NULL) { - path = g_build_filename(PURPLE_DATADIR, "pidgin", - "icons", "hicolor", "16x16", "emblems", - "emblem-office.png", NULL); - return _pidgin_blist_get_cached_emblem(path); - } - /* Regular old "tune" is the only one in all protocols. */ - /* This emblem comes from the small emoticon set now, to reduce duplication. */ - path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin", - "emotes", "small", "music.png", NULL); - return _pidgin_blist_get_cached_emblem(path); - } - - protocol = purple_account_get_protocol(account); - if (!protocol) - return NULL; - - name = purple_protocol_client_list_emblem(PURPLE_PROTOCOL_CLIENT(protocol), buddy); - - if (name == NULL) { - PurpleStatus *status; - - if (!purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOOD)) - return NULL; - - status = purple_presence_get_status(p, "mood"); - name = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); - - if (!(name && *name)) - return NULL; - - path = pidgin_mood_get_icon_path(name); - } else { - filename = g_strdup_printf("emblem-%s.png", name); - path = g_build_filename(PURPLE_DATADIR, "pidgin", "icons", - "hicolor", "16x16", "emblems", filename, NULL); - g_free(filename); - } - - /* _pidgin_blist_get_cached_emblem() assumes ownership of path */ - return _pidgin_blist_get_cached_emblem(path); -} - - -gchar * -pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased) -{ - const char *name, *name_color, *status_color, *dim_grey; - char *text = NULL; - PurpleProtocol *protocol = NULL; - PurpleMetaContact *contact; - PurplePresence *presence; - PidginBlistNode *gtkcontactnode = NULL; - char *idletime = NULL, *statustext = NULL, *nametext = NULL; - gchar *contact_alias; - - /* XXX Good luck cleaning up this crap */ - contact = PURPLE_META_CONTACT(PURPLE_BLIST_NODE(b)->parent); - if(contact) { - gtkcontactnode = g_object_get_data(G_OBJECT(contact), UI_DATA); - } - - g_object_get(contact, "alias", &contact_alias, NULL); - - /* Name */ - if (gtkcontactnode && !gtkcontactnode->contact_expanded && contact_alias) - name = contact_alias; - else - name = purple_buddy_get_alias(b); - - nametext = g_markup_escape_text(name, strlen(name)); - - presence = purple_buddy_get_presence(b); - - /* Name is all that is needed */ - { - PurpleAccount *account = purple_buddy_get_account(b); - - /* Status Info */ - protocol = purple_account_get_protocol(account); - - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, status_text) && - purple_account_get_connection(account)) { - char *tmp = purple_protocol_client_status_text(PURPLE_PROTOCOL_CLIENT(protocol), b); - const char *end; - - if(tmp && !g_utf8_validate(tmp, -1, &end)) { - char *new = g_strndup(tmp, - g_utf8_pointer_to_offset(tmp, end)); - g_free(tmp); - tmp = new; - } - if(tmp) { - g_strdelimit(tmp, "\n", ' '); - purple_str_strip_char(tmp, '\r'); - } - statustext = tmp; - } - - if(!purple_presence_is_online(presence) && !statustext) - statustext = g_strdup(_("Offline")); - - /* Idle Text */ - if (purple_presence_is_idle(presence)) { - time_t idle_secs = purple_presence_get_idle_time(presence); - - if (idle_secs > 0) { - int iday, ihrs, imin; - time_t t; - - time(&t); - iday = (t - idle_secs) / (24 * 60 * 60); - ihrs = ((t - idle_secs) / 60 / 60) % 24; - imin = ((t - idle_secs) / 60) % 60; - - if (iday) - idletime = g_strdup_printf(_("Idle %dd %dh %02dm"), iday, ihrs, imin); - else if (ihrs) - idletime = g_strdup_printf(_("Idle %dh %02dm"), ihrs, imin); - else - idletime = g_strdup_printf(_("Idle %dm"), imin); - - } else - idletime = g_strdup(_("Idle")); - } - } - - dim_grey = "dim grey"; - - /* choose the colors of the text */ - name_color = NULL; - status_color = dim_grey; - - if(!selected) { - if(purple_presence_is_idle(presence) || - !purple_presence_is_online(presence)) - { - name_color = dim_grey; - } - } - - if(aliased && selected) { - name_color = NULL; - status_color = NULL; - } - - /* Put it all together */ - if(statustext || idletime) { - /* using <span size='smaller'> breaks the status, so it must be separated into <small><span>*/ - if (name_color) { - text = g_strdup_printf("<span foreground='%s'>%s</span>\n" - "<small><span foreground='%s'>%s%s%s</span></small>", - name_color, nametext, status_color, - idletime != NULL ? idletime : "", - (idletime != NULL && statustext != NULL) ? " - " : "", - statustext != NULL ? statustext : ""); - } else if (status_color) { - text = g_strdup_printf("%s\n<small><span foreground='%s'>%s%s%s</span></small>", - nametext, status_color, - idletime != NULL ? idletime : "", - (idletime != NULL && statustext != NULL) ? " - " : "", - statustext != NULL ? statustext : ""); - } else { - text = g_strdup_printf("%s\n<small>%s%s%s</small>", - nametext, - idletime != NULL ? idletime : "", - (idletime != NULL && statustext != NULL) ? " - " : "", - statustext != NULL ? statustext : ""); - } - } else { - if (name_color) { - text = g_strdup_printf("<span color='%s'>%s</span>", - name_color, nametext); - } else { - text = g_strdup_printf("%s", nametext); - } - } - g_free(nametext); - g_free(statustext); - g_free(idletime); - g_free(contact_alias); - - return text; -} - -static void pidgin_blist_hide_node(PurpleBuddyList *list, PurpleBlistNode *node, gboolean update) -{ - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - GtkTreeIter iter; - - if (!gtknode || !gtknode->row || !gtkblist) - return; - - if(gtkblist->selected_node == node) - gtkblist->selected_node = NULL; - if (get_iter_from_node(node, &iter)) { - gtk_tree_store_remove(gtkblist->treemodel, &iter); - if(update && (PURPLE_IS_META_CONTACT(node) || - PURPLE_IS_BUDDY(node) || PURPLE_IS_CHAT(node))) { - pidgin_blist_update(list, node->parent); - } - } - gtk_tree_row_reference_free(gtknode->row); - gtknode->row = NULL; -} - -static void -conversation_updated_cb(PurpleConversation *conv, PurpleConversationUpdateType type, - G_GNUC_UNUSED PidginBuddyList *gtkblist) -{ - PurpleAccount *account = purple_conversation_get_account(conv); - - if (type != PURPLE_CONVERSATION_UPDATE_UNSEEN) - return; - - if(account != NULL && purple_conversation_get_name(conv) != NULL) { - PurpleBuddy *buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv)); - if(buddy != NULL) - pidgin_blist_update_buddy(NULL, PURPLE_BLIST_NODE(buddy), TRUE); - } -} - -static void -conversation_deleting_cb(PurpleConversation *conv, PidginBuddyList *gtkblist) -{ - conversation_updated_cb(conv, PURPLE_CONVERSATION_UPDATE_UNSEEN, gtkblist); -} - -static void -conversation_deleted_update_ui_cb(PurpleConversation *conv, PidginBlistNode *ui) -{ - if (ui->conv != conv) - return; - ui->conv = NULL; -} - -static void -conversation_created_cb(PurpleConversation *conv, - G_GNUC_UNUSED PidginBuddyList *gtkblist) -{ - PurpleAccount *account = purple_conversation_get_account(conv); - - if (PURPLE_IS_IM_CONVERSATION(conv)) { - GSList *buddies = purple_blist_find_buddies(account, purple_conversation_get_name(conv)); - while (buddies) { - PurpleBlistNode *buddy = buddies->data; - PidginBlistNode *ui = g_object_get_data(G_OBJECT(buddy), UI_DATA); - buddies = g_slist_delete_link(buddies, buddies); - if (!ui) - continue; - ui->conv = conv; - - purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", - ui, G_CALLBACK(conversation_deleted_update_ui_cb), ui); - } - } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) { - PurpleChat *chat = purple_blist_find_chat(account, purple_conversation_get_name(conv)); - PidginBlistNode *ui; - if (!chat) - return; - ui = g_object_get_data(G_OBJECT(chat), UI_DATA); - if (!ui) - return; - ui->conv = conv; - - purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", - ui, G_CALLBACK(conversation_deleted_update_ui_cb), ui); - } -} - -/********************************************************************************** - * Public API Functions * - **********************************************************************************/ -static void -pidgin_blist_node_free(PidginBlistNode *node) { - if(node->recent_signonoff_timer > 0) { - g_source_remove(node->recent_signonoff_timer); - } - - purple_signals_disconnect_by_handle(node); - - g_free(node); -} - -static void -pidgin_blist_new_node(G_GNUC_UNUSED PurpleBuddyList *list, - PurpleBlistNode *node) -{ - PidginBlistNode *pidgin_node = NULL; - - pidgin_node = g_new0(PidginBlistNode, 1); - - g_object_set_data_full(G_OBJECT(node), UI_DATA, pidgin_node, - (GDestroyNotify)pidgin_blist_node_free); -} - -gboolean -pidgin_blist_node_is_contact_expanded(PurpleBlistNode *node) { - PidginBlistNode *pidgin_node = NULL; - - if (PURPLE_IS_BUDDY(node)) { - node = node->parent; - if (node == NULL) - return FALSE; - } - - g_return_val_if_fail(PURPLE_IS_META_CONTACT(node), FALSE); - - pidgin_node = g_object_get_data(G_OBJECT(node), UI_DATA); - - return pidgin_node->contact_expanded; -} - -void -pidgin_blist_setup_sort_methods(void) -{ - const char *id; - - pidgin_blist_sort_method_reg("none", _("Manually"), sort_method_none); - pidgin_blist_sort_method_reg("alphabetical", _("Alphabetically"), sort_method_alphabetical); - pidgin_blist_sort_method_reg("status", _("By status"), sort_method_status); - - id = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type"); - if (id == NULL) { - purple_debug_warning("gtkblist", "Sort method was NULL, resetting to alphabetical\n"); - id = "alphabetical"; - } - pidgin_blist_sort_method_set(id); -} - -static void -_prefs_change_sort_method(const char *pref_name, - G_GNUC_UNUSED PurplePrefType type, gconstpointer val, - G_GNUC_UNUSED gpointer data) -{ - if(purple_strequal(pref_name, PIDGIN_PREFS_ROOT "/blist/sort_type")) - pidgin_blist_sort_method_set(val); -} - -/* This assumes there are not things like groupless buddies or multi-leveled groups. - * I'm sure other things in this code assumes that also. - */ -static void -icon_theme_changed_cb(G_GNUC_UNUSED GtkIconTheme *self, gpointer data) { - PurpleBuddyList *list = data; - PurpleBlistNode *node = purple_blist_get_root(list); - - while (node) { - pidgin_blist_update_group(list, node); - node = node->next; - } -} - -/******************************************/ -/* End of connection error handling stuff */ -/******************************************/ - -/* builds the blist layout according to to the current theme */ -static void -pidgin_blist_build_layout(PurpleBuddyList *list) -{ - GtkTreeViewColumn *column; - GtkCellRenderer *rend; - - column = gtkblist->text_column; - - gtk_tree_view_column_clear(column); - - /* status icons */ - rend = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "icon-name", STATUS_ICON_COLUMN, - "visible", STATUS_ICON_VISIBLE_COLUMN, - NULL); - g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); - - /* name */ - gtkblist->text_rend = rend = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(column, rend, TRUE); - gtk_tree_view_column_set_attributes(column, rend, - "markup", NAME_COLUMN, - NULL); - g_signal_connect(G_OBJECT(rend), "editing-started", - G_CALLBACK(gtk_blist_renderer_editing_started_cb), list); - g_signal_connect(G_OBJECT(rend), "editing-canceled", - G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list); - g_signal_connect(G_OBJECT(rend), "edited", - G_CALLBACK(gtk_blist_renderer_edited_cb), list); - g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); - g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); - - /* emblem */ - rend = gtk_cell_renderer_pixbuf_new(); - g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, - NULL); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "pixbuf", EMBLEM_COLUMN, - "visible", EMBLEM_VISIBLE_COLUMN, NULL); - - /* protocol icon */ - rend = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "icon-name", PROTOCOL_ICON_NAME_COLUMN, - NULL); - g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL); - - /* buddy icon */ - rend = gtk_cell_renderer_pixbuf_new(); - g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "pixbuf", BUDDY_ICON_COLUMN, - NULL); -} - -static gboolean -pidgin_blist_search_equal_func(GtkTreeModel *model, gint column, - const gchar *key, GtkTreeIter *iter, gpointer data) -{ - PurpleBlistNode *node = NULL; - gboolean res = TRUE; - const char *compare = NULL; - - if (!pidgin_tree_view_search_equal_func(model, column, key, iter, data)) - return FALSE; - - /* If the search string does not match the displayed label, then look - * at the alternate labels for the nodes and search in them. Currently, - * alternate labels that make sense are usernames/email addresses for - * buddies (but only for the ones who don't have a local alias). - */ - - gtk_tree_model_get(model, iter, NODE_COLUMN, &node, -1); - if (!node) - return TRUE; - - compare = NULL; - if (PURPLE_IS_META_CONTACT(node)) { - PurpleBuddy *b = purple_meta_contact_get_priority_buddy(PURPLE_META_CONTACT(node)); - if (!purple_buddy_get_local_alias(b)) - compare = purple_buddy_get_name(b); - } else if (PURPLE_IS_BUDDY(node)) { - if (!purple_buddy_get_local_alias(PURPLE_BUDDY(node))) - compare = purple_buddy_get_name(PURPLE_BUDDY(node)); - } - - if (compare) { - char *tmp, *enteredstring; - tmp = g_utf8_normalize(key, -1, G_NORMALIZE_DEFAULT); - enteredstring = g_utf8_casefold(tmp, -1); - g_free(tmp); - - if (g_str_has_prefix(compare, enteredstring)) { - res = FALSE; - } - g_free(enteredstring); - } - - return res; -} - -static void -pidgin_blist_populate_menus(void) { - GtkApplication *application = NULL; - GMenu *source = NULL, *target = NULL; - - application = GTK_APPLICATION(g_application_get_default()); - - /* Add the icon menu to all the menus that need it. */ - source = gtk_application_get_menu_by_id(application, "custom-icon"); - - /* The group context menu. */ - target = gtk_application_get_menu_by_id(application, "group-custom-icon"); - g_menu_append_section(target, NULL, G_MENU_MODEL(source)); - - /* The chat context menu. */ - target = gtk_application_get_menu_by_id(application, "chat-custom-icon"); - g_menu_append_section(target, NULL, G_MENU_MODEL(source)); - - /* The contact context menu. */ - target = gtk_application_get_menu_by_id(application, "contact-custom-icon"); - g_menu_append_section(target, NULL, G_MENU_MODEL(source)); - - /* The buddy context menu. */ - target = gtk_application_get_menu_by_id(application, "buddy-custom-icon"); - g_menu_append_section(target, NULL, G_MENU_MODEL(source)); - - /* Add the voice and video menu to the buddy menu. */ - source = gtk_application_get_menu_by_id(application, "voice-video"); - target = gtk_application_get_menu_by_id(application, "buddy-voice-video"); - g_menu_append_section(target, NULL, G_MENU_MODEL(source)); -} - -static void pidgin_blist_show(PurpleBuddyList *list) -{ - void *handle; - GtkWidget *sep, *sw; - GtkEventController *key_controller = NULL; - GtkGesture *click = NULL; - GtkIconTheme *icon_theme; - GtkTreeSelection *selection; - - gtkblist = PIDGIN_BUDDY_LIST(list); - - gtkblist->window = pidgin_contact_list_window_new(); - - /* the main vbox is already packed and shown via glade, we just need a - * reference to it to pack the rest of our widgets here. - */ - gtkblist->vbox = pidgin_contact_list_window_get_vbox(PIDGIN_CONTACT_LIST_WINDOW(gtkblist->window)); - - /****************************** GtkTreeView **********************************/ - gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, - G_TYPE_STRING, /* Status icon */ - G_TYPE_BOOLEAN, /* Status icon visible */ - G_TYPE_STRING, /* Name */ - GDK_TYPE_PIXBUF, /* Buddy icon */ - G_TYPE_POINTER, /* Node */ - GDK_TYPE_PIXBUF, /* Emblem */ - G_TYPE_BOOLEAN, /* Emblem visible */ - G_TYPE_STRING /* Protocol icon */ - ); - - gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel)); - gtk_widget_set_name(gtkblist->treeview, "pidgin_blist_treeview"); - - icon_theme = gtk_icon_theme_get_for_display(gdk_display_get_default()); - g_signal_connect(icon_theme, "changed", - G_CALLBACK(icon_theme_changed_cb), list); - - /* Set up selection stuff */ - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); - g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(pidgin_blist_selection_changed), NULL); - - /* Tooltips */ - gtk_widget_set_has_tooltip(gtkblist->treeview, TRUE); - g_signal_connect(G_OBJECT(gtkblist->treeview), "query-tooltip", - G_CALLBACK(pidgin_blist_query_tooltip), gtkblist); - - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); - - /* everything else column */ - gtkblist->text_column = gtk_tree_view_column_new (); - gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->text_column); - pidgin_blist_build_layout(list); - - g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", - G_CALLBACK(gtk_blist_row_activated_cb), gtkblist); - g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", - G_CALLBACK(gtk_blist_row_expanded_cb), gtkblist); - g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", - G_CALLBACK(gtk_blist_row_collapsed_cb), gtkblist); - - click = gtk_gesture_click_new(); - gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(click), 0); - g_signal_connect(click, "pressed", G_CALLBACK(gtk_blist_button_press_cb), - gtkblist); - gtk_widget_add_controller(gtkblist->treeview, GTK_EVENT_CONTROLLER(click)); - - key_controller = gtk_event_controller_key_new(); - g_signal_connect(G_OBJECT(key_controller), "key-pressed", - G_CALLBACK(pidgin_blist_key_press_cb), gtkblist); - gtk_widget_add_controller(gtkblist->treeview, key_controller); - - /* Enable CTRL+F searching */ - gtk_tree_view_set_search_column(GTK_TREE_VIEW(gtkblist->treeview), NAME_COLUMN); - gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(gtkblist->treeview), - pidgin_blist_search_equal_func, NULL, NULL); - - sw = gtk_scrolled_window_new(); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), gtkblist->treeview); - - gtk_widget_set_vexpand(sw, TRUE); - gtk_box_append(GTK_BOX(gtkblist->vbox), sw); - - sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); - gtk_box_append(GTK_BOX(gtkblist->vbox), sep); - - /* Update some dynamic things */ - pidgin_blist_update_sort_methods(); - - /* OK... let's show this bad boy. */ - pidgin_blist_refresh(list); - purple_blist_set_visible(TRUE); - - handle = pidgin_blist_get_handle(); - - /* sorting */ - purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/sort_type", - _prefs_change_sort_method, NULL); - - /* Setup some purple signal handlers. */ - - handle = purple_conversations_get_handle(); - purple_signal_connect(handle, "conversation-updated", gtkblist, - G_CALLBACK(conversation_updated_cb), - gtkblist); - purple_signal_connect(handle, "deleting-conversation", gtkblist, - G_CALLBACK(conversation_deleting_cb), - gtkblist); - purple_signal_connect(handle, "conversation-created", gtkblist, - G_CALLBACK(conversation_created_cb), - gtkblist); - purple_signal_connect(handle, "chat-joined", gtkblist, - G_CALLBACK(conversation_created_cb), - gtkblist); - - /* emit our created signal */ - handle = pidgin_blist_get_handle(); - purple_signal_emit(handle, "gtkblist-created", list); - - gtkblist->action_group = G_ACTION_GROUP(g_simple_action_group_new()); - g_action_map_add_action_entries(G_ACTION_MAP(gtkblist->action_group), - menu_actions, G_N_ELEMENTS(menu_actions), - gtkblist); - gtk_widget_insert_action_group(gtkblist->treeview, "menu", - G_ACTION_GROUP(gtkblist->action_group)); - - pidgin_blist_populate_menus(); -} - -static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender) -{ - PurpleBlistNode *node; - - gtkblist = PIDGIN_BUDDY_LIST(list); - if(!gtkblist || !gtkblist->treeview) - return; - - node = purple_blist_get_root(list); - - while (node) - { - /* This is only needed when we're reverting to a non-GTK sorted - * status. We shouldn't need to remove otherwise. - */ - if (remove && !PURPLE_IS_GROUP(node)) - pidgin_blist_hide_node(list, node, FALSE); - - if (PURPLE_IS_BUDDY(node)) - pidgin_blist_update_buddy(list, node, rerender); - else if (PURPLE_IS_CHAT(node)) - pidgin_blist_update(list, node); - else if (PURPLE_IS_GROUP(node)) - pidgin_blist_update(list, node); - node = purple_blist_node_next(node, FALSE); - } - -} - -void pidgin_blist_refresh(PurpleBuddyList *list) -{ - redo_buddy_list(list, FALSE, TRUE); -} - -static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter) { - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - GtkTreePath *path; - - if (!gtknode) { - return FALSE; - } - - if (!gtkblist) { - purple_debug_error("gtkblist", "get_iter_from_node was called, but we don't seem to have a blist\n"); - return FALSE; - } - - if (!gtknode->row) - return FALSE; - - if (!GTK_IS_TREE_MODEL(gtkblist->treemodel)) { - return FALSE; - } - - if ((path = gtk_tree_row_reference_get_path(gtknode->row)) == NULL) - return FALSE; - - if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), iter, path)) { - gtk_tree_path_free(path); - return FALSE; - } - gtk_tree_path_free(path); - return TRUE; -} - -static void -pidgin_blist_remove(PurpleBuddyList *list, PurpleBlistNode *node) { - purple_request_close_with_handle(node); - - pidgin_blist_hide_node(list, node, TRUE); - - if(node->parent) { - pidgin_blist_update(list, node->parent); - } -} - -static gboolean do_selection_changed(PurpleBlistNode *new_selection) -{ - PurpleBlistNode *old_selection = NULL; - - /* test for gtkblist because crazy timeout means we can be called after the blist is gone */ - if (gtkblist && new_selection != gtkblist->selected_node) { - old_selection = gtkblist->selected_node; - gtkblist->selected_node = new_selection; - if(new_selection) - pidgin_blist_update(NULL, new_selection); - if(old_selection) - pidgin_blist_update(NULL, old_selection); - } - - return FALSE; -} - -static void -pidgin_blist_selection_changed(GtkTreeSelection *selection, - G_GNUC_UNUSED gpointer data) -{ - PurpleBlistNode *new_selection = NULL; - GtkTreeIter iter; - - if(gtk_tree_selection_get_selected(selection, NULL, &iter)){ - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, - NODE_COLUMN, &new_selection, -1); - } - - /* we set this up as a timeout, otherwise the blist flickers ... - * but we don't do it for groups, because it causes total bizarness - - * the previously selected buddy node might rendered at half height. - */ - if ((new_selection != NULL) && PURPLE_IS_GROUP(new_selection)) { - do_selection_changed(new_selection); - } else { - g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); - } -} - -static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTreeIter *iter) -{ - GtkTreeIter parent_iter = {0, NULL, NULL, NULL}, cur, *curptr = NULL; - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - GtkTreePath *newpath; - - if(!iter) - return FALSE; - - /* XXX: it's not necessary, but let's silence a warning*/ - memset(&parent_iter, 0, sizeof(parent_iter)); - - if(node->parent && !get_iter_from_node(node->parent, &parent_iter)) - return FALSE; - - if(get_iter_from_node(node, &cur)) - curptr = &cur; - - if(PURPLE_IS_META_CONTACT(node) || PURPLE_IS_CHAT(node)) { - current_sort_method->func(node, list, parent_iter, curptr, iter); - } else { - sort_method_none(node, list, parent_iter, curptr, iter); - } - - if(gtknode != NULL) { - gtk_tree_row_reference_free(gtknode->row); - } else { - pidgin_blist_new_node(list, node); - gtknode = g_object_get_data(G_OBJECT(node), UI_DATA); - } - - newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), - iter); - gtknode->row = - gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), - newpath); - - gtk_tree_path_free(newpath); - - if (!editing_blist) - gtk_tree_store_set(gtkblist->treemodel, iter, - NODE_COLUMN, node, - -1); - - if(node->parent) { - GtkTreePath *expand = NULL; - PidginBlistNode *gtkparentnode = g_object_get_data(G_OBJECT(node->parent), UI_DATA); - - if(PURPLE_IS_GROUP(node->parent)) { - if(!purple_blist_node_get_bool(node->parent, "collapsed")) - expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); - } else if(PURPLE_IS_META_CONTACT(node->parent) && - gtkparentnode->contact_expanded) { - expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); - } - if(expand) { - gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, FALSE); - gtk_tree_path_free(expand); - } - } - - return TRUE; -} - -/* This version of pidgin_blist_update_group can take the original buddy or a - * group, but has much better algorithmic performance with a pre-known buddy. - */ -static void pidgin_blist_update_group(PurpleBuddyList *list, - PurpleBlistNode *node) -{ - PurpleBlistNode* gnode; - gboolean show = FALSE; - - g_return_if_fail(node != NULL); - - if (editing_blist) - return; - - if (PURPLE_IS_GROUP(node)) - gnode = node; - else if (PURPLE_IS_BUDDY(node)) - gnode = node->parent->parent; - else if (PURPLE_IS_META_CONTACT(node) || PURPLE_IS_CHAT(node)) - gnode = node->parent; - else - return; - - show = TRUE; - - if (show) { - gchar *title; - GtkTreeIter iter; - GtkTreePath *path; - gboolean expanded; - GdkPixbuf *avatar = NULL; - - if(!insert_node(list, gnode, &iter)) - return; - - path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(gtkblist->treeview), path); - gtk_tree_path_free(path); - - title = pidgin_get_group_title(gnode, expanded); - avatar = pidgin_blist_get_buddy_icon(gnode, TRUE, TRUE); - - gtk_tree_store_set(gtkblist->treemodel, &iter, - STATUS_ICON_VISIBLE_COLUMN, FALSE, - STATUS_ICON_COLUMN, NULL, - NAME_COLUMN, title, - NODE_COLUMN, gnode, - BUDDY_ICON_COLUMN, avatar, - EMBLEM_VISIBLE_COLUMN, FALSE, - -1); - g_free(title); - } else { - pidgin_blist_hide_node(list, gnode, TRUE); - } -} - -static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded) -{ - PurpleGroup *group; - char group_count[12] = ""; - char *mark, *esc; - PurpleBlistNode *selected_node = NULL; - GtkTreeIter iter; - - group = (PurpleGroup*)gnode; - - if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)), NULL, &iter)) { - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, - NODE_COLUMN, &selected_node, -1); - } - - if (!expanded) { - g_snprintf(group_count, sizeof(group_count), "%d/%d", - purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)), - purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group))); - } - - esc = g_markup_escape_text(purple_group_get_name(group), -1); - - mark = g_strdup_printf("<b>%s</b>%s%s%s", - esc ? esc : "", - !expanded ? " <span weight='light'>(</span>" : "", - group_count, - !expanded ? "<span weight='light'>)</span>" : ""); - - g_free(esc); - return mark; -} - -static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node) -{ - PurplePresence *presence = purple_buddy_get_presence(buddy); - PurpleProtocol *protocol = NULL; - GdkPixbuf *avatar, *emblem; - char *mark; - char *idle = NULL; - gboolean selected = (gtkblist->selected_node == node); - const gchar *protocol_icon_name = NULL, *status_icon_name = NULL; - - if(editing_blist) { - return; - } - - status_icon_name = pidgin_blist_get_status_icon_name(PURPLE_BLIST_NODE(buddy)); - avatar = pidgin_blist_get_buddy_icon(PURPLE_BLIST_NODE(buddy), TRUE, TRUE); - - if(avatar != NULL) { - if(!PURPLE_BUDDY_IS_ONLINE(buddy) || purple_presence_is_idle(presence)) { - do_alphashift(avatar, 77); - } - } - - emblem = pidgin_blist_get_emblem(PURPLE_BLIST_NODE(buddy)); - mark = pidgin_blist_get_name_markup(buddy, selected, TRUE); - - protocol = purple_account_get_protocol(purple_buddy_get_account(buddy)); - protocol_icon_name = purple_protocol_get_icon_name(protocol); - - gtk_tree_store_set(gtkblist->treemodel, iter, - STATUS_ICON_COLUMN, status_icon_name, - STATUS_ICON_VISIBLE_COLUMN, TRUE, - NAME_COLUMN, mark, - BUDDY_ICON_COLUMN, avatar, - EMBLEM_COLUMN, emblem, - EMBLEM_VISIBLE_COLUMN, (emblem != NULL), - PROTOCOL_ICON_NAME_COLUMN, protocol_icon_name, - -1); - - g_free(mark); - g_free(idle); - if(emblem) - g_object_unref(emblem); - if(avatar) - g_object_unref(avatar); -} - -/* This is a variation on the original gtk_blist_update_contact. Here we - can know in advance which buddy has changed so we can just update that */ -static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *node) -{ - PurpleBlistNode *cnode; - PurpleMetaContact *contact; - PurpleBuddy *buddy; - PidginBlistNode *gtknode; - - if (editing_blist) - return; - - if (PURPLE_IS_BUDDY(node)) - cnode = node->parent; - else - cnode = node; - - g_return_if_fail(PURPLE_IS_META_CONTACT(cnode)); - - /* First things first, update the group */ - if (PURPLE_IS_BUDDY(node)) - pidgin_blist_update_group(list, node); - else - pidgin_blist_update_group(list, cnode->parent); - - contact = (PurpleMetaContact*)cnode; - buddy = purple_meta_contact_get_priority_buddy(contact); - - if (buddy_is_displayable(buddy)) - { - GtkTreeIter iter; - - if(!insert_node(list, cnode, &iter)) - return; - - gtknode = g_object_get_data(G_OBJECT(cnode), UI_DATA); - - if(gtknode->contact_expanded) { - gchar *mark; - const gchar *icon_name = NULL; - - mark = g_markup_escape_text(purple_meta_contact_get_alias(contact), -1); - - icon_name = pidgin_blist_get_status_icon_name(cnode); - - gtk_tree_store_set(gtkblist->treemodel, &iter, - STATUS_ICON_COLUMN, icon_name, - STATUS_ICON_VISIBLE_COLUMN, TRUE, - NAME_COLUMN, mark, - BUDDY_ICON_COLUMN, NULL, - -1); - g_free(mark); - } else { - buddy_node(buddy, &iter, cnode); - } - } else { - pidgin_blist_hide_node(list, cnode, TRUE); - } -} - - - -static void -pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, - G_GNUC_UNUSED gboolean status_change) -{ - PurpleBuddy *buddy; - PidginBlistNode *gtkparentnode; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - if (node->parent == NULL) - return; - - buddy = (PurpleBuddy*)node; - - /* First things first, update the contact */ - pidgin_blist_update_contact(list, node); - - gtkparentnode = g_object_get_data(G_OBJECT(node->parent), UI_DATA); - - if (gtkparentnode->contact_expanded && buddy_is_displayable(buddy)) - { - GtkTreeIter iter; - - if (!insert_node(list, node, &iter)) - return; - - buddy_node(buddy, &iter, node); - - } else { - pidgin_blist_hide_node(list, node, TRUE); - } - -} - -static void pidgin_blist_update_chat(PurpleBuddyList *list, PurpleBlistNode *node) -{ - PurpleChat *chat; - - g_return_if_fail(PURPLE_IS_CHAT(node)); - - if (editing_blist) - return; - - /* First things first, update the group */ - pidgin_blist_update_group(list, node->parent); - - chat = (PurpleChat*)node; - - if(purple_account_is_connected(purple_chat_get_account(chat))) { - PurpleProtocol *protocol = NULL; - GtkTreeIter iter; - GdkPixbuf *avatar, *emblem; - const gchar *color = NULL; - gchar *mark, *tmp; - gboolean selected = (gtkblist->selected_node == node); - gboolean nick_said = FALSE; - const gchar *protocol_icon_name = NULL, *status_icon_name = NULL; - - if (!insert_node(list, node, &iter)) - return; - - status_icon_name = pidgin_blist_get_status_icon_name(node); - emblem = pidgin_blist_get_emblem(node); - avatar = pidgin_blist_get_buddy_icon(node, TRUE, FALSE); - - mark = g_markup_escape_text(purple_chat_get_name(chat), -1); - - if(selected ) { - /* nick_said color is the same as gtkconv:tab-label-attention */ - color = (nick_said ? "#006aff" : NULL); - } - - if(color) { - tmp = g_strdup_printf("<span color='%s'>%s</span>", color, mark); - g_free(mark); - mark = tmp; - } - - protocol = purple_account_get_protocol(purple_chat_get_account(chat)); - protocol_icon_name = purple_protocol_get_icon_name(protocol); - - gtk_tree_store_set(gtkblist->treemodel, &iter, - STATUS_ICON_COLUMN, status_icon_name, - STATUS_ICON_VISIBLE_COLUMN, TRUE, - BUDDY_ICON_COLUMN, avatar, - EMBLEM_COLUMN, emblem, - EMBLEM_VISIBLE_COLUMN, emblem != NULL, - PROTOCOL_ICON_NAME_COLUMN, protocol_icon_name, - NAME_COLUMN, mark, - -1); - - g_free(mark); - if(emblem) - g_object_unref(emblem); - if(avatar) - g_object_unref(avatar); - } else { - pidgin_blist_hide_node(list, node, TRUE); - } -} - -static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node) -{ - if(list) { - gtkblist = PIDGIN_BUDDY_LIST(list); - } - if(!gtkblist || !gtkblist->treeview || !node) { - return; - } - - if(g_object_get_data(G_OBJECT(node), UI_DATA) == NULL) { - pidgin_blist_new_node(list, node); - } - - if(PURPLE_IS_GROUP(node)) { - pidgin_blist_update_group(list, node); - } else if(PURPLE_IS_META_CONTACT(node)) { - pidgin_blist_update_contact(list, node); - } else if(PURPLE_IS_BUDDY(node)) { - pidgin_blist_update_buddy(list, node, TRUE); - } else if(PURPLE_IS_CHAT(node)) { - pidgin_blist_update_chat(list, node); - } -} - -static void -pidgin_blist_set_visible(G_GNUC_UNUSED PurpleBuddyList *list, gboolean show) -{ - if (!(gtkblist && gtkblist->window)) - return; - - if (show) { - gtk_widget_show(gtkblist->window); - } else { - if (!gtk_widget_get_visible(gtkblist->window)) - gtk_widget_show(gtkblist->window); - /* gtk_window_iconify(GTK_WINDOW(gtkblist->window)); */ - } -} - -static void pidgin_blist_request_add_buddy(G_GNUC_UNUSED PurpleBuddyList *list, PurpleAccount *account, const char *username, const char *group, const char *alias) @@ -3745,139 +491,6 @@ NULL, NULL); } -PidginBuddyList * -pidgin_blist_get_default_gtk_blist(void) -{ - return gtkblist; -} - -static gboolean -autojoin_cb(PurpleConnection *gc, G_GNUC_UNUSED gpointer data) -{ - PurpleAccount *account = purple_connection_get_account(gc); - PurpleBlistNode *gnode, *cnode; - for (gnode = purple_blist_get_default_root(); gnode; - gnode = gnode->next) { - if(!PURPLE_IS_GROUP(gnode)) - continue; - for(cnode = gnode->child; cnode; cnode = cnode->next) - { - PurpleChat *chat; - - if(!PURPLE_IS_CHAT(cnode)) - continue; - - chat = (PurpleChat *)cnode; - - if(purple_chat_get_account(chat) != account) - continue; - - if (purple_blist_node_get_bool(PURPLE_BLIST_NODE(chat), "gtk-autojoin")) - purple_serv_join_chat(gc, purple_chat_get_components(chat)); - } - } - - /* Stop processing; we handled the autojoins. */ - return TRUE; -} - -void * -pidgin_blist_get_handle(void) { - static int handle; - - return &handle; -} - -static gboolean buddy_signonoff_timeout_cb(PurpleBuddy *buddy) -{ - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(buddy), UI_DATA); - PurpleAccount *account = NULL; - - gtknode->recent_signonoff = FALSE; - gtknode->recent_signonoff_timer = 0; - - account = purple_buddy_get_account(buddy); - if(purple_account_is_connected(account)) { - pidgin_blist_update(NULL, PURPLE_BLIST_NODE(buddy)); - } - - return FALSE; -} - -static void -buddy_signonoff_cb(PurpleBuddy *buddy) { - PidginBlistNode *gtknode = g_object_get_data(G_OBJECT(buddy), UI_DATA); - - if(gtknode == NULL) { - pidgin_blist_new_node(purple_blist_get_default(), - PURPLE_BLIST_NODE(buddy)); - - gtknode = g_object_get_data(G_OBJECT(buddy), UI_DATA); - } - - gtknode->recent_signonoff = TRUE; - - if(gtknode->recent_signonoff_timer > 0) - g_source_remove(gtknode->recent_signonoff_timer); - - g_object_ref(buddy); - gtknode->recent_signonoff_timer = g_timeout_add_seconds_full( - G_PRIORITY_DEFAULT, 10, - (GSourceFunc)buddy_signonoff_timeout_cb, - buddy, g_object_unref); -} - -void pidgin_blist_init(void) -{ - void *gtk_blist_handle = pidgin_blist_get_handle(); - - cached_emblems = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - - /* Remove old prefs */ - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_empty_groups"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_idle_time"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"); - - /* Initialize prefs */ - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/blist"); - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/sort_type", "alphabetical"); - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/theme", ""); - - /* Register our signals */ - purple_signal_register(gtk_blist_handle, "gtkblist-created", - purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, - PURPLE_TYPE_BUDDY_LIST); - - purple_signal_register(gtk_blist_handle, "drawing-tooltip", - purple_marshal_VOID__POINTER_POINTER_UINT, G_TYPE_NONE, - 3, PURPLE_TYPE_BLIST_NODE, - G_TYPE_POINTER, /* pointer to a (GString *) */ - G_TYPE_BOOLEAN); - - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", - gtk_blist_handle, G_CALLBACK(buddy_signonoff_cb), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", - gtk_blist_handle, G_CALLBACK(buddy_signonoff_cb), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-privacy-changed", - gtk_blist_handle, G_CALLBACK(pidgin_blist_update_privacy_cb), NULL); - - purple_signal_connect_priority(purple_connections_get_handle(), "autojoin", - gtk_blist_handle, G_CALLBACK(autojoin_cb), - NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST); -} - -void -pidgin_blist_uninit(void) { - g_hash_table_destroy(cached_emblems); - - purple_signals_unregister_by_instance(pidgin_blist_get_handle()); - purple_signals_disconnect_by_handle(pidgin_blist_get_handle()); - - gtkblist = NULL; -} - /************************************************************************** * GTK Buddy list GObject code **************************************************************************/ @@ -3887,310 +500,10 @@ } static void -pidgin_buddy_list_finalize(GObject *obj) -{ - PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(obj); - - purple_signals_disconnect_by_handle(gtkblist); - - gtk_window_destroy(GTK_WINDOW(gtkblist->window)); - - gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL; - g_clear_object(>kblist->treemodel); - - purple_prefs_disconnect_by_handle(pidgin_blist_get_handle()); +pidgin_buddy_list_class_init(PidginBuddyListClass *klass) { + PurpleBuddyListClass *purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass); - G_OBJECT_CLASS(pidgin_buddy_list_parent_class)->finalize(obj); -} - -static void -pidgin_buddy_list_class_init(PidginBuddyListClass *klass) -{ - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - PurpleBuddyListClass *purple_blist_class; - - obj_class->finalize = pidgin_buddy_list_finalize; - - purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass); - purple_blist_class->new_node = pidgin_blist_new_node; - purple_blist_class->show = pidgin_blist_show; - purple_blist_class->update = pidgin_blist_update; - purple_blist_class->remove = pidgin_blist_remove; - purple_blist_class->set_visible = pidgin_blist_set_visible; purple_blist_class->request_add_buddy = pidgin_blist_request_add_buddy; purple_blist_class->request_add_chat = pidgin_blist_request_add_chat; purple_blist_class->request_add_group = pidgin_blist_request_add_group; } - -/********************************************************************* - * Buddy List sorting functions * - *********************************************************************/ - -GList * -pidgin_blist_get_sort_methods(void) -{ - return pidgin_blist_sort_methods; -} - -void pidgin_blist_sort_method_reg(const char *id, const char *name, pidgin_blist_sort_function func) -{ - struct _PidginBlistSortMethod *method; - - g_return_if_fail(id != NULL); - g_return_if_fail(name != NULL); - g_return_if_fail(func != NULL); - - method = g_new0(struct _PidginBlistSortMethod, 1); - method->id = g_strdup(id); - method->name = g_strdup(name); - method->func = func; - pidgin_blist_sort_methods = g_list_append(pidgin_blist_sort_methods, method); - pidgin_blist_update_sort_methods(); -} - -void pidgin_blist_sort_method_unreg(const char *id) -{ - GList *l = pidgin_blist_sort_methods; - - g_return_if_fail(id != NULL); - - while(l) { - struct _PidginBlistSortMethod *method = l->data; - if(purple_strequal(method->id, id)) { - pidgin_blist_sort_methods = g_list_delete_link(pidgin_blist_sort_methods, l); - g_free(method->id); - g_free(method->name); - g_free(method); - break; - } - l = l->next; - } - pidgin_blist_update_sort_methods(); -} - -void pidgin_blist_sort_method_set(const char *id){ - GList *l = pidgin_blist_sort_methods; - - if(!id) - id = "none"; - - while (l && !purple_strequal(((struct _PidginBlistSortMethod*)l->data)->id, id)) - l = l->next; - - if (l) { - current_sort_method = l->data; - } else if (!current_sort_method) { - pidgin_blist_sort_method_set("none"); - return; - } - if (purple_strequal(id, "none")) { - redo_buddy_list(purple_blist_get_default(), TRUE, FALSE); - } else { - redo_buddy_list(purple_blist_get_default(), FALSE, FALSE); - } -} - -/****************************************** - ** Sort Methods - ******************************************/ - -static void -sort_method_none(PurpleBlistNode *node, G_GNUC_UNUSED PurpleBuddyList *blist, - GtkTreeIter parent_iter, GtkTreeIter *cur, GtkTreeIter *iter) -{ - PurpleBlistNode *sibling = node->prev; - GtkTreeIter sibling_iter; - - if (cur != NULL) { - *iter = *cur; - return; - } - - while (sibling && !get_iter_from_node(sibling, &sibling_iter)) { - sibling = sibling->prev; - } - - gtk_tree_store_insert_after(gtkblist->treemodel, iter, - node->parent ? &parent_iter : NULL, - sibling ? &sibling_iter : NULL); -} - -static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter) -{ - GtkTreeIter more_z; - - const char *my_name; - - if(PURPLE_IS_META_CONTACT(node)) { - my_name = purple_meta_contact_get_alias((PurpleMetaContact*)node); - } else if(PURPLE_IS_CHAT(node)) { - my_name = purple_chat_get_name((PurpleChat*)node); - } else { - sort_method_none(node, blist, groupiter, cur, iter); - return; - } - - if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { - gtk_tree_store_insert(gtkblist->treemodel, iter, &groupiter, 0); - return; - } - - do { - PurpleBlistNode *n; - const char *this_name; - int cmp; - - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &n, -1); - - if(PURPLE_IS_META_CONTACT(n)) { - this_name = purple_meta_contact_get_alias((PurpleMetaContact*)n); - } else if(PURPLE_IS_CHAT(n)) { - this_name = purple_chat_get_name((PurpleChat*)n); - } else { - this_name = NULL; - } - - cmp = purple_utf8_strcasecmp(my_name, this_name); - - if(this_name && (cmp < 0 || (cmp == 0 && node < n))) { - if(cur) { - gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); - *iter = *cur; - return; - } else { - gtk_tree_store_insert_before(gtkblist->treemodel, iter, - &groupiter, &more_z); - return; - } - } - } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); - - if(cur) { - gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); - *iter = *cur; - return; - } else { - gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); - return; - } -} - -static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter) -{ - GtkTreeIter more_z; - - PurpleBuddy *my_buddy, *this_buddy; - - if(PURPLE_IS_META_CONTACT(node)) { - my_buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)node); - } else if(PURPLE_IS_CHAT(node)) { - if (cur != NULL) { - *iter = *cur; - return; - } - - gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); - return; - } else { - sort_method_alphabetical(node, blist, groupiter, cur, iter); - return; - } - - - if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { - gtk_tree_store_insert(gtkblist->treemodel, iter, &groupiter, 0); - return; - } - - do { - PurpleBlistNode *n; - gint name_cmp; - gint presence_cmp; - - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &n, -1); - - if(PURPLE_IS_META_CONTACT(n)) { - this_buddy = purple_meta_contact_get_priority_buddy((PurpleMetaContact*)n); - } else { - this_buddy = NULL; - } - - name_cmp = purple_utf8_strcasecmp( - purple_meta_contact_get_alias(purple_buddy_get_contact(my_buddy)), - (this_buddy - ? purple_meta_contact_get_alias(purple_buddy_get_contact(this_buddy)) - : NULL)); - - presence_cmp = purple_buddy_presence_compare( - PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(my_buddy)), - this_buddy ? PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(this_buddy)) : NULL); - - if (this_buddy == NULL || - (presence_cmp < 0 || - (presence_cmp == 0 && - (name_cmp < 0 || (name_cmp == 0 && node < n))))) - { - if (cur != NULL) - { - gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); - *iter = *cur; - return; - } - else - { - gtk_tree_store_insert_before(gtkblist->treemodel, iter, - &groupiter, &more_z); - return; - } - } - } - while (gtk_tree_model_iter_next(GTK_TREE_MODEL(gtkblist->treemodel), - &more_z)); - - if (cur) { - gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); - *iter = *cur; - return; - } else { - gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); - return; - } -} - -void -pidgin_blist_update_sort_methods(void) -{ - GApplication *application = NULL; - GMenu *menu = NULL; - GList *l; - - if(gtkblist == NULL || !PIDGIN_IS_CONTACT_LIST_WINDOW(gtkblist->window)) { - return; - } - - /* get the menu and clear any existing entries. */ - application = g_application_get_default(); - menu = gtk_application_get_menu_by_id(GTK_APPLICATION(application), - "sort-buddies"); - g_menu_remove_all(menu); - - /* Walk through the sort methods and add them to the menu. */ - for (l = pidgin_blist_sort_methods; l; l = l->next) { - PidginBlistSortMethod *method = NULL; - GMenuItem *item = NULL; - GVariant *value = NULL; - gchar *action = NULL; - - method = (PidginBlistSortMethod *)l->data; - value = g_variant_new_string(method->id); - - action = g_action_print_detailed_name("blist.sort-method", value); - item = g_menu_item_new(method->name, action); - - g_free(action); - g_variant_unref(value); - - g_menu_append_item(menu, item); - g_object_unref(item); - } -}
--- a/pidgin/gtkblist.h Fri Feb 17 19:34:51 2023 -0600 +++ b/pidgin/gtkblist.h Fri Feb 17 19:37:13 2023 -0600 @@ -26,8 +26,6 @@ #ifndef _PIDGINBLIST_H_ #define _PIDGINBLIST_H_ -#include <gtk/gtk.h> - #include <purple.h> #define PIDGIN_TYPE_BUDDY_LIST (pidgin_buddy_list_get_type()) @@ -39,33 +37,11 @@ **************************************************************************/ /** * PidginBuddyList: - * @vbox: This is the vbox that everything important gets packed - * into. Your plugin might want to pack something in it - * itself. Go, plugins! - * @treeview: It's a treeview... d'uh. - * @treemodel: This is the treemodel. - * @text_column: Column - * @refresh_timer: The timer for refreshing every 30 seconds - * @selected_node: The currently selected node - * @scrollbook: Scrollbook for alerts * - * Like, everything you need to know about the gtk buddy list + * The remnants of the buddy list, soon to be lost to the wind. */ struct _PidginBuddyList { PurpleBuddyList parent; - - GtkWidget *window; - GtkWidget *vbox; - - GtkWidget *treeview; - GtkTreeStore *treemodel; - GtkTreeViewColumn *text_column; - - GtkCellRenderer *text_rend; - - GActionGroup *action_group; - - PurpleBlistNode *selected_node; }; G_BEGIN_DECLS @@ -78,141 +54,6 @@ PurpleBuddyList) /** - * pidgin_blist_get_handle: - * - * Get the handle for the GTK blist system. - * - * Returns: the handle to the blist system - */ -void *pidgin_blist_get_handle(void); - -/** - * pidgin_blist_init: - * - * Initializes the GTK blist system. - */ -void pidgin_blist_init(void); - -/** - * pidgin_blist_uninit: - * - * Uninitializes the GTK blist system. - */ -void pidgin_blist_uninit(void); - -/** - * pidgin_blist_get_default_gtk_blist: - * - * Returns the default gtk buddy list - * - * There's normally only one buddy list window, but that isn't a necessity. This function - * returns the PidginBuddyList we're most likely wanting to work with. This is slightly - * cleaner than an externed global. - * - * Returns: (transfer none): The default GTK buddy list. - */ -PidginBuddyList *pidgin_blist_get_default_gtk_blist(void); - -/** - * pidgin_blist_refresh: - * @list: This is the core list that gets updated from - * - * Refreshes all the nodes of the buddy list. - * This should only be called when something changes to affect most of the nodes (such as a ui preference changing) - */ -void pidgin_blist_refresh(PurpleBuddyList *list); - -/** - * pidgin_blist_get_emblem: - * @node: The node to return an emblem for - * - * Returns the blist emblem. - * - * This may be an existing pixbuf that has been given an additional ref, - * so it shouldn't be modified. - * - * Returns: (transfer full): A GdkPixbuf for the emblem to show, or NULL - */ -GdkPixbuf * -pidgin_blist_get_emblem(PurpleBlistNode *node); - -/** - * pidgin_blist_node_is_contact_expanded: - * @node: The node in question. - * - * Returns a boolean indicating if @node is part of an expanded contact. - * - * This only makes sense for contact and buddy nodes. %FALSE is returned - * for other types of nodes. - * - * Returns: A boolean indicating if @node is part of an expanded contact. - */ -gboolean pidgin_blist_node_is_contact_expanded(PurpleBlistNode *node); - -/************************************************************************** - * GTK Buddy List sorting functions - **************************************************************************/ - -typedef void (*pidgin_blist_sort_function)(PurpleBlistNode *new, PurpleBuddyList *blist, GtkTreeIter group, GtkTreeIter *cur, GtkTreeIter *iter); - -/** - * pidgin_blist_get_sort_methods: - * - * Gets the current list of sort methods. - * - * Returns: (transfer none) (element-type PidginBlistSortMethod): A GSlist of sort methods - */ -GList *pidgin_blist_get_sort_methods(void); - -struct _PidginBlistSortMethod { - char *id; - char *name; - pidgin_blist_sort_function func; -}; - -typedef struct _PidginBlistSortMethod PidginBlistSortMethod; - -/** - * pidgin_blist_sort_method_reg: - * @id: The unique ID of the sorting method - * @name: The method's name. - * @func: (scope call): A pointer to the function. - * - * Registers a buddy list sorting method. - */ -void pidgin_blist_sort_method_reg(const char *id, const char *name, pidgin_blist_sort_function func); - -/** - * pidgin_blist_sort_method_unreg: - * @id: The method's id - * - * Unregisters a buddy list sorting method. - */ -void pidgin_blist_sort_method_unreg(const char *id); - -/** - * pidgin_blist_sort_method_set: - * @id: The method's id. - * - * Sets a buddy list sorting method. - */ -void pidgin_blist_sort_method_set(const char *id); - -/** - * pidgin_blist_setup_sort_methods: - * - * Sets up the programs default sort methods - */ -void pidgin_blist_setup_sort_methods(void); - -/** - * pidgin_blist_update_sort_methods: - * - * Updates the Sorting menu on the GTK buddy list window. - */ -void pidgin_blist_update_sort_methods(void); - -/** * pidgin_blist_joinchat_is_showable: * * Determines if showing the join chat dialog is a valid action. @@ -229,31 +70,6 @@ */ void pidgin_blist_joinchat_show(void); -/** - * pidgin_blist_get_name_markup: - * @buddy: The buddy to return markup from - * @selected: Whether this buddy is selected. If TRUE, the markup will not change the color. - * @aliased: TRUE to return the appropriate alias of this buddy, FALSE to return its username and status information - * - * Returns a buddy's Pango markup appropriate for setting in a GtkCellRenderer. - * - * Returns: The markup for this buddy - */ -gchar *pidgin_blist_get_name_markup(PurpleBuddy *buddy, gboolean selected, gboolean aliased); - -/** - * pidgin_blist_query_tooltip_for_node: - * @node: The buddy list to query a tooltip for. - * @tooltip: The tooltip object to add a tooltip to. - * - * Queries and creates a custom tooltip for a buddy list node. - * - * Returns: %TRUE if a custom tooltip was added, %FALSE otherwise. - * - * Since: 3.0.0 - */ -gboolean pidgin_blist_query_tooltip_for_node(PurpleBlistNode *node, GtkTooltip *tooltip); - G_END_DECLS #endif /* _PIDGINBLIST_H_ */
--- a/pidgin/gtkconv.c Fri Feb 17 19:34:51 2023 -0600 +++ b/pidgin/gtkconv.c Fri Feb 17 19:37:13 2023 -0600 @@ -1379,7 +1379,8 @@ static gboolean pidgin_conv_userlist_query_tooltip(GtkWidget *widget, int x, int y, - gboolean keyboard_mode, GtkTooltip *tooltip, + gboolean keyboard_mode, + G_GNUC_UNUSED GtkTooltip *tooltip, gpointer userdata) { PidginConversation *gtkconv = userdata; @@ -1389,9 +1390,6 @@ GtkTreePath *path = NULL; GtkTreeModel *model = NULL; GtkTreeIter iter; - PurpleBlistNode *node = NULL; - PurpleProtocol *protocol = NULL; - char *who = NULL; conv = gtkconv->active_conv; account = purple_conversation_get_account(conv); @@ -1425,20 +1423,6 @@ } } - gtk_tree_model_get(model, &iter, CHAT_USERS_NAME_COLUMN, &who, -1); - - protocol = purple_connection_get_protocol(connection); - node = (PurpleBlistNode*)purple_blist_find_buddy(account, who); - g_free(who); - - if (node && protocol && (purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) { - if (pidgin_blist_query_tooltip_for_node(node, tooltip)) { - gtk_tree_view_set_tooltip_row(GTK_TREE_VIEW(widget), tooltip, path); - gtk_tree_path_free(path); - return TRUE; - } - } - gtk_tree_path_free(path); return FALSE; } @@ -2084,8 +2068,6 @@ { char *title; PurpleAccount *account = purple_conversation_get_account(conv); - PurpleBuddy *buddy = NULL; - char *markup = NULL; if ((account == NULL) || !purple_account_is_connected(account) || @@ -2095,43 +2077,6 @@ else title = g_strdup(purple_conversation_get_title(conv)); - if (PURPLE_IS_IM_CONVERSATION(conv)) { - buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv)); - if (buddy) { - markup = pidgin_blist_get_name_markup(buddy, FALSE, FALSE); - } else { - markup = title; - } - } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) { - const char *topic = gtkconv->topic_text - ? gtk_editable_get_text(GTK_EDITABLE(gtkconv->topic_text)) - : NULL; - const char *title = purple_conversation_get_title(conv); - const char *name = purple_conversation_get_name(conv); - - char *topic_esc, *unaliased, *unaliased_esc, *title_esc; - - topic_esc = topic ? g_markup_escape_text(topic, -1) : NULL; - unaliased = g_utf8_collate(title, name) ? g_strdup_printf("(%s)", name) : NULL; - unaliased_esc = unaliased ? g_markup_escape_text(unaliased, -1) : NULL; - title_esc = g_markup_escape_text(title, -1); - - markup = g_strdup_printf("%s%s<span size='smaller'>%s</span>%s<span size='smaller'>%s</span>", - title_esc, - unaliased_esc ? " " : "", - unaliased_esc ? unaliased_esc : "", - topic_esc && *topic_esc ? "\n" : "", - topic_esc ? topic_esc : ""); - - g_free(title_esc); - g_free(topic_esc); - g_free(unaliased); - g_free(unaliased_esc); - } - - if (title != markup) - g_free(markup); - if (pidgin_display_window_conversation_is_selected(displaywin, conv)) { const char* current_title = gtk_window_get_title(GTK_WINDOW(win)); if (current_title == NULL || !purple_strequal(current_title, title)) {
--- a/pidgin/pidginapplication.c Fri Feb 17 19:34:51 2023 -0600 +++ b/pidgin/pidginapplication.c Fri Feb 17 19:37:13 2023 -0600 @@ -839,9 +839,6 @@ /* load plugins we had when we quit */ purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded"); - /* gk 20201008: this needs to be moved to the buddy list initialization. */ - pidgin_blist_setup_sort_methods(); - gtk_window_set_default_icon_name("pidgin"); g_free(opt_config_dir_arg);
--- a/pidgin/pidginui.c Fri Feb 17 19:34:51 2023 -0600 +++ b/pidgin/pidginui.c Fri Feb 17 19:37:13 2023 -0600 @@ -187,7 +187,6 @@ pidgin_accounts_init(); pidgin_request_init(); - pidgin_blist_init(); pidgin_conversations_init(); pidgin_commands_init(); pidgin_xfers_init(); @@ -206,7 +205,6 @@ pidgin_notify_uninit(); pidgin_commands_uninit(); pidgin_conversations_uninit(); - pidgin_blist_uninit(); pidgin_request_uninit(); pidgin_accounts_uninit(); pidgin_xfers_uninit();
--- a/pidgin/plugins/gtkbuddynote/gtkbuddynote.c Fri Feb 17 19:34:51 2023 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/* - * GtkBuddyNote - Store notes on particular buddies - * Copyright (C) 2007 Etan Reisner <deryni@pidgin.im> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA. - */ - -#include <glib/gi18n.h> - -#include <purple.h> - -#include <pidgin.h> - -static void -append_to_tooltip(PurpleBlistNode *node, GString *text, gboolean full) -{ - if (full) { - const gchar *note = purple_blist_node_get_string(node, "notes"); - - if ((note != NULL) && (*note != '\0')) { - char *tmp, *esc; - purple_markup_html_to_xhtml(note, NULL, &tmp); - esc = g_markup_escape_text(tmp, -1); - g_free(tmp); - g_string_append_printf(text, _("\n<b>Buddy Note</b>: %s"), - esc); - g_free(esc); - } - } -} - -static GPluginPluginInfo * -gtk_buddy_note_query(GError **error) -{ - const gchar * const authors[] = { - "Etan Reisner <deryni@pidgin.im>", - NULL - }; - - const gchar * const dependencies[] = { - "core-plugin_pack-buddynote", - NULL - }; - - return pidgin_plugin_info_new( - "id", "gtkbuddynote", - "name", N_("Buddy Note Tooltips"), - "version", DISPLAY_VERSION, - "category", N_("User interface"), - "summary", N_("Shows stored buddy notes on the buddy's tooltip."), - "description", N_("Shows stored buddy notes on the buddy's tooltip."), - "authors", authors, - "website", PURPLE_WEBSITE, - "abi-version", PURPLE_ABI_VERSION, - "dependencies", dependencies, - NULL - ); -} - -static gboolean -gtk_buddy_note_load(GPluginPlugin *plugin, GError **error) -{ - purple_signal_connect(pidgin_blist_get_handle(), "drawing-tooltip", - plugin, G_CALLBACK(append_to_tooltip), NULL); - return TRUE; -} - -static gboolean -gtk_buddy_note_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) -{ - return TRUE; -} - -GPLUGIN_NATIVE_PLUGIN_DECLARE(gtk_buddy_note)
--- a/pidgin/plugins/gtkbuddynote/meson.build Fri Feb 17 19:34:51 2023 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -gtkbuddynote = library('gtkbuddynote', 'gtkbuddynote.c', - c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="PidginPlugin-BuddyNote"'], - dependencies : [libpurple_dep, libpidgin_dep, glib], - name_prefix : '', - install : true, install_dir : PIDGIN_PLUGINDIR) - -devenv.append('PIDGIN_PLUGIN_PATH', meson.current_build_dir())
--- a/pidgin/plugins/meson.build Fri Feb 17 19:34:51 2023 -0600 +++ b/pidgin/plugins/meson.build Fri Feb 17 19:37:13 2023 -0600 @@ -1,6 +1,5 @@ subdir('disco') subdir('gestures') -subdir('gtkbuddynote') subdir('iconaway') subdir('notify') subdir('spellchk')
--- a/pidgin/plugins/transparency/transparency.c Fri Feb 17 19:34:51 2023 -0600 +++ b/pidgin/plugins/transparency/transparency.c Fri Feb 17 19:37:13 2023 -0600 @@ -61,20 +61,6 @@ * CODE */ -static GtkWidget * -get_buddy_list_window(void) { - PurpleBuddyList *purple_blist = NULL; - - purple_blist = purple_blist_get_default(); - if(PIDGIN_IS_BUDDY_LIST(purple_blist)) { - PidginBuddyList *pidgin_blist = PIDGIN_BUDDY_LIST(purple_blist); - - return pidgin_blist->window; - } - - return NULL; -} - /* Set window transparency level */ static void set_wintrans(GtkWidget *window, int alpha, gboolean enabled) @@ -143,35 +129,6 @@ g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, NULL); } -/* When buddy list window is focused, - * if we're only transparent when unfocused, deal with transparency */ -static void -focus_blist_win_cb(GtkEventControllerFocus *self, gpointer data) -{ - GtkWidget *window = NULL; - gboolean enter = GPOINTER_TO_INT(data); - GSettings *settings = NULL; - - settings = g_settings_new_with_backend(OPT_SCHEMA, - purple_core_get_settings_backend()); - - if(!g_settings_get_boolean(settings, OPT_WINTRANS_BL_ENABLED)) { - g_object_unref(settings); - return; - } - if(!g_settings_get_boolean(settings, OPT_WINTRANS_BL_ONFOCUS)) { - g_object_unref(settings); - return; - } - - window = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(self)); - set_wintrans(window, - g_settings_get_int(settings, OPT_WINTRANS_BL_ALPHA), - !enter); - - g_object_unref(settings); -} - static void change_alpha(GtkWidget *w, gpointer data) { int alpha = gtk_range_get_value(GTK_RANGE(w)); GSettings *settings = NULL; @@ -208,16 +165,6 @@ remove_focus_controller_from_conv_win(GTK_WIDGET(window)); } -static void set_blist_trans(GtkWidget *w, const char *pref) { - GtkWidget *window = get_buddy_list_window(); - gboolean enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); - purple_prefs_set_bool(pref, enabled); - if (window != NULL) { - set_wintrans(window, purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA), - purple_prefs_get_bool(OPT_WINTRANS_BL_ENABLED)); - } -} - static void remove_slider(GtkWidget *slider_frame) { gtk_widget_unparent(slider_frame); @@ -392,37 +339,6 @@ add_focus_controller_to_conv_win(GTK_WIDGET(window)); } -static void -blist_created_cb(PurpleBuddyList *purple_blist, gpointer data) { - GtkWidget *window = get_buddy_list_window(); - GSettings *settings = NULL; - - settings = g_settings_new_with_backend(OPT_SCHEMA, - purple_core_get_settings_backend()); - - if (window != NULL) { - GtkEventController *focus = NULL; - - if (g_settings_get_boolean(settings, OPT_WINTRANS_BL_ENABLED)) { - set_wintrans(window, - g_settings_get_int(settings, OPT_WINTRANS_BL_ALPHA), - TRUE); - } - - focus = gtk_event_controller_focus_new(); - - g_signal_connect(focus, "enter", G_CALLBACK(focus_blist_win_cb), - GINT_TO_POINTER(TRUE)); - g_signal_connect(focus, "leave", G_CALLBACK(focus_blist_win_cb), - GINT_TO_POINTER(FALSE)); - - gtk_widget_add_controller(window, focus); - g_object_set_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY, focus); - } - - g_object_unref(settings); -} - static void alpha_change(GtkWidget *w, gpointer data) { GApplication *application = NULL; GList *wins; @@ -454,13 +370,6 @@ purple_prefs_set_int(pref, alpha); } -static void bl_alpha_change(GtkWidget *w, gpointer data) { - GtkWidget *window = get_buddy_list_window(); - if (window != NULL) { - change_alpha(w, window); - } -} - static void update_existing_convs(void) { GApplication *application = NULL; @@ -485,7 +394,7 @@ static GtkWidget * get_config_frame(PurplePlugin *plugin) { GtkWidget *ret; - GtkWidget *imtransbox, *bltransbox; + GtkWidget *imtransbox; GtkWidget *hbox; GtkWidget *label, *slider; GtkWidget *button; @@ -543,22 +452,6 @@ gtk_box_append(GTK_BOX(trans_box), hbox); - /* Buddy List trans options */ - bltransbox = pidgin_make_frame (ret, _("Buddy List Window")); - button = pidgin_prefs_checkbox(_("_Buddy List window transparency"), - OPT_WINTRANS_BL_ENABLED, bltransbox); - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(set_blist_trans), - (gpointer) OPT_WINTRANS_BL_ENABLED); - - trans_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18); - g_object_bind_property(button, "active", trans_box, "sensitive", - G_BINDING_SYNC_CREATE); - button = pidgin_prefs_checkbox( - _("Remove Buddy List window transparency on focus"), - OPT_WINTRANS_BL_ONFOCUS, trans_box); - gtk_box_append(GTK_BOX(bltransbox), trans_box); - /* IM transparency slider */ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); @@ -569,13 +462,6 @@ gtk_range_set_value(GTK_RANGE(slider), purple_prefs_get_int(OPT_WINTRANS_BL_ALPHA)); - g_signal_connect(G_OBJECT(slider), "value-changed", - G_CALLBACK(bl_alpha_change), NULL); - focus = gtk_event_controller_focus_new(); - g_signal_connect(focus, "leave", G_CALLBACK(alpha_pref_set_int), - (gpointer)OPT_WINTRANS_BL_ALPHA); - gtk_widget_add_controller(slider, focus); - gtk_box_append(GTK_BOX(hbox), slider); gtk_box_append(GTK_BOX(trans_box), hbox); @@ -608,7 +494,6 @@ static gboolean transparency_load(GPluginPlugin *plugin, GError **error) { GApplication *application = NULL; - GtkWidget *window = NULL; application = g_application_get_default(); g_signal_connect(application, "window-added", @@ -618,45 +503,15 @@ update_existing_convs(); - window = get_buddy_list_window(); - if(window != NULL) { - blist_created_cb(NULL, NULL); - } else { - purple_signal_connect(pidgin_blist_get_handle(), - "gtkblist-created", plugin, - G_CALLBACK(blist_created_cb), NULL); - } - return TRUE; } static gboolean transparency_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error) { - GtkWidget *window = NULL; - purple_debug_info(WINTRANS_PLUGIN_ID, "Unloading transparency plugin\n"); remove_convs_wintrans(TRUE); - window = get_buddy_list_window(); - if (window != NULL) { - GSettings *settings = NULL; - GtkEventController *focus = NULL; - - settings = g_settings_new_with_backend(OPT_SCHEMA, - purple_core_get_settings_backend()); - if (g_settings_get_boolean(settings, OPT_WINTRANS_BL_ENABLED)) { - set_wintrans(window, 0, FALSE); - } - g_object_unref(settings); - - /* Remove the focus cbs */ - focus = g_object_get_data(G_OBJECT(window), WINTRANS_CONTROLLER_KEY); - if(GTK_IS_EVENT_CONTROLLER_FOCUS(focus)) { - gtk_widget_remove_controller(window, focus); - } - } - return TRUE; }
--- a/pidgin/win32/gtkwin32dep.c Fri Feb 17 19:34:51 2023 -0600 +++ b/pidgin/win32/gtkwin32dep.c Fri Feb 17 19:37:13 2023 -0600 @@ -73,12 +73,13 @@ static LRESULT CALLBACK message_window_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (msg == PIDGIN_WM_FOCUS_REQUEST) { - PidginBuddyList *blist; - purple_debug_info("winpidgin", "Got external Buddy List focus request."); - blist = pidgin_blist_get_default_gtk_blist(); - if (blist != NULL && blist->window != NULL) { - gtk_window_present(GTK_WINDOW(blist->window)); - } + GtkWidget *widget = NULL; + + purple_debug_info("winpidgin", "Got external focus request."); + + widget = pidgin_display_window_get_default(); + gtk_window_present(GTK_WINDOW(widget)); + return TRUE; } else if (msg == PIDGIN_WM_PROTOCOL_HANDLE) { char *proto_msg = (char *) lparam;