changeset 19514:7b348650524e

merge of '45cf46d3ba2b0debc7f98159ddf5a0262809cde4' and '9a3d36b0c95c012e35437cbd6cbab3949631344e'
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Tue, 28 Aug 2007 21:03:49 +0000
parents 8c59bb6dfc7e (current diff) ea26d30449fd (diff)
children b62683f4120d
files
diffstat 14 files changed, 302 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Tue Aug 28 12:34:33 2007 +0000
+++ b/ChangeLog.API	Tue Aug 28 21:03:49 2007 +0000
@@ -1,6 +1,12 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 Version 2.2.0 (??/??/????):
+	libpurple:
+		Added:
+		* PURPLE_MESSAGE_INVISIBLE flag, which can be used by
+		  purple_conv_im_send_with_flags to send a message, but not display it
+		  in the conversation
+
 	Pidgin:
 		Added:
 		* pidgin_set_accessible_relations, sets up label-for and labelled-by
--- a/finch/gntconv.c	Tue Aug 28 12:34:33 2007 +0000
+++ b/finch/gntconv.c	Tue Aug 28 21:03:49 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;
 }
 
--- a/finch/libgnt/pygnt/dbus-gnt	Tue Aug 28 12:34:33 2007 +0000
+++ b/finch/libgnt/pygnt/dbus-gnt	Tue Aug 28 21:03:49 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()
 
--- a/finch/libgnt/pygnt/gendef.sh	Tue Aug 28 12:34:33 2007 +0000
+++ b/finch/libgnt/pygnt/gendef.sh	Tue Aug 28 21:03:49 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
--- a/libpurple/conversation.c	Tue Aug 28 12:34:33 2007 +0000
+++ b/libpurple/conversation.c	Tue Aug 28 21:03:49 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)
@@ -111,17 +112,17 @@
 
 	/* Always linkfy the text for display, unless we're
 	 * explicitly asked to do otheriwse*/
-	if(msgflags & PURPLE_MESSAGE_NO_LINKIFY)
-		displayed = g_strdup(message);
-	else
-		displayed = purple_markup_linkify(message);
-
-	if ((conv->features & PURPLE_CONNECTION_HTML) &&
-		!(msgflags & PURPLE_MESSAGE_RAW))
-	{
+	if (!(msgflags & PURPLE_MESSAGE_INVISIBLE)) {
+		if(msgflags & PURPLE_MESSAGE_NO_LINKIFY)
+			displayed = g_strdup(message);
+		else
+			displayed = purple_markup_linkify(message);
+	}
+
+	if (displayed && (conv->features & PURPLE_CONNECTION_HTML) &&
+		!(msgflags & PURPLE_MESSAGE_RAW)) {
 		sent = g_strdup(displayed);
-	}
-	else
+	} else
 		sent = g_strdup(message);
 
 	msgflags |= PURPLE_MESSAGE_SEND;
@@ -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;
 }
--- a/libpurple/conversation.h	Tue Aug 28 12:34:33 2007 +0000
+++ b/libpurple/conversation.h	Tue Aug 28 21:03:49 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.
@@ -117,9 +118,9 @@
 	                                        apply formatting         */
 	PURPLE_MESSAGE_IMAGES      = 0x1000, /**< Message contains images  */
 	PURPLE_MESSAGE_NOTIFY      = 0x2000, /**< Message is a notification */
-	PURPLE_MESSAGE_NO_LINKIFY  = 0x4000  /**< Message should not be auto-
+	PURPLE_MESSAGE_NO_LINKIFY  = 0x4000, /**< Message should not be auto-
 										   linkified */
-
+	PURPLE_MESSAGE_INVISIBLE   = 0x8000, /**< Message should not be displayed */
 } PurpleMessageFlags;
 
 /**
@@ -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);
+
 /*@}*/
 
 
--- a/libpurple/dbus-analyze-functions.py	Tue Aug 28 12:34:33 2007 +0000
+++ b/libpurple/dbus-analyze-functions.py	Tue Aug 28 21:03:49 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#"
--- a/libpurple/protocols/yahoo/yahoo.c	Tue Aug 28 12:34:33 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Tue Aug 28 21:03:49 2007 +0000
@@ -4096,9 +4096,7 @@
 
 	purple_debug(PURPLE_DEBUG_INFO, "yahoo",
 	           "Sending <ding> on account %s to buddy %s.\n", username, c->name);
-	/* TODO: find out how to send a <ding> without showing up as a blank line on
-	 * the conversation window. */
-	purple_conv_im_send(PURPLE_CONV_IM(c), "<ding>");
+	purple_conv_im_send_with_flags(PURPLE_CONV_IM(c), "<ding>", PURPLE_MESSAGE_INVISIBLE);
 
 	return TRUE;
 }
--- a/libpurple/server.c	Tue Aug 28 12:34:33 2007 +0000
+++ b/libpurple/server.c	Tue Aug 28 21:03:49 2007 +0000
@@ -272,6 +272,7 @@
 	PurpleAttentionType *attn;
 	PurpleMessageFlags flags;
 	PurplePlugin *prpl;
+	PurpleConversation *conv;
 	gboolean (*send_attention)(PurpleConnection *, const char *, guint);
 	
 	gchar *description;
@@ -302,8 +303,8 @@
 	if (!send_attention(gc, who, type_code))
 		return;
 
-	/* TODO: icons, sound, shaking... same as serv_got_attention(). */
-	serv_got_im(gc, who, description, flags, mtime);
+	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who);
+	purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime);
 
 	g_free(description);
 }
--- a/pidgin/gtkblist.c	Tue Aug 28 12:34:33 2007 +0000
+++ b/pidgin/gtkblist.c	Tue Aug 28 21:03:49 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);
--- a/pidgin/gtkconv.c	Tue Aug 28 12:34:33 2007 +0000
+++ b/pidgin/gtkconv.c	Tue Aug 28 21:03:49 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, "<Separator>", NULL },
 
 
+	{ N_("/Conversation/_Hide"), NULL, menu_hide_conv_cb, 0,
+			"<StockItem>", NULL},
 	{ N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0,
 			"<StockItem>", 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)
 {
--- a/pidgin/gtkconv.h	Tue Aug 28 12:34:33 2007 +0000
+++ b/pidgin/gtkconv.h	Tue Aug 28 21:03:49 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);
--- a/pidgin/gtkdialogs.c	Tue Aug 28 12:34:33 2007 +0000
+++ b/pidgin/gtkdialogs.c	Tue Aug 28 21:03:49 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);
 }
 
--- a/pidgin/gtksound.c	Tue Aug 28 12:34:33 2007 +0000
+++ b/pidgin/gtksound.c	Tue Aug 28 21:03:49 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;