# HG changeset patch # User Sadrul Habib Chowdhury # Date 1189837595 0 # Node ID 7af1af2b7ad30dc8759eaa52cb70468ca303d09c # Parent 4fdf9c1212e20d5f8e21dc45d418162ea9b12b20# Parent 501c05b1e3e5970974bd8e15a5b5f52d9b6e2328 propagate from branch 'im.pidgin.pidgin' (head 02b9fa9cd591c94c082a8ff02ea27fe33b278a0a) to branch 'im.pidgin.sadrul.conv.persistent' (head f0287adbcd60b64f6209cc00141e15d1a67a4c7f) diff -r 4fdf9c1212e2 -r 7af1af2b7ad3 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Sat Sep 15 03:01:22 2007 +0000 +++ b/pidgin/gtkblist.c Sat Sep 15 06:26:35 2007 +0000 @@ -139,12 +139,21 @@ static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded); static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node); -struct _pidgin_blist_node { +typedef enum { + PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE = 1 << 0, /* Whether there's pending message in a conversation */ +} PidginBlistNodeFlags; + +typedef struct _pidgin_blist_node { GtkTreeRowReference *row; gboolean contact_expanded; gboolean recent_signonoff; gint recent_signonoff_timer; -}; + struct { + PurpleConversation *conv; + time_t last_message; /* timestamp for last displayed message */ + PidginBlistNodeFlags flags; + } conv; +} PidginBlistNode; static char dim_grey_string[8] = ""; static char *dim_grey() @@ -312,6 +321,23 @@ gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))); } +static void gtk_blist_menu_persistent_cb(GtkWidget *w, PurpleChat *chat) +{ + purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent", + gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))); +} + +static PurpleConversation * +find_conversation_with_buddy(PurpleBuddy *buddy) +{ + PidginBlistNode *ui = buddy->node.ui_data; + if (ui) + return ui->conv.conv; + return purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + purple_buddy_get_name(buddy), + purple_buddy_get_account(buddy)); +} + static void gtk_blist_join_chat(PurpleChat *chat) { PurpleConversation *conv; @@ -1354,16 +1380,19 @@ create_chat_menu(PurpleBlistNode *node, PurpleChat *c) { GtkWidget *menu; - gboolean autojoin; + gboolean autojoin, persistent; menu = gtk_menu_new(); autojoin = (purple_blist_node_get_bool(node, "gtk-autojoin") || (purple_blist_node_get_string(node, "gtk-autojoin") != NULL)); + persistent = purple_blist_node_get_bool(node, "gtk-persistent"); pidgin_new_item_from_stock(menu, _("_Join"), PIDGIN_STOCK_CHAT, G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL); pidgin_new_check_item(menu, _("Auto-Join"), G_CALLBACK(gtk_blist_menu_autojoin_cb), node, autojoin); + pidgin_new_check_item(menu, _("Persistent"), + G_CALLBACK(gtk_blist_menu_persistent_cb), node, persistent); pidgin_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL); @@ -3359,17 +3388,17 @@ } if(buddy) { - PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - purple_buddy_get_name(buddy), - purple_buddy_get_account(buddy)); + PurpleConversation *conv = find_conversation_with_buddy(buddy); PurplePresence *p; gboolean trans; if(conv != NULL) { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if((gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) && size == PIDGIN_STATUS_ICON_SMALL) { - return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_MESSAGE, - icon_size, "GtkTreeView"); + if (gtkconv == NULL && size == PIDGIN_STATUS_ICON_SMALL) { + PidginBlistNode *ui = buddy->node.ui_data; + if (ui == NULL || (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)) + return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), + PIDGIN_STOCK_STATUS_MESSAGE, icon_size, "GtkTreeView"); } } @@ -3425,16 +3454,17 @@ struct _pidgin_blist_node *gtkcontactnode = NULL; char *idletime = NULL, *statustext = NULL; time_t t; - PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - purple_buddy_get_name(b), - purple_buddy_get_account(b)); - PidginConversation *gtkconv; + PurpleConversation *conv = find_conversation_with_buddy(b); gboolean hidden_conv = FALSE; - if(conv != NULL) { - gtkconv = PIDGIN_CONVERSATION(conv); - if(gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) { - hidden_conv = TRUE; + if (conv != NULL) { + PidginBlistNode *ui = b->node.ui_data; + if (ui) { + if (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE) + hidden_conv = TRUE; + } else { + if (PIDGIN_CONVERSATION(conv) == NULL) + hidden_conv = TRUE; } } @@ -3769,7 +3799,7 @@ menu = NULL; } - convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0); + convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0); if (!convs) /* no conversations added, don't show the menu */ return; @@ -3825,7 +3855,7 @@ gtkblist->menutrayicon = NULL; } - convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0); + convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0); if (convs) { GtkWidget *img = NULL; GString *tooltip_text = NULL; @@ -3833,14 +3863,10 @@ tooltip_text = g_string_new(""); l = convs; while (l != NULL) { - if (PIDGIN_IS_PIDGIN_CONVERSATION(l->data)) { - PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data); - - g_string_append_printf(tooltip_text, - ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count), - gtkconv->unseen_count, - gtk_label_get_text(GTK_LABEL(gtkconv->tab_label))); - } + int count = GPOINTER_TO_INT(purple_conversation_get_data(l->data, "unseen-count")); + g_string_append_printf(tooltip_text, + ngettext("%d unread message from %s\n", "%d unread messages from %s\n", count), + count, purple_conversation_get_name(l->data)); l = l->next; } if(tooltip_text->len > 0) { @@ -3868,6 +3894,87 @@ conversation_updated_cb(conv, PURPLE_CONV_UPDATE_UNSEEN, gtkblist); } +static void +conversation_deleted_update_ui_cb(PurpleConversation *conv, struct _pidgin_blist_node *ui) +{ + if (ui->conv.conv != conv) + return; + ui->conv.conv = NULL; + ui->conv.flags = 0; + ui->conv.last_message = 0; +} + +static void +written_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message, + PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node) +{ + PidginBlistNode *ui = node->ui_data; + if (ui->conv.conv != conv || PIDGIN_CONVERSATION(conv)) + return; + ui->conv.flags |= PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE; + ui->conv.last_message = time(NULL); /* XXX: for lack of better data */ + pidgin_blist_update(purple_get_blist(), node); +} + +static void +displayed_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message, + PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node) +{ + PidginBlistNode *ui = node->ui_data; + if (ui->conv.conv != conv) + return; + ui->conv.flags &= ~PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE; + pidgin_blist_update(purple_get_blist(), node); +} + +static void +conversation_created_cb(PurpleConversation *conv, PidginBuddyList *gtkblist) +{ + switch (conv->type) { + case PURPLE_CONV_TYPE_IM: + { + GSList *buddies = purple_find_buddies(conv->account, conv->name); + while (buddies) { + PurpleBlistNode *buddy = buddies->data; + struct _pidgin_blist_node *ui = buddy->ui_data; + buddies = g_slist_delete_link(buddies, buddies); + if (!ui) + continue; + ui->conv.conv = conv; + ui->conv.flags = 0; + ui->conv.last_message = 0; + purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", + ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui); + purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg", + ui, PURPLE_CALLBACK(written_msg_update_ui_cb), buddy); + purple_signal_connect(pidgin_conversations_get_handle(), "displayed-im-msg", + ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), buddy); + } + } + case PURPLE_CONV_TYPE_CHAT: + { + PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name); + struct _pidgin_blist_node *ui; + if (!chat) + break; + ui = chat->node.ui_data; + if (!ui) + break; + ui->conv.conv = conv; + ui->conv.flags = 0; + ui->conv.last_message = 0; + purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", + ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui); + purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", + ui, PURPLE_CALLBACK(written_msg_update_ui_cb), chat); + purple_signal_connect(pidgin_conversations_get_handle(), "displayed-chat-msg", + ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), chat); + } + default: + break; + } +} + /********************************************************************************** * Public API Functions * **********************************************************************************/ @@ -4781,6 +4888,9 @@ purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", gtkblist, PURPLE_CALLBACK(conversation_deleting_cb), gtkblist); + purple_signal_connect(purple_conversations_get_handle(), "conversation-created", + gtkblist, PURPLE_CALLBACK(conversation_created_cb), + gtkblist); gtk_widget_hide(gtkblist->headline_hbox); gtk_widget_hide(gtkblist->error_buttons); @@ -4887,6 +4997,7 @@ if(gtknode->recent_signonoff_timer > 0) purple_timeout_remove(gtknode->recent_signonoff_timer); + purple_signals_disconnect_by_handle(node->ui_data); g_free(node->ui_data); node->ui_data = NULL; } @@ -5311,14 +5422,17 @@ GdkPixbuf *emblem; char *mark; gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); - const char *name = purple_chat_get_name(chat); - PurpleConversation *conv = - purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, chat->account); - gboolean hidden = (conv && !PIDGIN_CONVERSATION(conv)); - - if(!insert_node(list, node, &iter)) + PidginBlistNode *ui; + PurpleConversation *conv; + gboolean hidden; + + if (!insert_node(list, node, &iter)) return; + ui = node->ui_data; + conv = ui->conv.conv; + hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)); + status = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_SMALL); emblem = pidgin_blist_get_emblem(node); diff -r 4fdf9c1212e2 -r 7af1af2b7ad3 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Sat Sep 15 03:01:22 2007 +0000 +++ b/pidgin/gtkconv.c Sat Sep 15 06:26:35 2007 +0000 @@ -69,6 +69,8 @@ #include "gtknickcolors.h" +#define CLOSE_CONV_TIMEOUT_SECS (10 * 60) + #define AUTO_RESPONSE "<AUTO-REPLY> : " typedef enum @@ -122,7 +124,6 @@ static GtkWidget *invite_dialog = NULL; static GtkWidget *warn_close_dialog = NULL; -static PidginWindow *hidden_convwin = NULL; static GList *window_list = NULL; /* Lists of status icons at all available sizes for use as window icons */ @@ -160,6 +161,7 @@ static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv); static gboolean pidgin_userlist_motion_cb (GtkWidget *w, GdkEventMotion *event, PidginConversation *gtkconv); static void pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv); +static void hide_conv(PidginConversation *gtkconv, gboolean closetimer); static void pidgin_conv_set_position_size(PidginWindow *win, int x, int y, int width, int height); @@ -207,12 +209,49 @@ **************************************************************************/ static gboolean -close_conv_cb(GtkWidget *w, GdkEventButton *event, PidginConversation *gtkconv) -{ +close_this_sucker(gpointer data) +{ + PidginConversation *gtkconv = data; GList *list = g_list_copy(gtkconv->convs); - g_list_foreach(list, (GFunc)purple_conversation_destroy, NULL); g_list_free(list); + return FALSE; +} + +static gboolean +close_conv_cb(GtkWidget *w, GdkEventButton *event, PidginConversation *gtkconv) +{ + /* We are going to destroy the conversations immediately only if the 'close immediately' + * preference is selected. Otherwise, close the conversation after a reasonable timeout + * (I am going to consider 10 minutes as a 'reasonable timeout' here. + * For chats, close immediately if the chat is not in the buddylist, or if the chat is + * not marked 'Persistent' */ + PurpleConversation *conv = gtkconv->active_conv; + PurpleAccount *account = purple_conversation_get_account(conv); + const char *name = purple_conversation_get_name(conv); + + switch (purple_conversation_get_type(conv)) { + case PURPLE_CONV_TYPE_IM: + { + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately")) + close_this_sucker(gtkconv); + else + hide_conv(gtkconv, TRUE); + break; + } + case PURPLE_CONV_TYPE_CHAT: + { + PurpleChat *chat = purple_blist_find_chat(account, name); + if (!chat || + !purple_blist_node_get_bool(&chat->node, "gtk-persistent")) + close_this_sucker(gtkconv); + else + hide_conv(gtkconv, FALSE); + break; + } + default: + ; + } return TRUE; } @@ -1314,18 +1353,33 @@ add_remove_cb(NULL, PIDGIN_CONVERSATION(conv)); } -#if 0 -static void -menu_hide_conv_cb(gpointer data, guint action, GtkWidget *widget) -{ - PidginWindow *win = data; - PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win); - PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win); +static gboolean +close_already(gpointer data) +{ + purple_conversation_destroy(data); + return FALSE; +} + +static void +hide_conv(PidginConversation *gtkconv, gboolean closetimer) +{ + GList *list; + purple_signal_emit(pidgin_conversations_get_handle(), "conversation-hiding", gtkconv); - purple_conversation_set_ui_ops(conv, NULL); -} -#endif + + for (list = g_list_copy(gtkconv->convs); list; list = g_list_delete_link(list, list)) { + PurpleConversation *conv = list->data; + if (closetimer) { + guint timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer")); + if (timer) + purple_timeout_remove(timer); + timer = purple_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv); + purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(timer)); + } + purple_conversation_set_ui_ops(conv, NULL); + } +} static void menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget) @@ -2340,63 +2394,69 @@ return get_prpl_icon_list(account); } -GdkPixbuf * -pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon) -{ - PurpleAccount *account = NULL; - const char *name = NULL; - GdkPixbuf *status = NULL; - PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); - const char *icon_size = small_icon ? PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL; - g_return_val_if_fail(conv != NULL, NULL); - - account = purple_conversation_get_account(conv); - name = purple_conversation_get_name(conv); - - g_return_val_if_fail(account != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - /* Use the buddy icon, if possible */ - if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { - PurpleBuddy *b = purple_find_buddy(account, name); - if (b != NULL) { +static GdkPixbuf * +pidgin_conv_get_icon(PurpleConversation *conv, GtkWidget *parent, const char *icon_size) +{ + PurpleAccount *account = NULL; + const char *name = NULL; + GdkPixbuf *status = NULL; + PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); + g_return_val_if_fail(conv != NULL, NULL); + + account = purple_conversation_get_account(conv); + name = purple_conversation_get_name(conv); + + g_return_val_if_fail(account != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + /* Use the buddy icon, if possible */ + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + PurpleBuddy *b = purple_find_buddy(account, name); + if (b != NULL) { PurplePresence *p = purple_buddy_get_presence(b); - /* I hate this hack. It fixes a bug where the pending message icon - * displays in the conv tab even though it shouldn't. - * A better solution would be great. */ - if (ops && ops->update) - ops->update(NULL, (PurpleBlistNode*)b); + /* I hate this hack. It fixes a bug where the pending message icon + * displays in the conv tab even though it shouldn't. + * A better solution would be great. */ + if (ops && ops->update) + ops->update(NULL, (PurpleBlistNode*)b); /* XXX Seanegan: We really need a util function to return a pixbuf for a Presence to avoid all this switching */ if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AWAY)) - status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, PIDGIN_CONVERSATION(conv)->icon, icon_size); + status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, parent, icon_size); else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_EXTENDED_AWAY)) - status = pidgin_create_status_icon(PURPLE_STATUS_EXTENDED_AWAY, PIDGIN_CONVERSATION(conv)->icon, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE)) - status = pidgin_create_status_icon(PURPLE_STATUS_OFFLINE, PIDGIN_CONVERSATION(conv)->icon, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE)) - status = pidgin_create_status_icon(PURPLE_STATUS_AVAILABLE, PIDGIN_CONVERSATION(conv)->icon, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE)) - status = pidgin_create_status_icon(PURPLE_STATUS_INVISIBLE, PIDGIN_CONVERSATION(conv)->icon, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE)) - status = pidgin_create_status_icon(PURPLE_STATUS_UNAVAILABLE, PIDGIN_CONVERSATION(conv)->icon, icon_size); - } - } - - /* If they don't have a buddy icon, then use the PRPL icon */ - if (status == NULL) { + status = pidgin_create_status_icon(PURPLE_STATUS_EXTENDED_AWAY, parent, icon_size); + else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE)) + status = pidgin_create_status_icon(PURPLE_STATUS_OFFLINE, parent, icon_size); + else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE)) + status = pidgin_create_status_icon(PURPLE_STATUS_AVAILABLE, parent, icon_size); + else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE)) + status = pidgin_create_status_icon(PURPLE_STATUS_INVISIBLE, parent, icon_size); + else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE)) + status = pidgin_create_status_icon(PURPLE_STATUS_UNAVAILABLE, parent, icon_size); + } + } + + /* If they don't have a buddy icon, then use the PRPL icon */ + if (status == NULL) { GtkIconSize size = gtk_icon_size_from_name(icon_size); if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { - status = gtk_widget_render_icon (PIDGIN_CONVERSATION(conv)->icon, PIDGIN_STOCK_STATUS_PERSON, - size, "GtkWidget"); + status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_PERSON, + size, "GtkWidget"); } else { - status = gtk_widget_render_icon (PIDGIN_CONVERSATION(conv)->icon, PIDGIN_STOCK_STATUS_CHAT, - size, "GtkWidget"); + status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_CHAT, + size, "GtkWidget"); } } return status; } +GdkPixbuf * +pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon) +{ + const char *icon_size = small_icon ? PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL; + return pidgin_conv_get_icon(conv, PIDGIN_CONVERSATION(conv)->icon, icon_size); +} + static void update_tab_icon(PurpleConversation *conv) @@ -2748,9 +2808,9 @@ PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); GdkModifierType state; - if(gtkconv->win==hidden_convwin) { - pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); - pidgin_conv_placement_place(gtkconv); + if (gtkconv == NULL) { + pidgin_conv_attach_to_conversation(conv); + gtkconv = PIDGIN_CONVERSATION(conv); } pidgin_conv_switch_active_conversation(conv); @@ -2783,15 +2843,19 @@ PurpleConversation *conv = (PurpleConversation*)l->data; PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if(gtkconv == NULL || gtkconv->active_conv != conv) + if (gtkconv != NULL && gtkconv->active_conv != conv) continue; - - if (gtkconv->unseen_state >= min_state - && (!hidden_only || - (hidden_only && gtkconv->win == hidden_convwin))) { - + if (gtkconv == NULL) { + if (!hidden_only || + !purple_conversation_get_data(conv, "unseen-count")) + continue; r = g_list_prepend(r, conv); c++; + } else { + if (gtkconv->unseen_state >= min_state && !hidden_only) { + r = g_list_prepend(r, conv); + c++; + } } } @@ -2831,11 +2895,11 @@ PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); GtkWidget *icon = gtk_image_new(); - GdkPixbuf *pbuf = pidgin_conv_get_tab_icon(conv, TRUE); + GdkPixbuf *pbuf = pidgin_conv_get_icon(conv, icon, PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC); GtkWidget *item; gchar *text = g_strdup_printf("%s (%d)", - gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)), - gtkconv->unseen_count); + gtkconv ? gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)) : purple_conversation_get_name(conv), + gtkconv ? gtkconv->unseen_count : GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))); gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pbuf); g_object_unref(pbuf); @@ -3093,7 +3157,7 @@ PurpleConversation *conv; GtkWidget *item; - if (win->window == NULL || win == hidden_convwin) + if (win->window == NULL) return; gtkconv = pidgin_conv_window_get_active_gtkconv(win); @@ -4942,6 +5006,9 @@ GtkWidget *tab_cont; PurpleBlistNode *convnode; + if (hidden) + return; + if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) { conv->ui_data = gtkconv; if (!g_list_find(gtkconv->convs, conv)) @@ -5041,10 +5108,7 @@ G_CALLBACK(gtk_widget_grab_focus), gtkconv->entry); - if (hidden) - pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv); - else - pidgin_conv_placement_place(gtkconv); + pidgin_conv_placement_place(gtkconv); if (nick_colors == NULL) { nbr_nick_colors = NUM_NICK_COLORS; @@ -5052,11 +5116,13 @@ } } +#if 0 static void pidgin_conv_new_hidden(PurpleConversation *conv) { private_gtkconv_new(conv, TRUE); } +#endif void pidgin_conv_new(PurpleConversation *conv) @@ -5073,22 +5139,20 @@ return; /* create hidden conv if hide_new pref is always */ - if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) - { - ui_ops->create_conversation = pidgin_conv_new_hidden; - purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); - ui_ops->create_conversation = pidgin_conv_new; - return; - } - - /* create hidden conv if hide_new pref is away and account is away */ - if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 && - !purple_status_is_available(purple_account_get_active_status(account))) - { - ui_ops->create_conversation = pidgin_conv_new_hidden; - purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); - ui_ops->create_conversation = pidgin_conv_new; - return; + /* or if hide_new pref is away and account is away */ + if ((strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) || + (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 && + !purple_status_is_available(purple_account_get_active_status(account)))) { + if (!conv) { + ui_ops->create_conversation = NULL; + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); + purple_conversation_set_ui_ops(conv, NULL); + ui_ops->create_conversation = pidgin_conv_new; + } + } else { + /* new message for an IM */ + if (conv && conv->type == PURPLE_CONV_TYPE_IM) + pidgin_conv_attach_to_conversation(conv); } } @@ -5097,6 +5161,9 @@ { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + if (!gtkconv) + return; + gtkconv->convs = g_list_remove(gtkconv->convs, conv); /* Don't destroy ourselves until all our convos are gone */ if (gtkconv->convs) { @@ -6569,6 +6636,19 @@ pidgin_conv_update_fields(conv, flags); } +static void +wrote_msg_update_unseen_cb(PurpleAccount *account, const char *who, const char *message, + PurpleConversation *conv, PurpleMessageFlags flag, gpointer null) +{ + if (conv == NULL || PIDGIN_IS_PIDGIN_CONVERSATION(conv)) + return; + if (flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) { + purple_conversation_set_data(conv, "unseen-count", + GINT_TO_POINTER(GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")) + 1)); + purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN); + } +} + static PurpleConversationUiOps conversation_ui_ops = { pidgin_conv_new, @@ -7066,6 +7146,7 @@ account_status_changed_cb(PurpleAccount *account, PurpleStatus *oldstatus, PurpleStatus *newstatus) { +#if 0 GList *l; PurpleConversation *conv = NULL; PidginConversation *gtkconv; @@ -7075,27 +7156,7 @@ if(purple_status_is_available(oldstatus) || !purple_status_is_available(newstatus)) return; - - while ((l = hidden_convwin->gtkconvs) != NULL) - { - gtkconv = l->data; - - conv = gtkconv->active_conv; - - while(l && !purple_status_is_available( - purple_account_get_active_status( - purple_conversation_get_account(conv)))) - l = l->next; - if (!l) - break; - - pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); - pidgin_conv_placement_place(gtkconv); - - /* TODO: do we need to do anything for any other conversations that are in the same gtkconv here? - * I'm a little concerned that not doing so will cause the "pending" indicator in the gtkblist not to be cleared. -DAA*/ - purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN); - } +#endif } static void @@ -7103,32 +7164,25 @@ gconstpointer value, gpointer data) { GList *l; - PurpleConversation *conv = NULL; - PidginConversation *gtkconv; gboolean when_away = FALSE; - if(!hidden_convwin) - return; - if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always")==0) return; if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away")==0) when_away = TRUE; - while ((l = hidden_convwin->gtkconvs) != NULL) + for (l = purple_get_conversations(); l; l = l->next) { - gtkconv = l->data; - - conv = gtkconv->active_conv; - + PurpleConversation *conv = l->data; + PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + if (gtkconv) + continue; if(when_away && !purple_status_is_available( purple_account_get_active_status( purple_conversation_get_account(conv)))) continue; - - pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); - pidgin_conv_placement_place(gtkconv); + pidgin_conv_attach_to_conversation(conv); } } @@ -7314,6 +7368,10 @@ gtkconv->attach.timer = 0; while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */ PurpleConvMessage *msg = gtkconv->attach.current->data; + if (gtkconv->attach.when && gtkconv->attach.when < msg->when) { + gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "
", 0); + gtkconv->attach.when = 0; + } pidgin_conv_write_conv(gtkconv->active_conv, msg->who, msg->who, msg->what, msg->flags, msg->when); gtkconv->attach.current = gtkconv->attach.current->prev; count++; @@ -7333,18 +7391,20 @@ { GList *list; PidginConversation *gtkconv; + int timer; if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) return FALSE; + purple_conversation_set_data(conv, "unseen-count", NULL); purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops()); private_gtkconv_new(conv, FALSE); gtkconv = PIDGIN_CONVERSATION(conv); list = purple_conversation_get_message_history(conv); if (list) { - list = g_list_last(list); - gtkconv->attach.current = list; + gtkconv->attach.when = ((PurpleConvMessage*)(list->data))->when; + gtkconv->attach.current = g_list_last(list); gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv); } else { purple_signal_emit(pidgin_conversations_get_handle(), @@ -7356,6 +7416,10 @@ pidgin_conv_chat_add_users(conv, PURPLE_CONV_CHAT(conv)->in_room, TRUE); } + timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer")); + if (timer) + purple_timeout_remove(timer); + return TRUE; } @@ -7421,6 +7485,7 @@ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", TRUE); purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never"); + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", FALSE); #ifdef _WIN32 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", FALSE); @@ -7584,9 +7649,6 @@ purple_conversations_set_ui_ops(&conversation_ui_ops); - hidden_convwin = pidgin_conv_window_new(); - window_list = g_list_remove(window_list, hidden_convwin); - purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", handle, PURPLE_CALLBACK(account_status_changed_cb), NULL); @@ -7622,6 +7684,10 @@ purple_signal_connect_priority(purple_conversations_get_handle(), "conversation-updated", handle, PURPLE_CALLBACK(pidgin_conv_updated), NULL, PURPLE_SIGNAL_PRIORITY_LOWEST); + purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg", handle, + PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL); + purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", handle, + PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL); } void @@ -7630,8 +7696,6 @@ purple_prefs_disconnect_by_handle(pidgin_conversations_get_handle()); purple_signals_disconnect_by_handle(pidgin_conversations_get_handle()); purple_signals_unregister_by_instance(pidgin_conversations_get_handle()); - pidgin_conv_window_destroy(hidden_convwin); - hidden_convwin=NULL; } @@ -9048,7 +9112,7 @@ if (win->gtkconvs && win->gtkconvs->next == NULL) pidgin_conv_tab_pack(win, win->gtkconvs->data); - if (!win->gtkconvs && win != hidden_convwin) + if (!win->gtkconvs) pidgin_conv_window_destroy(win); } @@ -9587,9 +9651,7 @@ gboolean pidgin_conv_is_hidden(PidginConversation *gtkconv) { - g_return_val_if_fail(gtkconv != NULL, FALSE); - - return (gtkconv->win == hidden_convwin); + return (gtkconv == NULL); } @@ -9690,3 +9752,4 @@ return colors; } + diff -r 4fdf9c1212e2 -r 7af1af2b7ad3 pidgin/gtkconv.h --- a/pidgin/gtkconv.h Sat Sep 15 03:01:22 2007 +0000 +++ b/pidgin/gtkconv.h Sat Sep 15 06:26:35 2007 +0000 @@ -168,6 +168,7 @@ struct { int timer; GList *current; + time_t when; } attach; }; diff -r 4fdf9c1212e2 -r 7af1af2b7ad3 pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Sat Sep 15 03:01:22 2007 +0000 +++ b/pidgin/gtkprefs.c Sat Sep 15 06:26:35 2007 +0000 @@ -993,6 +993,8 @@ pidgin_prefs_checkbox(_("Show _formatting on incoming messages"), PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox); + pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"), + PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", vbox); iconpref1 = pidgin_prefs_checkbox(_("Show _detailed information"), PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", vbox);