/soc/2015/igor.gajowiak/chatlog: d78ceb0c9faf: Add incoming mess...
Igor Gajowiak <[email protected]>
| Newsgroups | gmane.comp.gnome.gaim.cvs |
|---|---|
| Message-ID | <[email protected]> |
Changeset: d78ceb0c9fafd2e84ac1256aaf5bd467272c1dec Author: Igor Gajowiak <[email protected]> Date: 2015-06-20 00:54 +0200 Branch: default URL: https://hg.pidgin.im/soc/2015/igor.gajowiak/chatlog/rev/d78ceb0c9faf Description: Add incoming message replacing. diffstat: libpurple/conversation.c | 35 ++++++ libpurple/conversation.h | 12 ++ libpurple/message.c | 5 + libpurple/protocol.c | 6 + libpurple/protocol.h | 9 + libpurple/protocols/jabber/jabber.c | 1 + libpurple/protocols/jabber/message.c | 89 ++++++++++++++++- libpurple/protocols/jabber/message.h | 1 + libpurple/server.c | 20 +++ libpurple/server.h | 8 + pidgin/gtkconv.c | 87 ++++++++++++++++- pidgin/gtkconv.h | 2 + pidgin/gtkwebview.c | 94 ++++++++++++++++++ pidgin/themes/Contents/Resources/Outgoing/Content.html | 6 - pidgin/themes/Contents/Resources/main.css | 5 + pidgin/themes/Template.html | 68 ++++++------ 16 files changed, 403 insertions(+), 45 deletions(-) diffs (truncated from 759 to 300 lines): diff --git a/libpurple/conversation.c b/libpurple/conversation.c --- a/libpurple/conversation.c +++ b/libpurple/conversation.c @@ -772,6 +772,7 @@ purple_conversation_replace_message(Purp PurpleConversationClass *klass = NULL; g_return_if_fail(PURPLE_IS_CONVERSATION(conv)); + g_return_if_fail(replaced_msg_id != PURPLE_MESSAGE_ID_NONE); g_return_if_fail(msg != NULL); klass = PURPLE_CONVERSATION_GET_CLASS(conv); @@ -803,6 +804,40 @@ purple_conversation_send_with_flags(Purp common_send(conv, message, flags); } +void +purple_conversation_send_replace_message_with_flags( + PurpleConversation *conv, guint replaced_msg_id, + const char *new_message, PurpleMessageFlags flags) +{ + g_return_if_fail(PURPLE_IS_CONVERSATION(conv)); + g_return_if_fail(replaced_msg_id != PURPLE_MESSAGE_ID_NONE); + g_return_if_fail(new_message != NULL); + + PurpleAccount *account; + PurpleConnection *gc; + PurpleMessage *msg; + + account = purple_conversation_get_account(conv); + g_return_if_fail(PURPLE_IS_ACCOUNT(account)); + + gc = purple_account_get_connection(account); + g_return_if_fail(PURPLE_IS_CONNECTION(gc)); + + flags |= PURPLE_MESSAGE_SEND; + + if (PURPLE_IS_IM_CONVERSATION(conv)) { + msg = purple_message_new_outgoing( + purple_conversation_get_name(conv), new_message, flags); + + // Replace message in conversation if sending succeeded + if (purple_serv_send_replace_im(gc, replaced_msg_id, msg)) + purple_conversation_replace_message(conv, replaced_msg_id, msg); + } + else if (PURPLE_IS_CHAT_CONVERSATION(conv)) { + // TODO: Do we need to implement this? + } +} + gboolean purple_conversation_has_focus(PurpleConversation *conv) { diff --git a/libpurple/conversation.h b/libpurple/conversation.h --- a/libpurple/conversation.h +++ b/libpurple/conversation.h @@ -525,6 +525,18 @@ void purple_conversation_send_with_flags PurpleMessageFlags flags); /** + * purple_conversation_send_replace_message_with_flags: + * @conv: The conversation. + * @replaced_msg_id: The id of the PurpleMessage to replace. + * @new_message: The new message data. + * @flags: The PurpleMessageFlags flags to use in addition to + * PURPLE_MESSAGE_SEND. + */ +void purple_conversation_send_replace_message_with_flags( + PurpleConversation *conv, guint replaced_msg_id, + const char *new_message, PurpleMessageFlags flags); + +/** * purple_conversation_set_features: * @conv: The conversation * @features: Bitset defining supported features diff --git a/libpurple/message.c b/libpurple/message.c --- a/libpurple/message.c +++ b/libpurple/message.c @@ -251,6 +251,9 @@ purple_message_init(GTypeInstance *insta PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(msg); PURPLE_DBUS_REGISTER_POINTER(msg, PurpleMessage); + purple_signal_register(msg, "purple-message-edit", + purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, PURPLE_TYPE_MESSAGE); + priv->id = generate_next_id(); g_hash_table_insert(messages, GINT_TO_POINTER(priv->id), msg); } @@ -261,6 +264,8 @@ purple_message_finalize(GObject *obj) PurpleMessage *message = PURPLE_MESSAGE(obj); PurpleMessagePrivate *priv = PURPLE_MESSAGE_GET_PRIVATE(message); + purple_signal_unregister(message, "purple-message-edit"); + g_free(priv->author); g_free(priv->author_alias); g_free(priv->recipient); diff --git a/libpurple/protocol.c b/libpurple/protocol.c --- a/libpurple/protocol.c +++ b/libpurple/protocol.c @@ -611,6 +611,12 @@ purple_protocol_im_iface_send(PurpleProt DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, send, gc, msg); } +int purple_protocol_im_iface_replace(PurpleProtocol *protocol, PurpleConnection *gc, + guint replaced_msg_id, PurpleMessage *msg) +{ + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, 0, replace, gc, replaced_msg_id, msg); +} + unsigned int purple_protocol_im_iface_send_typing(PurpleProtocol *protocol, PurpleConnection *gc, const char *name, PurpleIMTypingState state) diff --git a/libpurple/protocol.h b/libpurple/protocol.h --- a/libpurple/protocol.h +++ b/libpurple/protocol.h @@ -394,6 +394,10 @@ typedef struct _PurpleProtocolIMIface Pu * negative value. You can use one of the valid #errno values, or * just big something. If the message should not be echoed to the * conversation window, return 0. + * @replace This function should behave exactly the same as @send except + * the fact that it should replace a message instead of sending a new one. + * It is a protocol's responsibility to map PurpleMessage ids to internal + * protocol's message ids and vice versa. * @send_typing: If this protocol requires the #PURPLE_IM_TYPING message to be * sent repeatedly to signify that the user is still typing, then * the protocol should return the number of seconds to wait before @@ -412,6 +416,8 @@ struct _PurpleProtocolIMIface /*< public >*/ int (*send)(PurpleConnection *, PurpleMessage *msg); + int (*replace)(PurpleConnection *, guint replaced_msg_id, PurpleMessage *msg); + unsigned int (*send_typing)(PurpleConnection *, const char *name, PurpleIMTypingState state); }; @@ -1013,6 +1019,9 @@ GType purple_protocol_im_iface_get_type( int purple_protocol_im_iface_send(PurpleProtocol *, PurpleConnection *, PurpleMessage *msg); +int purple_protocol_im_iface_replace(PurpleProtocol *, PurpleConnection *, + guint replaced_msg_id, PurpleMessage *msg); + unsigned int purple_protocol_im_iface_send_typing(PurpleProtocol *, PurpleConnection *, const char *name, PurpleIMTypingState state); diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -4208,6 +4208,7 @@ static void jabber_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) { im_iface->send = jabber_message_send_im; + im_iface->replace = jabber_message_replace_im; im_iface->send_typing = jabber_send_typing; } diff --git a/libpurple/protocols/jabber/message.c b/libpurple/protocols/jabber/message.c --- a/libpurple/protocols/jabber/message.c +++ b/libpurple/protocols/jabber/message.c @@ -841,9 +841,8 @@ void jabber_message_parse(JabberStream * const char *id = purple_xmlnode_get_attrib(child, "id"); // Some communicators (OneTeam for example) - // do not send proper xmlns here, so I skip the check for now - // if(id && !strcmp(xmlns, NS_MESSAGE_REPLACE)) - if(id) + // do not send proper xmlns here + if(id && !strcmp(xmlns, NS_MESSAGE_REPLACE)) jm->replaced_msg_id = g_strdup(id); } } @@ -1109,6 +1108,12 @@ void jabber_message_send(JabberMessage * purple_xmlnode_insert_data(child, jm->body, -1); } + if (jm->replaced_msg_id) { + child = purple_xmlnode_new_child(message, "replace"); + purple_xmlnode_set_attrib(child, "id", jm->replaced_msg_id); + purple_xmlnode_set_namespace(child, NS_MESSAGE_REPLACE); + } + if(jm->xhtml) { if ((child = purple_xmlnode_from_str(jm->xhtml, -1))) { purple_xmlnode_insert_child(message, child); @@ -1188,6 +1193,84 @@ int jabber_message_send_im(PurpleConnect jm->to = g_strdup(rcpt); jm->id = jabber_get_next_id(jm->js); + g_hash_table_insert(prpl_to_jbr, purple_message_get_id(msg), g_strdup(jm->id)); + + if(jbr) { + if(jbr->thread_id) + jm->thread_id = jbr->thread_id; + + if (jbr->chat_states == JABBER_CHAT_STATES_UNSUPPORTED) + jm->chat_state = JM_STATE_NONE; + else { + /* if(JABBER_CHAT_STATES_UNKNOWN == jbr->chat_states) + jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED; */ + } + } + + tmp = purple_utf8_strip_unprintables(purple_message_get_contents(msg)); + purple_markup_html_to_xhtml(tmp, &xhtml, &jm->body); + g_free(tmp); + + tmp = jabber_message_smileyfy_xhtml(jm, xhtml); + if (tmp) { + g_free(xhtml); + xhtml = tmp; + } + + /* + * For backward compatibility with user expectations or for those not on + * the user's roster, allow sending XHTML-IM markup. + */ + if (!jbr || !jbr->caps.info || + jabber_resource_has_capability(jbr, NS_XHTML_IM)) { + if (!jabber_xhtml_plain_equal(xhtml, jm->body)) + /* Wrap the message in <p/> for great interoperability justice. */ + jm->xhtml = g_strdup_printf("<html xmlns='" NS_XHTML_IM "'><body xmlns='" NS_XHTML "'><p>%s</p></body></html>", xhtml); + } + + g_free(xhtml); + + jabber_message_send(jm); + jabber_message_free(jm); + return 1; +} + +// TODO: copied from jabber_message_send_im, try to refactor +int jabber_message_replace_im(PurpleConnection *gc, guint replaced_msg_id, PurpleMessage *msg) +{ + JabberMessage *jm; + JabberBuddy *jb; + JabberBuddyResource *jbr; + char *xhtml; + char *tmp; + char *resource; + const gchar *rcpt = purple_message_get_recipient(msg); + char *msg_id; + + if (!rcpt || purple_message_is_empty(msg)) + return 0; + + msg_id = (char*) g_hash_table_lookup(prpl_to_jbr, GUINT_TO_POINTER(replaced_msg_id)); + if (msg_id == NULL) + return 0; + + resource = jabber_get_resource(rcpt); + + jb = jabber_buddy_find(purple_connection_get_protocol_data(gc), rcpt, TRUE); + jbr = jabber_buddy_find_resource(jb, resource); + + g_free(resource); + + jm = g_new0(JabberMessage, 1); + jm->js = purple_connection_get_protocol_data(gc); + jm->type = JABBER_MESSAGE_CHAT; + jm->chat_state = JM_STATE_ACTIVE; + jm->to = g_strdup(rcpt); + jm->id = jabber_get_next_id(jm->js); + jm->replaced_msg_id = g_strdup(msg_id); + + g_hash_table_insert(prpl_to_jbr, purple_message_get_id(msg), g_strdup(jm->id)); + if(jbr) { if(jbr->thread_id) jm->thread_id = jbr->thread_id; diff --git a/libpurple/protocols/jabber/message.h b/libpurple/protocols/jabber/message.h --- a/libpurple/protocols/jabber/message.h +++ b/libpurple/protocols/jabber/message.h @@ -71,6 +71,7 @@ void jabber_message_send(JabberMessage * void jabber_message_parse(JabberStream *js, PurpleXmlNode *packet); int jabber_message_send_im(PurpleConnection *gc, PurpleMessage *msg); +int jabber_message_replace_im(PurpleConnection *gc, guint replaced_msg_id, PurpleMessage *msg); int jabber_message_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg); unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state); diff --git a/libpurple/server.c b/libpurple/server.c --- a/libpurple/server.c +++ b/libpurple/server.c @@ -159,6 +159,26 @@ int purple_serv_send_im(PurpleConnection return val; } +int purple_serv_send_replace_im(PurpleConnection *gc, guint replaced_msg_id, PurpleMessage *msg) +{ + g_return_val_if_fail(gc != NULL, -EINVAL); + g_return_val_if_fail(replaced_msg_id != PURPLE_MESSAGE_ID_NONE, -EINVAL); + g_return_val_if_fail(msg != NULL, -EINVAL); + + PurpleProtocol *protocol = NULL; + protocol = purple_connection_get_protocol(gc); + + g_return_val_if_fail(protocol != NULL, -EINVAL); + g_return_val_if_fail(PURPLE_PROTOCOL_HAS_IM_IFACE(protocol), -EINVAL); + + int res = -EINVAL; _______________________________________________ Commits mailing list [email protected] https://pidgin.im/cgi-bin/mailman/listinfo/commits