X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=src%2Fempathy-chat-manager.c;h=279d7d8ff43bcdf31716067920e6a55f93aec8bd;hp=36f9512c78dc8bf4655a5a28d9e35c58e7d6d71b;hb=21674ad377f646ef1e242f7e4260ee2398a220be;hpb=52e1e2fbdafd993933ba7866847ddbbedd7a2748 diff --git a/src/empathy-chat-manager.c b/src/empathy-chat-manager.c index 36f9512c..279d7d8f 100644 --- a/src/empathy-chat-manager.c +++ b/src/empathy-chat-manager.c @@ -17,21 +17,22 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include +#include "config.h" +#include "empathy-chat-manager.h" -#include -#include -#include +#include +#include +#include "empathy-chatroom-manager.h" #include "empathy-chat-window.h" +#include "empathy-request-util.h" +#include "empathy-ui-utils.h" +#include "extensions.h" #define DEBUG_FLAG EMPATHY_DEBUG_OTHER -#include +#include "empathy-debug.h" -#include "empathy-chat-manager.h" - -#include +#define CHAT_MANAGER_PATH "/org/gnome/Empathy/ChatManager" enum { CLOSED_CHATS_CHANGED, @@ -60,7 +61,17 @@ struct _EmpathyChatManagerPriv guint num_displayed_chat; + /* account path -> (GHashTable<(owned gchar *) contact ID + * -> (owned gchar *) non-NULL message>) + */ + GHashTable *messages; + TpBaseClient *handler; + + /* Cached to keep Folks in memory while empathy-chat is running; we don't + * want to reload it each time the last chat window is closed + * and re-opened. */ + EmpathyIndividualManager *individual_mgr; }; #define GET_PRIV(o) \ @@ -74,6 +85,7 @@ typedef struct TpAccount *account; gchar *id; gboolean room; + gboolean sms; } ChatData; static ChatData * @@ -86,6 +98,7 @@ chat_data_new (EmpathyChat *chat) data->account = g_object_ref (empathy_chat_get_account (chat)); data->id = g_strdup (empathy_chat_get_id (chat)); data->room = empathy_chat_is_room (chat); + data->sms = empathy_chat_is_sms_channel (chat); return data; } @@ -124,6 +137,36 @@ chat_destroyed_cb (gpointer data, priv->num_displayed_chat); } +static void +join_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpChannel *channel = TP_CHANNEL (source); + GError *error = NULL; + + if (!tp_channel_join_finish (channel, result, &error)) + { + DEBUG ("Failed to join chat (%s): %s", + tp_channel_get_identifier (channel), error->message); + g_error_free (error); + } +} + +static void +individual_mgr_cb (EmpathyChatWindow *window, + GParamSpec *spec, + EmpathyChatManager *self) +{ + EmpathyChatManagerPriv *priv = GET_PRIV (self); + + if (priv->individual_mgr != NULL) + return; + + priv->individual_mgr = empathy_chat_window_get_individual_manager (window); + g_object_ref (priv->individual_mgr); +} + static void process_tp_chat (EmpathyChatManager *self, EmpathyTpChat *tp_chat, @@ -133,12 +176,13 @@ process_tp_chat (EmpathyChatManager *self, EmpathyChatManagerPriv *priv = GET_PRIV (self); EmpathyChat *chat = NULL; const gchar *id; + EmpathyChatWindow *window; id = empathy_tp_chat_get_id (tp_chat); if (!tp_str_empty (id)) { chat = empathy_chat_window_find_chat (account, id, - empathy_tp_chat_is_sms_channel (tp_chat)); + tp_text_channel_is_sms_channel ((TpTextChannel *) tp_chat)); } if (chat != NULL) @@ -147,6 +191,8 @@ process_tp_chat (EmpathyChatManager *self, } else { + GHashTable *chats = NULL; + chat = empathy_chat_new (tp_chat); /* empathy_chat_new returns a floating reference as EmpathyChat is * a GtkWidget. This reference will be taken by a container @@ -160,71 +206,36 @@ process_tp_chat (EmpathyChatManager *self, g_signal_emit (self, signals[DISPLAYED_CHATS_CHANGED], 0, priv->num_displayed_chat); + /* Set the saved message in the channel if we have one. */ + chats = g_hash_table_lookup (priv->messages, + tp_proxy_get_object_path (account)); + + if (chats != NULL) + { + const gchar *msg = g_hash_table_lookup (chats, id); + + if (msg != NULL) + empathy_chat_set_text (chat, msg); + } + g_object_weak_ref ((GObject *) chat, chat_destroyed_cb, self); } - empathy_chat_window_present_chat (chat, user_action_time); + + window = empathy_chat_window_present_chat (chat, user_action_time); + + if (priv->individual_mgr == NULL) + { + /* We want to cache it as soon it's created */ + tp_g_signal_connect_object (window, "notify::individual-manager", + G_CALLBACK (individual_mgr_cb), self, 0); + } if (empathy_tp_chat_is_invited (tp_chat, NULL)) { /* We have been invited to the room. Add ourself as member as this * channel has been approved. */ - empathy_tp_chat_join (tp_chat); + tp_channel_join_async (TP_CHANNEL (tp_chat), "", join_cb, self); } - - g_object_unref (tp_chat); -} - -typedef struct -{ - EmpathyChatManager *self; - EmpathyTpChat *tp_chat; - TpAccount *account; - gint64 user_action_time; - gulong sig_id; -} chat_ready_ctx; - -static chat_ready_ctx * -chat_ready_ctx_new (EmpathyChatManager *self, - EmpathyTpChat *tp_chat, - TpAccount *account, - gint64 user_action_time) -{ - chat_ready_ctx *ctx = g_slice_new0 (chat_ready_ctx); - - ctx->self = g_object_ref (self); - ctx->tp_chat = g_object_ref (tp_chat); - ctx->account = g_object_ref (account); - ctx->user_action_time = user_action_time; - return ctx; -} - -static void -chat_ready_ctx_free (chat_ready_ctx *ctx) -{ - g_object_unref (ctx->self); - g_object_unref (ctx->tp_chat); - g_object_unref (ctx->account); - - if (ctx->sig_id != 0) - g_signal_handler_disconnect (ctx->tp_chat, ctx->sig_id); - - g_slice_free (chat_ready_ctx, ctx); -} - -static void -tp_chat_ready_cb (GObject *object, - GParamSpec *spec, - gpointer user_data) -{ - EmpathyTpChat *tp_chat = EMPATHY_TP_CHAT (object); - chat_ready_ctx *ctx = user_data; - - if (!empathy_tp_chat_is_ready (tp_chat)) - return; - - process_tp_chat (ctx->self, tp_chat, ctx->account, ctx->user_action_time); - - chat_ready_ctx_free (ctx); } static void @@ -242,35 +253,21 @@ handle_channels (TpSimpleHandler *handler, for (l = channels; l != NULL; l = g_list_next (l)) { - TpChannel *channel = l->data; - EmpathyTpChat *tp_chat; + EmpathyTpChat *tp_chat = l->data; - if (tp_proxy_get_invalidated (channel) != NULL) + if (tp_proxy_get_invalidated (tp_chat) != NULL) continue; - if (!TP_IS_TEXT_CHANNEL (channel)) + if (!EMPATHY_IS_TP_CHAT (tp_chat)) { DEBUG ("Channel %s doesn't implement Messages; can't handle it", - tp_proxy_get_object_path (channel)); + tp_proxy_get_object_path (tp_chat)); continue; } - DEBUG ("Now handling channel %s", tp_proxy_get_object_path (channel)); - - tp_chat = empathy_tp_chat_new (account, channel); - - if (empathy_tp_chat_is_ready (tp_chat)) - { - process_tp_chat (self, tp_chat, account, user_action_time); - } - else - { - chat_ready_ctx *ctx = chat_ready_ctx_new (self, tp_chat, account, - user_action_time); + DEBUG ("Now handling channel %s", tp_proxy_get_object_path (tp_chat)); - ctx->sig_id = g_signal_connect (tp_chat, "notify::ready", - G_CALLBACK (tp_chat_ready_cb), ctx); - } + process_tp_chat (self, tp_chat, account, user_action_time); } tp_handle_channels_context_accept (context); @@ -280,32 +277,22 @@ static void empathy_chat_manager_init (EmpathyChatManager *self) { EmpathyChatManagerPriv *priv = GET_PRIV (self); - TpDBusDaemon *dbus; + TpAccountManager *am; GError *error = NULL; priv->closed_queue = g_queue_new (); + priv->messages = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_hash_table_unref); - dbus = tp_dbus_daemon_dup (&error); - if (dbus == NULL) - { - g_critical ("Failed to get D-Bus daemon: %s", error->message); - g_error_free (error); - return; - } + am = tp_account_manager_dup (); priv->chatroom_mgr = empathy_chatroom_manager_dup_singleton (NULL); /* Text channels handler */ - priv->handler = tp_simple_handler_new (dbus, FALSE, FALSE, + priv->handler = tp_simple_handler_new_with_am (am, FALSE, FALSE, EMPATHY_CHAT_BUS_NAME_SUFFIX, FALSE, handle_channels, self, NULL); - /* EmpathyTpChat relies on these features being prepared */ - tp_base_client_add_connection_features_varargs (priv->handler, - TP_CONNECTION_FEATURE_CAPABILITIES, 0); - tp_base_client_add_channel_features_varargs (priv->handler, - TP_CHANNEL_FEATURE_CHAT_STATES, 0); - - g_object_unref (dbus); + g_object_unref (am); tp_base_client_take_handler_filter (priv->handler, tp_asv_new ( TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT, @@ -342,8 +329,11 @@ empathy_chat_manager_finalize (GObject *object) priv->closed_queue = NULL; } + tp_clear_pointer (&priv->messages, g_hash_table_unref); + tp_clear_object (&priv->handler); tp_clear_object (&priv->chatroom_mgr); + tp_clear_object (&priv->individual_mgr); G_OBJECT_CLASS (empathy_chat_manager_parent_class)->finalize (object); } @@ -381,7 +371,7 @@ empathy_chat_manager_constructed (GObject *obj) if (dbus_daemon != NULL) { tp_dbus_daemon_register_object (dbus_daemon, - "/org/gnome/Empathy/ChatManager", obj); + CHAT_MANAGER_PATH, obj); g_object_unref (dbus_daemon); } @@ -403,7 +393,7 @@ empathy_chat_manager_class_init ( G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__UINT, + g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_UINT, NULL); @@ -413,7 +403,7 @@ empathy_chat_manager_class_init ( G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__UINT, + g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_UINT, NULL); @@ -433,6 +423,8 @@ empathy_chat_manager_closed_chat (EmpathyChatManager *self, { EmpathyChatManagerPriv *priv = GET_PRIV (self); ChatData *data; + GHashTable *chats; + gchar *message; data = chat_data_new (chat); @@ -443,10 +435,48 @@ empathy_chat_manager_closed_chat (EmpathyChatManager *self, g_signal_emit (self, signals[CLOSED_CHATS_CHANGED], 0, g_queue_get_length (priv->closed_queue)); + + /* If there was a message saved from last time it was closed + * (perhaps by accident?) save it to our hash table so it can be + * used again when the same chat pops up. Hot. */ + message = empathy_chat_dup_text (chat); + + chats = g_hash_table_lookup (priv->messages, + tp_proxy_get_object_path (data->account)); + + /* Don't create a new hash table if we don't already have one and we + * don't actually have a message to save. */ + if (chats == NULL && tp_str_empty (message)) + { + g_free (message); + return; + } + else if (chats == NULL && !tp_str_empty (message)) + { + chats = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + g_hash_table_insert (priv->messages, + g_strdup (tp_proxy_get_object_path (data->account)), + chats); + } + + if (tp_str_empty (message)) + { + g_hash_table_remove (chats, data->id); + /* might be '\0' */ + g_free (message); + } + else + { + /* takes ownership of message */ + g_hash_table_insert (chats, g_strdup (data->id), message); + } } void -empathy_chat_manager_undo_closed_chat (EmpathyChatManager *self) +empathy_chat_manager_undo_closed_chat (EmpathyChatManager *self, + gint64 timestamp) { EmpathyChatManagerPriv *priv = GET_PRIV (self); ChatData *data; @@ -460,11 +490,13 @@ empathy_chat_manager_undo_closed_chat (EmpathyChatManager *self) data->room ? "room" : "contact", data->id); if (data->room) - empathy_join_muc (data->account, data->id, - TP_USER_ACTION_TIME_NOT_USER_ACTION); + empathy_join_muc (data->account, data->id, timestamp); + else if (data->sms) + empathy_sms_contact_id (data->account, data->id, timestamp, + NULL, NULL); else - empathy_chat_with_contact_id (data->account, data->id, - TP_USER_ACTION_TIME_NOT_USER_ACTION); + empathy_chat_with_contact_id (data->account, data->id, timestamp, + NULL, NULL); g_signal_emit (self, signals[CLOSED_CHATS_CHANGED], 0, g_queue_get_length (priv->closed_queue)); @@ -482,9 +514,11 @@ empathy_chat_manager_get_num_closed_chats (EmpathyChatManager *self) static void empathy_chat_manager_dbus_undo_closed_chat (EmpSvcChatManager *manager, + gint64 timestamp, DBusGMethodInvocation *context) { - empathy_chat_manager_undo_closed_chat ((EmpathyChatManager *) manager); + empathy_chat_manager_undo_closed_chat ((EmpathyChatManager *) manager, + timestamp); emp_svc_chat_manager_return_from_undo_closed_chat (context); } @@ -513,13 +547,15 @@ empathy_chat_manager_call_undo_closed_chat (void) proxy = g_object_new (TP_TYPE_PROXY, "dbus-daemon", dbus_daemon, "dbus-connection", tp_proxy_get_dbus_connection (TP_PROXY (dbus_daemon)), - "bus-name", "org.freedesktop.Telepathy.Client.Empathy.Chat", - "object-path", "/org/gnome/Empathy/ChatManager", + "bus-name", EMPATHY_CHAT_BUS_NAME, + "object-path", CHAT_MANAGER_PATH, NULL); tp_proxy_add_interface_by_id (proxy, EMP_IFACE_QUARK_CHAT_MANAGER); - emp_cli_chat_manager_call_undo_closed_chat (proxy, -1, NULL, NULL, NULL, NULL); + emp_cli_chat_manager_call_undo_closed_chat (proxy, -1, empathy_get_current_action_time (), + NULL, NULL, NULL, NULL); + g_object_unref (proxy); g_object_unref (dbus_daemon); }