# HG changeset patch # User Sadrul Habib Chowdhury # Date 1188275572 0 # Node ID 4da3c8618c241021c18966bc947108796ad1f5ee # Parent f0c3497e2ea68ddba5a1a85a62a48a9cd1a21ee1# Parent c17e41049b61338f3c7e371de371dfeedc3f27f9 propagate from branch 'im.pidgin.pidgin' (head 4313008137cace2c9699584ec7308c1e888ae137) to branch 'im.pidgin.sadrul.conv.persistent' (head 8b67243aa294f0638d06c636c153233b0d44a6e2) diff -r f0c3497e2ea6 -r 4da3c8618c24 finch/gntconv.c --- a/finch/gntconv.c Tue Aug 28 03:46:31 2007 +0000 +++ b/finch/gntconv.c Tue Aug 28 04:32:52 2007 +0000 @@ -1006,6 +1006,7 @@ { FinchConv *ggconv = conv->ui_data; gnt_text_view_clear(GNT_TEXT_VIEW(ggconv->tv)); + purple_conversation_clear_message_history(conv); return PURPLE_CMD_STATUS_OK; } diff -r f0c3497e2ea6 -r 4da3c8618c24 finch/libgnt/pygnt/dbus-gnt --- a/finch/libgnt/pygnt/dbus-gnt Tue Aug 28 03:46:31 2007 +0000 +++ b/finch/libgnt/pygnt/dbus-gnt Tue Aug 28 04:32:52 2007 +0000 @@ -13,7 +13,7 @@ import gnt import sys -from time import strftime +import time convwins = {} @@ -27,19 +27,26 @@ # if a conv window is closed, then reopened, this thing crashes convwins[key] = None -def wrote_msg(account, who, msg, conv, flags): - stuff = show_conversation(conv) +def add_message(conv, who, msg, flags, timestamp): + stuff = show_conversation(conv, False) tv = stuff[1] tv.append_text_with_flags("\n", 0) - tv.append_text_with_flags(strftime("(%X) "), 8) + if timestamp: + tv.append_text_with_flags(time.strftime("(%X) ", time.localtime(timestamp)), 8) + else: + tv.append_text_with_flags(time.strftime("(%X) "), 8) if flags & 3: tv.append_text_with_flags(who + ": ", 1) + msg = purple.PurpleMarkupStripHtml(msg) tv.append_text_with_flags(msg, 0) stuff[0].set_urgent() else: tv.append_text_with_flags(msg, 8) tv.scroll(0) +def wrote_msg(account, who, msg, conv, flags): + add_message(conv, who, msg, flags, None) + bus = dbus.SessionBus() obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject") purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface") @@ -79,7 +86,7 @@ def conv_window_destroyed(win, key): del convwins[key] -def show_conversation(conv): +def show_conversation(conv, history): key = get_dict_key(conv) if key in convwins: return convwins[key] @@ -96,9 +103,21 @@ vbox.add_widget(entry) entry.connect("key_pressed", send_im_cb, conv) tv.clear() + tv.attach_scroll_widget(entry) win.show() convwins[key] = [win, tv, entry] win.connect("destroy", conv_window_destroyed, key) + + if history: + msgs = purple.PurpleConversationGetMessageHistory(conv) + msgs.reverse() + for msg in msgs: + who = purple.PurpleConversationMessageGetSender(msg) + what = purple.PurpleConversationMessageGetMessage(msg) + flags = purple.PurpleConversationMessageGetFlags(msg) + when = purple.PurpleConversationMessageGetTimestamp(msg) + add_message(conv, who, what, flags, when) + return convwins[key] def show_buddylist(): @@ -127,7 +146,7 @@ convs = purple.PurpleGetConversations() for conv in convs: - show_conversation(conv) + show_conversation(conv, True) gnt.gnt_main() diff -r f0c3497e2ea6 -r 4da3c8618c24 finch/libgnt/pygnt/gendef.sh --- a/finch/libgnt/pygnt/gendef.sh Tue Aug 28 03:46:31 2007 +0000 +++ b/finch/libgnt/pygnt/gendef.sh Tue Aug 28 04:32:52 2007 +0000 @@ -31,7 +31,9 @@ rm -f gnt.def for file in $FILES do + echo -n "Generating definitions for ${file} ... " python /usr/share/pygtk/2.0/codegen/h2def.py ../$file >> gnt.def + echo "Done" done # Remove the definitions about the enums diff -r f0c3497e2ea6 -r 4da3c8618c24 libpurple/conversation.c --- a/libpurple/conversation.c Tue Aug 28 03:46:31 2007 +0000 +++ b/libpurple/conversation.c Tue Aug 28 04:32:52 2007 +0000 @@ -39,6 +39,7 @@ static GList *ims = NULL; static GList *chats = NULL; static PurpleConversationUiOps *default_ops = NULL; +static GHashTable *histories = NULL; void purple_conversations_set_ui_ops(PurpleConversationUiOps *ops) @@ -202,6 +203,51 @@ conv, time(NULL), NULL)); } +/* Functions that deal with PurpleConvMessage */ + +static void +add_message_to_history(PurpleConversation *conv, const char *who, const char *message, + PurpleMessageFlags flags, time_t when) +{ + GList *list; + PurpleConvMessage *msg; + + if (flags & PURPLE_MESSAGE_SEND) { + const char *me = NULL; + if (conv->account->gc) + me = conv->account->gc->display_name; + if (!me) + me = conv->account->username; + who = me; + } + + msg = g_new0(PurpleConvMessage, 1); + PURPLE_DBUS_REGISTER_POINTER(msg, PurpleConvMessage); + msg->who = g_strdup(who); + msg->flags = flags; + msg->what = g_strdup(message); + msg->when = when; + + list = g_hash_table_lookup(histories, conv); + list = g_list_prepend(list, msg); + g_hash_table_insert(histories, conv, list); +} + +static void +free_conv_message(PurpleConvMessage *msg) +{ + g_free(msg->who); + g_free(msg->what); + PURPLE_DBUS_UNREGISTER_POINTER(msg); + g_free(msg); +} + +static void +message_history_free(GList *list) +{ + g_list_foreach(list, (GFunc)free_conv_message, NULL); + g_list_free(list); +} /************************************************************************** * Conversation API @@ -473,6 +519,8 @@ purple_conversation_close_logs(conv); + purple_conversation_clear_message_history(conv); + PURPLE_DBUS_UNREGISTER_POINTER(conv); g_free(conv); conv = NULL; @@ -805,9 +853,6 @@ ops = purple_conversation_get_ui_ops(conv); - if (ops == NULL || ops->write_conv == NULL) - return; - account = purple_conversation_get_account(conv); type = purple_conversation_get_type(conv); @@ -891,7 +936,9 @@ } } - ops->write_conv(conv, who, alias, displayed, flags, mtime); + if (ops && ops->write_conv) + ops->write_conv(conv, who, alias, displayed, flags, mtime); + add_message_to_history(conv, who, message, flags, mtime); purple_signal_emit(purple_conversations_get_handle(), (type == PURPLE_CONV_TYPE_IM ? "wrote-im-msg" : "wrote-chat-msg"), @@ -2020,6 +2067,42 @@ return menu; } +void purple_conversation_clear_message_history(PurpleConversation *conv) +{ + GList *list = g_hash_table_lookup(histories, conv); + message_history_free(list); + g_hash_table_remove(histories, conv); +} + +GList *purple_conversation_get_message_history(PurpleConversation *conv) +{ + return g_hash_table_lookup(histories, conv); +} + +const char *purple_conversation_message_get_sender(PurpleConvMessage *msg) +{ + g_return_val_if_fail(msg, NULL); + return msg->who; +} + +const char *purple_conversation_message_get_message(PurpleConvMessage *msg) +{ + g_return_val_if_fail(msg, NULL); + return msg->what; +} + +PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg) +{ + g_return_val_if_fail(msg, 0); + return msg->flags; +} + +time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg) +{ + g_return_val_if_fail(msg, 0); + return msg->when; +} + gboolean purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **error) @@ -2300,6 +2383,9 @@ purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONVERSATION), purple_value_new(PURPLE_TYPE_BOXED, "GList **")); + + /* Initialize the history */ + histories = g_hash_table_new(g_direct_hash, g_direct_equal); } void @@ -2308,4 +2394,6 @@ while (conversations) purple_conversation_destroy((PurpleConversation*)conversations->data); purple_signals_unregister_by_instance(purple_conversations_get_handle()); + g_hash_table_destroy(histories); + histories = NULL; } diff -r f0c3497e2ea6 -r 4da3c8618c24 libpurple/conversation.h --- a/libpurple/conversation.h Tue Aug 28 03:46:31 2007 +0000 +++ b/libpurple/conversation.h Tue Aug 28 04:32:52 2007 +0000 @@ -37,6 +37,7 @@ typedef struct _PurpleConvIm PurpleConvIm; typedef struct _PurpleConvChat PurpleConvChat; typedef struct _PurpleConvChatBuddy PurpleConvChatBuddy; +typedef struct _PurpleConvMessage PurpleConvMessage; /** * A type of conversation. @@ -283,6 +284,17 @@ }; /** + * Description of a conversation message + */ +struct _PurpleConvMessage +{ + char *who; + char *what; + PurpleMessageFlags flags; + time_t when; +}; + +/** * A core representation of a conversation between two or more people. * * The conversation can be an IM or a chat. @@ -650,6 +662,60 @@ */ void purple_conversation_foreach(void (*func)(PurpleConversation *conv)); +/** + * Retrieve the message history of a conversation. + * + * @param conv The conversation + * + * @return A GList of PurpleConvMessage's. The must not modify the list or the data within. + * The list contains the newest message at the beginning, and the oldest message at + * the end. + */ +GList *purple_conversation_get_message_history(PurpleConversation *conv); + +/** + * Clear the message history of a conversation. + * + * @param conv The conversation + */ +void purple_conversation_clear_message_history(PurpleConversation *conv); + +/** + * Get the sender from a PurpleConvMessage + * + * @param msg A PurpleConvMessage + * + * @return The name of the sender of the message + */ +const char *purple_conversation_message_get_sender(PurpleConvMessage *msg); + +/** + * Get the message from a PurpleConvMessage + * + * @param msg A PurpleConvMessage + * + * @return The name of the sender of the message + */ +const char *purple_conversation_message_get_message(PurpleConvMessage *msg); + +/** + * Get the message-flags of a PurpleConvMessage + * + * @param msg A PurpleConvMessage + * + * @return The name of the sender of the message + */ +PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg); + +/** + * Get the timestamp of a PurpleConvMessage + * + * @param msg A PurpleConvMessage + * + * @return The name of the sender of the message + */ +time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg); + /*@}*/ diff -r f0c3497e2ea6 -r 4da3c8618c24 libpurple/dbus-analyze-functions.py --- a/libpurple/dbus-analyze-functions.py Tue Aug 28 03:46:31 2007 +0000 +++ b/libpurple/dbus-analyze-functions.py Tue Aug 28 04:32:52 2007 +0000 @@ -66,6 +66,7 @@ "purple_savedstatuses_get_all", "purple_status_type_get_attrs", "purple_presence_get_statuses", + "purple_conversation_get_message_history", ] pointer = "#pointer#" diff -r f0c3497e2ea6 -r 4da3c8618c24 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Tue Aug 28 03:46:31 2007 +0000 +++ b/pidgin/gtkblist.c Tue Aug 28 04:32:52 2007 +0000 @@ -332,8 +332,10 @@ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, chat->account); - if (conv != NULL) + if (conv != NULL) { + pidgin_conv_attach_to_conversation(conv); purple_conversation_present(conv); + } serv_join_chat(chat->account->gc, chat->components); g_free(chat_name); diff -r f0c3497e2ea6 -r 4da3c8618c24 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Tue Aug 28 03:46:31 2007 +0000 +++ b/pidgin/gtkconv.c Tue Aug 28 04:32:52 2007 +0000 @@ -326,6 +326,7 @@ const char *cmd, char **args, char **error, void *data) { clear_conversation_scrollback(conv); + purple_conversation_clear_message_history(conv); return PURPLE_CMD_STATUS_OK; } @@ -1299,6 +1300,14 @@ } static void +menu_hide_conv_cb(gpointer data, guint action, GtkWidget *widget) +{ + PidginWindow *win = data; + PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win); + purple_conversation_set_ui_ops(conv, NULL); +} + +static void menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget) { PidginWindow *win = data; @@ -2697,6 +2706,7 @@ pidgin_conv_present_conversation(PurpleConversation *conv) { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + GdkModifierType state; if(gtkconv->win==hidden_convwin) { pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); @@ -2704,7 +2714,10 @@ } pidgin_conv_switch_active_conversation(conv); - pidgin_conv_window_switch_gtkconv(gtkconv->win, gtkconv); + /* Switch the tab only if the user initiated the event by pressing + * a button or hitting a key. */ + if (gtk_get_current_event_state(&state)) + pidgin_conv_window_switch_gtkconv(gtkconv->win, gtkconv); gtk_window_present(GTK_WINDOW(gtkconv->win->window)); } @@ -2730,7 +2743,7 @@ PurpleConversation *conv = (PurpleConversation*)l->data; PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if(gtkconv->active_conv != conv) + if(gtkconv == NULL || gtkconv->active_conv != conv) continue; if (gtkconv->unseen_state >= min_state @@ -2844,6 +2857,8 @@ { "/Conversation/sep4", NULL, NULL, 0, "", NULL }, + { N_("/Conversation/_Hide"), NULL, menu_hide_conv_cb, 0, + "", NULL}, { N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0, "", GTK_STOCK_CLOSE }, @@ -5011,6 +5026,10 @@ g_list_foreach(gtkconv->send_history, (GFunc)g_free, NULL); g_list_free(gtkconv->send_history); + if (gtkconv->attach.timer) { + g_source_remove(gtkconv->attach.timer); + } + if (tooltip.gtkconv == gtkconv) reset_tooltip(); @@ -5227,6 +5246,15 @@ gtkconv = PIDGIN_CONVERSATION(conv); g_return_if_fail(gtkconv != NULL); + if (gtkconv->attach.timer) { + /* We are currently in the process of filling up the buffer with the message + * history of the conversation. So we do not need to add the message here. + * Instead, this message will be added to the message-list, which in turn will + * be processed and displayed by the attach-callback. + */ + return; + } + if (conv != gtkconv->active_conv) { if (flags & PURPLE_MESSAGE_ACTIVE_ONLY) @@ -7161,6 +7189,55 @@ pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC); } +static gboolean +add_message_history_to_gtkconv(gpointer data) +{ + PidginConversation *gtkconv = data; + int count = 0; + int timer = gtkconv->attach.timer; + gtkconv->attach.timer = 0; + while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */ + PurpleConvMessage *msg = gtkconv->attach.current->data; + 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++; + } + gtkconv->attach.timer = timer; + if (gtkconv->attach.current) + return TRUE; + + g_source_remove(gtkconv->attach.timer); + gtkconv->attach.timer = 0; + return FALSE; +} + +gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv) +{ + GList *list; + PidginConversation *gtkconv; + + if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) + return FALSE; + + 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.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv); + } + + /* XXX: If this is a chat: + * - populate the userlist + * - set the topic + */ + + return TRUE; +} + void * pidgin_conversations_get_handle(void) { diff -r f0c3497e2ea6 -r 4da3c8618c24 pidgin/gtkconv.h --- a/pidgin/gtkconv.h Tue Aug 28 03:46:31 2007 +0000 +++ b/pidgin/gtkconv.h Tue Aug 28 04:32:52 2007 +0000 @@ -162,6 +162,13 @@ GtkWidget *infopane; GtkListStore *infopane_model; GtkTreeIter infopane_iter; + + /* Used when attaching a PidginConversation to a PurpleConversation + * with message history */ + struct { + int timer; + GList *current; + } attach; }; /*@}*/ @@ -238,6 +245,8 @@ */ void pidgin_conv_present_conversation(PurpleConversation *conv); +gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv); + PidginWindow *pidgin_conv_get_window(PidginConversation *gtkconv); GdkPixbuf *pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon); void pidgin_conv_new(PurpleConversation *conv); diff -r f0c3497e2ea6 -r 4da3c8618c24 pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Tue Aug 28 03:46:31 2007 +0000 +++ b/pidgin/gtkdialogs.c Tue Aug 28 04:32:52 2007 +0000 @@ -768,6 +768,7 @@ if (conv == NULL) conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username); + pidgin_conv_attach_to_conversation(conv); purple_conversation_present(conv); } diff -r f0c3497e2ea6 -r 4da3c8618c24 pidgin/gtksound.c --- a/pidgin/gtksound.c Tue Aug 28 03:46:31 2007 +0000 +++ b/pidgin/gtksound.c Tue Aug 28 04:32:52 2007 +0000 @@ -114,7 +114,7 @@ play_conv_event(PurpleConversation *conv, PurpleSoundEventID event) { /* If we should not play the sound for some reason, then exit early */ - if (conv != NULL) + if (conv != NULL && PIDGIN_IS_PIDGIN_CONVERSATION(conv)) { PidginConversation *gtkconv; PidginWindow *win;