/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2002-2007 Imendio AB
- * Copyright (C) 2007 Collabora Ltd.
+ * Copyright (C) 2007-2008 Collabora Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
#include <glib/gi18n.h>
#include <gtk/gtk.h>
+#include <libmissioncontrol/mission-control.h>
+
#include <libempathy/empathy-contact-manager.h>
#include <libempathy/empathy-log-manager.h>
#include <libempathy/empathy-debug.h>
#include <libempathy/empathy-utils.h>
-#include <libempathy/empathy-conf.h>
-#include <libempathy/empathy-marshal.h>
#include "empathy-chat.h"
#include "empathy-chat-window.h"
#include "empathy-geometry.h"
+#include "empathy-conf.h"
#include "empathy-preferences.h"
#include "empathy-spell.h"
#include "empathy-spell-dialog.h"
#include "empathy-ui-utils.h"
+#include "empathy-gtk-marshal.h"
#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CHAT, EmpathyChatPriv))
struct _EmpathyChatPriv {
EmpathyLogManager *log_manager;
EmpathyTpChat *tp_chat;
- EmpathyChatWindow *window;
+ EmpathyChatWindow *window;
+ McAccount *account;
+ MissionControl *mc;
guint composing_stop_timeout_id;
gboolean sensitive;
gchar *id;
guint scroll_idle_id;
gboolean first_tp_chat;
time_t last_log_timestamp;
+ gboolean is_first_char;
+ guint block_events_timeout_id;
/* Used to automatically shrink a window that has temporarily
* grown due to long input.
*/
GtkTextIter end;
} EmpathyChatSpell;
-static void empathy_chat_class_init (EmpathyChatClass *klass);
-static void empathy_chat_init (EmpathyChat *chat);
-static void chat_finalize (GObject *object);
-static void chat_destroy_cb (EmpathyTpChat *tp_chat,
- EmpathyChat *chat);
-static void chat_send (EmpathyChat *chat,
- const gchar *msg);
-static void chat_input_text_view_send (EmpathyChat *chat);
-static void chat_message_received_cb (EmpathyTpChat *tp_chat,
- EmpathyMessage *message,
- EmpathyChat *chat);
-static void chat_send_error_cb (EmpathyTpChat *tp_chat,
- EmpathyMessage *message,
- TelepathyChannelTextSendError error_code,
- EmpathyChat *chat);
-void chat_sent_message_add (EmpathyChat *chat,
- const gchar *str);
-const gchar * chat_sent_message_get_next (EmpathyChat *chat);
-const gchar * chat_sent_message_get_last (EmpathyChat *chat);
-static gboolean chat_input_key_press_event_cb (GtkWidget *widget,
- GdkEventKey *event,
- EmpathyChat *chat);
-static void chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer,
- EmpathyChat *chat);
-static gboolean chat_text_view_focus_in_event_cb (GtkWidget *widget,
- GdkEvent *event,
- EmpathyChat *chat);
-static void chat_text_view_scroll_hide_cb (GtkWidget *widget,
- EmpathyChat *chat);
-static void chat_text_view_size_allocate_cb (GtkWidget *widget,
- GtkAllocation *allocation,
- EmpathyChat *chat);
-static void chat_text_view_realize_cb (GtkWidget *widget,
- EmpathyChat *chat);
-static void chat_text_populate_popup_cb (GtkTextView *view,
- GtkMenu *menu,
- EmpathyChat *chat);
-static void chat_text_check_word_spelling_cb (GtkMenuItem *menuitem,
- EmpathyChatSpell *chat_spell);
-static EmpathyChatSpell *chat_spell_new (EmpathyChat *chat,
- const gchar *word,
- GtkTextIter start,
- GtkTextIter end);
-static void chat_spell_free (EmpathyChatSpell *chat_spell);
-static void chat_composing_start (EmpathyChat *chat);
-static void chat_composing_stop (EmpathyChat *chat);
-static void chat_composing_remove_timeout (EmpathyChat *chat);
-static gboolean chat_composing_stop_timeout_cb (EmpathyChat *chat);
-static void chat_state_changed_cb (EmpathyTpChat *tp_chat,
- EmpathyContact *contact,
- TelepathyChannelChatState state,
- EmpathyChat *chat);
-static void chat_add_logs (EmpathyChat *chat);
-static gboolean chat_scroll_down_idle_func (EmpathyChat *chat);
+static void empathy_chat_class_init (EmpathyChatClass *klass);
+static void empathy_chat_init (EmpathyChat *chat);
+static void chat_finalize (GObject *object);
+static void chat_destroy_cb (EmpathyTpChat *tp_chat,
+ EmpathyChat *chat);
+static void chat_send (EmpathyChat *chat,
+ const gchar *msg);
+static void chat_input_text_view_send (EmpathyChat *chat);
+static void chat_message_received_cb (EmpathyTpChat *tp_chat,
+ EmpathyMessage *message,
+ EmpathyChat *chat);
+static void chat_send_error_cb (EmpathyTpChat *tp_chat,
+ EmpathyMessage *message,
+ TpChannelTextSendError error_code,
+ EmpathyChat *chat);
+static void chat_sent_message_add (EmpathyChat *chat,
+ const gchar *str);
+static const gchar * chat_sent_message_get_next (EmpathyChat *chat);
+static const gchar * chat_sent_message_get_last (EmpathyChat *chat);
+static gboolean chat_input_key_press_event_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ EmpathyChat *chat);
+static void chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer,
+ EmpathyChat *chat);
+static gboolean chat_text_view_focus_in_event_cb (GtkWidget *widget,
+ GdkEvent *event,
+ EmpathyChat *chat);
+static void chat_text_view_scroll_hide_cb (GtkWidget *widget,
+ EmpathyChat *chat);
+static void chat_text_view_size_allocate_cb (GtkWidget *widget,
+ GtkAllocation *allocation,
+ EmpathyChat *chat);
+static void chat_text_view_realize_cb (GtkWidget *widget,
+ EmpathyChat *chat);
+static void chat_text_populate_popup_cb (GtkTextView *view,
+ GtkMenu *menu,
+ EmpathyChat *chat);
+static void chat_text_check_word_spelling_cb (GtkMenuItem *menuitem,
+ EmpathyChatSpell *chat_spell);
+static EmpathyChatSpell *chat_spell_new (EmpathyChat *chat,
+ const gchar *word,
+ GtkTextIter start,
+ GtkTextIter end);
+static void chat_spell_free (EmpathyChatSpell *chat_spell);
+static void chat_composing_start (EmpathyChat *chat);
+static void chat_composing_stop (EmpathyChat *chat);
+static void chat_composing_remove_timeout (EmpathyChat *chat);
+static gboolean chat_composing_stop_timeout_cb (EmpathyChat *chat);
+static void chat_state_changed_cb (EmpathyTpChat *tp_chat,
+ EmpathyContact *contact,
+ TpChannelChatState state,
+ EmpathyChat *chat);
+static void chat_add_logs (EmpathyChat *chat);
+static gboolean chat_scroll_down_idle_func (EmpathyChat *chat);
enum {
COMPOSING,
LAST_SIGNAL
};
+enum {
+ PROP_0,
+ PROP_TP_CHAT
+};
+
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (EmpathyChat, empathy_chat, G_TYPE_OBJECT);
+static void
+chat_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyChatPriv *priv = GET_PRIV (object);
+
+ switch (param_id) {
+ case PROP_TP_CHAT:
+ g_value_set_object (value, priv->tp_chat);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ };
+}
+
+static void
+chat_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyChat *chat = EMPATHY_CHAT (object);
+
+ switch (param_id) {
+ case PROP_TP_CHAT:
+ empathy_chat_set_tp_chat (chat,
+ EMPATHY_TP_CHAT (g_value_get_object (value)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ };
+}
+
+static void
+chat_status_changed_cb (MissionControl *mc,
+ TpConnectionStatus status,
+ McPresence presence,
+ TpConnectionStatusReason reason,
+ const gchar *unique_name,
+ EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+ McAccount *account;
+
+ account = mc_account_lookup (unique_name);
+
+ if (status == TP_CONNECTION_STATUS_CONNECTED && !priv->tp_chat &&
+ empathy_account_equal (account, priv->account)) {
+ TpHandleType handle_type;
+
+ empathy_debug (DEBUG_DOMAIN,
+ "Account reconnected, request a new Text channel");
+
+ if (empathy_chat_is_group_chat (chat)) {
+ handle_type = TP_HANDLE_TYPE_ROOM;
+ } else {
+ handle_type = TP_HANDLE_TYPE_CONTACT;
+ }
+
+ mission_control_request_channel_with_string_handle (mc,
+ priv->account,
+ TP_IFACE_CHANNEL_TYPE_TEXT,
+ priv->id,
+ handle_type,
+ NULL, NULL);
+ }
+
+ g_object_unref (account);
+}
+
static void
empathy_chat_class_init (EmpathyChatClass *klass)
{
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = chat_finalize;
+ object_class->get_property = chat_get_property;
+ object_class->set_property = chat_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_TP_CHAT,
+ g_param_spec_object ("tp-chat",
+ "Empathy tp chat",
+ "The tp chat object",
+ EMPATHY_TYPE_TP_CHAT,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE));
signals[COMPOSING] =
g_signal_new ("composing",
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
- empathy_marshal_VOID__OBJECT_BOOLEAN,
+ _empathy_gtk_marshal_VOID__OBJECT_BOOLEAN,
G_TYPE_NONE,
2, EMPATHY_TYPE_MESSAGE, G_TYPE_BOOLEAN);
static void
empathy_chat_init (EmpathyChat *chat)
{
- EmpathyChatPriv *priv;
+ EmpathyChatPriv *priv = GET_PRIV (chat);
GtkTextBuffer *buffer;
chat->view = empathy_chat_view_new ();
chat->input_text_view = gtk_text_view_new ();
- chat->is_first_char = TRUE;
+ priv->is_first_char = TRUE;
g_object_set (chat->input_text_view,
"pixels-above-lines", 2,
"wrap-mode", GTK_WRAP_WORD_CHAR,
NULL);
- priv = GET_PRIV (chat);
-
priv->log_manager = empathy_log_manager_new ();
priv->default_window_height = -1;
priv->vscroll_visible = FALSE;
priv->sent_messages = NULL;
priv->sent_messages_index = -1;
priv->first_tp_chat = TRUE;
+ priv->mc = empathy_mission_control_new ();
+
+ dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), "AccountStatusChanged",
+ G_CALLBACK (chat_status_changed_cb),
+ chat, NULL);
g_signal_connect (chat->input_text_view,
"key_press_event",
g_list_free (priv->compositors);
chat_composing_remove_timeout (chat);
- g_object_unref (chat->account);
g_object_unref (priv->log_manager);
+ dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), "AccountStatusChanged",
+ G_CALLBACK (chat_status_changed_cb),
+ chat);
+ g_object_unref (priv->mc);
+
+
if (priv->tp_chat) {
g_object_unref (priv->tp_chat);
}
+ if (priv->account) {
+ g_object_unref (priv->account);
+ }
+
if (priv->scroll_idle_id) {
g_source_remove (priv->scroll_idle_id);
}
+ if (priv->block_events_timeout_id) {
+ g_source_remove (priv->block_events_timeout_id);
+ }
+
g_free (priv->id);
G_OBJECT_CLASS (empathy_chat_parent_class)->finalize (object);
empathy_chat_view_append_event (chat->view, _("Disconnected"));
gtk_widget_set_sensitive (chat->input_text_view, FALSE);
+ if (priv->block_events_timeout_id != 0) {
+ g_source_remove (priv->block_events_timeout_id);
+ }
+
if (EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat) {
EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat (chat, NULL);
}
g_free (msg);
- chat->is_first_char = TRUE;
+ priv->is_first_char = TRUE;
}
static void
}
static void
-chat_send_error_cb (EmpathyTpChat *tp_chat,
- EmpathyMessage *message,
- TelepathyChannelTextSendError error_code,
- EmpathyChat *chat)
+chat_send_error_cb (EmpathyTpChat *tp_chat,
+ EmpathyMessage *message,
+ TpChannelTextSendError error_code,
+ EmpathyChat *chat)
{
const gchar *error;
gchar *str;
g_free (str);
}
-void
+static void
chat_sent_message_add (EmpathyChat *chat,
const gchar *str)
{
priv->sent_messages_index = -1;
}
-const gchar *
+static const gchar *
chat_sent_message_get_next (EmpathyChat *chat)
{
EmpathyChatPriv *priv;
return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index);
}
-const gchar *
+static const gchar *
chat_sent_message_get_last (EmpathyChat *chat)
{
EmpathyChatPriv *priv;
EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
&spell_checker);
- if (chat->is_first_char) {
+ if (priv->is_first_char) {
GtkRequisition req;
gint window_height;
GtkWidget *dialog;
priv->last_input_height = req.height;
priv->padding_height = window_height - req.height - allocation->height;
- chat->is_first_char = FALSE;
+ priv->is_first_char = FALSE;
}
gtk_text_buffer_get_start_iter (buffer, &start);
}
static void
-chat_state_changed_cb (EmpathyTpChat *tp_chat,
- EmpathyContact *contact,
- TelepathyChannelChatState state,
- EmpathyChat *chat)
+chat_state_changed_cb (EmpathyTpChat *tp_chat,
+ EmpathyContact *contact,
+ TpChannelChatState state,
+ EmpathyChat *chat)
{
EmpathyChatPriv *priv;
GList *l;
/* Add messages from last conversation */
messages = empathy_log_manager_get_last_messages (priv->log_manager,
- chat->account,
+ priv->account,
empathy_chat_get_id (chat),
empathy_chat_is_group_chat (chat));
num_messages = g_list_length (messages);
return (priv->tp_chat != NULL);
}
+static const gchar *
+chat_get_window_id_for_geometry (EmpathyChat *chat)
+{
+ gboolean separate_windows;
+
+ empathy_conf_get_bool (empathy_conf_get (),
+ EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS,
+ &separate_windows);
+
+ if (separate_windows) {
+ return empathy_chat_get_id (chat);
+ } else {
+ return "chat-window";
+ }
+}
+
void
empathy_chat_save_geometry (EmpathyChat *chat,
gint x,
gint w,
gint h)
{
- empathy_geometry_save (empathy_chat_get_id (chat), x, y, w, h);
+ empathy_geometry_save (chat_get_window_id_for_geometry (chat), x, y, w, h);
}
void
gint *w,
gint *h)
{
- empathy_geometry_load (empathy_chat_get_id (chat), x, y, w, h);
+ empathy_geometry_load (chat_get_window_id_for_geometry (chat), x, y, w, h);
+}
+
+static gboolean
+chat_block_events_timeout_cb (gpointer data)
+{
+ EmpathyChat *chat = EMPATHY_CHAT (data);
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ chat->block_events = FALSE;
+ priv->block_events_timeout_id = 0;
+
+ return FALSE;
}
void
EmpathyTpChat *tp_chat)
{
EmpathyChatPriv *priv;
- GList *messages, *l;
g_return_if_fail (EMPATHY_IS_CHAT (chat));
g_return_if_fail (EMPATHY_IS_TP_CHAT (tp_chat));
return;
}
+ /* Block events for some time to avoid having "has come online" or
+ * "joined" messages. */
+ chat->block_events = TRUE;
+ if (priv->block_events_timeout_id != 0) {
+ g_source_remove (priv->block_events_timeout_id);
+ }
+ priv->block_events_timeout_id =
+ g_timeout_add_seconds (1, chat_block_events_timeout_cb, chat);
+
if (priv->tp_chat) {
g_signal_handlers_disconnect_by_func (priv->tp_chat,
chat_message_received_cb,
chat);
g_object_unref (priv->tp_chat);
}
+ if (priv->account) {
+ g_object_unref (priv->account);
+ }
g_free (priv->id);
priv->tp_chat = g_object_ref (tp_chat);
priv->id = g_strdup (empathy_tp_chat_get_id (tp_chat));
+ priv->account = g_object_ref (empathy_tp_chat_get_account (tp_chat));
+ empathy_tp_chat_set_acknowledge (tp_chat, TRUE);
if (priv->first_tp_chat) {
chat_add_logs (chat);
G_CALLBACK (chat_destroy_cb),
chat);
- /* Get pending messages */
- empathy_tp_chat_set_acknowledge (tp_chat, TRUE);
- messages = empathy_tp_chat_get_pendings (tp_chat);
- for (l = messages; l; l = l->next) {
- chat_message_received_cb (tp_chat, l->data, chat);
- g_object_unref (l->data);
- }
- g_list_free (messages);
-
if (!priv->sensitive) {
gtk_widget_set_sensitive (chat->input_text_view, TRUE);
empathy_chat_view_append_event (chat->view, _("Connected"));
if (EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat) {
EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat (chat, tp_chat);
}
+
+ g_object_notify (G_OBJECT (chat), "tp-chat");
}
const gchar *
return priv->id;
}
+McAccount *
+empathy_chat_get_account (EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ return priv->account;
+}
+
void
empathy_chat_clear (EmpathyChat *chat)
{