From 417a0425a90041d078047c446e9a9a78d6d9576b Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Wed, 2 Apr 2008 09:41:36 +0000 Subject: Remove vtable and add info as properties. Reorder empathy-chat.c to not declare func on top. svn path=/trunk/; revision=835 --- libempathy-gtk/empathy-chat.c | 1174 +++++++++++++++++++---------------------- libempathy-gtk/empathy-chat.h | 14 +- 2 files changed, 554 insertions(+), 634 deletions(-) (limited to 'libempathy-gtk') diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c index 0d632faf7..9c6b61d27 100644 --- a/libempathy-gtk/empathy-chat.c +++ b/libempathy-gtk/empathy-chat.c @@ -81,6 +81,10 @@ struct _EmpathyChatPriv { GList *backlog_messages; gboolean is_first_char; guint block_events_timeout_id; + TpHandleType handle_type; + gchar *name; + gchar *tooltip; + const gchar *icon_name; /* Used to automatically shrink a window that has temporarily * grown due to long input. */ @@ -90,80 +94,22 @@ struct _EmpathyChatPriv { gboolean vscroll_visible; }; -typedef struct { - EmpathyChat *chat; - gchar *word; - - GtkTextIter start; - 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, - 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); +static void empathy_chat_class_init (EmpathyChatClass *klass); +static void empathy_chat_init (EmpathyChat *chat); enum { COMPOSING, NEW_MESSAGE, - NAME_CHANGED, - STATUS_CHANGED, LAST_SIGNAL }; enum { PROP_0, - PROP_TP_CHAT + PROP_TP_CHAT, + PROP_NAME, + PROP_TOOLTIP, + PROP_ICON_NAME, + PROP_WIDGET }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -182,6 +128,18 @@ chat_get_property (GObject *object, case PROP_TP_CHAT: g_value_set_object (value, priv->tp_chat); break; + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_TOOLTIP: + g_value_set_string (value, priv->tooltip); + break; + case PROP_ICON_NAME: + g_value_set_string (value, priv->icon_name); + break; + case PROP_WIDGET: + g_value_set_object (value, priv->widget); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -221,23 +179,15 @@ chat_status_changed_cb (MissionControl *mc, 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_account_equal (account, priv->account) && + priv->handle_type != 0) { 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, + priv->handle_type, NULL, NULL); } @@ -245,136 +195,63 @@ chat_status_changed_cb (MissionControl *mc, } static void -empathy_chat_class_init (EmpathyChatClass *klass) +chat_composing_remove_timeout (EmpathyChat *chat) { - GObjectClass *object_class; - - 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; + EmpathyChatPriv *priv; - 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)); + priv = GET_PRIV (chat); - signals[COMPOSING] = - g_signal_new ("composing", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__BOOLEAN, - G_TYPE_NONE, - 1, G_TYPE_BOOLEAN); + if (priv->composing_stop_timeout_id) { + g_source_remove (priv->composing_stop_timeout_id); + priv->composing_stop_timeout_id = 0; + } +} - signals[NEW_MESSAGE] = - g_signal_new ("new-message", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - _empathy_gtk_marshal_VOID__OBJECT_BOOLEAN, - G_TYPE_NONE, - 2, EMPATHY_TYPE_MESSAGE, G_TYPE_BOOLEAN); +static gboolean +chat_composing_stop_timeout_cb (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; - signals[NAME_CHANGED] = - g_signal_new ("name-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, G_TYPE_POINTER); + priv = GET_PRIV (chat); - signals[STATUS_CHANGED] = - g_signal_new ("status-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); + priv->composing_stop_timeout_id = 0; + empathy_tp_chat_set_state (priv->tp_chat, + TP_CHANNEL_CHAT_STATE_PAUSED); - g_type_class_add_private (object_class, sizeof (EmpathyChatPriv)); + return FALSE; } static void -empathy_chat_init (EmpathyChat *chat) +chat_composing_start (EmpathyChat *chat) { - EmpathyChatPriv *priv = GET_PRIV (chat); - GtkTextBuffer *buffer; - - chat->view = empathy_chat_view_new (); - chat->input_text_view = gtk_text_view_new (); - - priv->is_first_char = TRUE; - - g_object_set (chat->input_text_view, - "pixels-above-lines", 2, - "pixels-below-lines", 2, - "pixels-inside-wrap", 1, - "right-margin", 2, - "left-margin", 2, - "wrap-mode", GTK_WRAP_WORD_CHAR, - NULL); - - priv->log_manager = empathy_log_manager_new (); - priv->default_window_height = -1; - priv->vscroll_visible = FALSE; - priv->sensitive = TRUE; - 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); + EmpathyChatPriv *priv; - g_signal_connect (chat->input_text_view, - "key_press_event", - G_CALLBACK (chat_input_key_press_event_cb), - chat); + priv = GET_PRIV (chat); - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); - g_signal_connect (buffer, - "changed", - G_CALLBACK (chat_input_text_buffer_changed_cb), - chat); - g_signal_connect (chat->view, - "focus_in_event", - G_CALLBACK (chat_text_view_focus_in_event_cb), - chat); + if (priv->composing_stop_timeout_id) { + /* Just restart the timeout */ + chat_composing_remove_timeout (chat); + } else { + empathy_tp_chat_set_state (priv->tp_chat, + TP_CHANNEL_CHAT_STATE_COMPOSING); + } - g_signal_connect (chat->input_text_view, - "size_allocate", - G_CALLBACK (chat_text_view_size_allocate_cb), - chat); + priv->composing_stop_timeout_id = g_timeout_add_seconds ( + COMPOSING_STOP_TIMEOUT, + (GSourceFunc) chat_composing_stop_timeout_cb, + chat); +} - g_signal_connect (chat->input_text_view, - "realize", - G_CALLBACK (chat_text_view_realize_cb), - chat); +static void +chat_composing_stop (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; - g_signal_connect (GTK_TEXT_VIEW (chat->input_text_view), - "populate_popup", - G_CALLBACK (chat_text_populate_popup_cb), - chat); + priv = GET_PRIV (chat); - /* create misspelt words identification tag */ - gtk_text_buffer_create_tag (buffer, - "misspelled", - "underline", PANGO_UNDERLINE_ERROR, - NULL); + chat_composing_remove_timeout (chat); + empathy_tp_chat_set_state (priv->tp_chat, + TP_CHANNEL_CHAT_STATE_ACTIVE); } static void @@ -420,6 +297,8 @@ chat_finalize (GObject *object) } g_free (priv->id); + g_free (priv->name); + g_free (priv->tooltip); G_OBJECT_CLASS (empathy_chat_parent_class)->finalize (object); } @@ -444,10 +323,97 @@ chat_destroy_cb (EmpathyTpChat *tp_chat, if (priv->block_events_timeout_id != 0) { g_source_remove (priv->block_events_timeout_id); } +} + +static void +chat_sent_message_add (EmpathyChat *chat, + const gchar *str) +{ + EmpathyChatPriv *priv; + GSList *list; + GSList *item; + + priv = GET_PRIV (chat); + + /* Save the sent message in our repeat buffer */ + list = priv->sent_messages; + + /* Remove any other occurances of this msg */ + while ((item = g_slist_find_custom (list, str, (GCompareFunc) strcmp)) != NULL) { + list = g_slist_remove_link (list, item); + g_free (item->data); + g_slist_free1 (item); + } + + /* Trim the list to the last 10 items */ + while (g_slist_length (list) > 10) { + item = g_slist_last (list); + if (item) { + list = g_slist_remove_link (list, item); + g_free (item->data); + g_slist_free1 (item); + } + } + + /* Add new message */ + list = g_slist_prepend (list, g_strdup (str)); + + /* Set list and reset the index */ + priv->sent_messages = list; + priv->sent_messages_index = -1; +} + +static const gchar * +chat_sent_message_get_next (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + gint max; + + priv = GET_PRIV (chat); + + if (!priv->sent_messages) { + empathy_debug (DEBUG_DOMAIN, + "No sent messages, next message is NULL"); + return NULL; + } + + max = g_slist_length (priv->sent_messages) - 1; - if (EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat) { - EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat (chat, NULL); + if (priv->sent_messages_index < max) { + priv->sent_messages_index++; } + + empathy_debug (DEBUG_DOMAIN, + "Returning next message index:%d", + priv->sent_messages_index); + + return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index); +} + +static const gchar * +chat_sent_message_get_last (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); + + priv = GET_PRIV (chat); + + if (!priv->sent_messages) { + empathy_debug (DEBUG_DOMAIN, + "No sent messages, last message is NULL"); + return NULL; + } + + if (priv->sent_messages_index >= 0) { + priv->sent_messages_index--; + } + + empathy_debug (DEBUG_DOMAIN, + "Returning last message index:%d", + priv->sent_messages_index); + + return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index); } static void @@ -470,9 +436,6 @@ chat_send (EmpathyChat *chat, return; } - /* FIXME: add here something to let group/privrate chat handle - * some special messages */ - message = empathy_message_new (msg); empathy_tp_chat_send (priv->tp_chat, message); @@ -505,6 +468,67 @@ chat_input_text_view_send (EmpathyChat *chat) priv->is_first_char = TRUE; } +static void +chat_state_changed_cb (EmpathyTpChat *tp_chat, + EmpathyContact *contact, + TpChannelChatState state, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + GList *l; + gboolean was_composing; + + priv = GET_PRIV (chat); + + if (empathy_contact_is_user (contact)) { + /* We don't care about our own chat state */ + return; + } + + was_composing = (priv->compositors != NULL); + + /* Find the contact in the list. After that l is the list elem or NULL */ + for (l = priv->compositors; l; l = l->next) { + if (contact == l->data) { + break; + } + } + + switch (state) { + case TP_CHANNEL_CHAT_STATE_GONE: + case TP_CHANNEL_CHAT_STATE_INACTIVE: + case TP_CHANNEL_CHAT_STATE_PAUSED: + case TP_CHANNEL_CHAT_STATE_ACTIVE: + /* Contact is not composing */ + if (l) { + priv->compositors = g_list_remove_link (priv->compositors, l); + g_object_unref (l->data); + g_list_free1 (l); + } + break; + case TP_CHANNEL_CHAT_STATE_COMPOSING: + /* Contact is composing */ + if (!l) { + priv->compositors = g_list_prepend (priv->compositors, + g_object_ref (contact)); + } + break; + default: + g_assert_not_reached (); + } + + empathy_debug (DEBUG_DOMAIN, "Was composing: %s now composing: %s", + was_composing ? "yes" : "no", + priv->compositors ? "yes" : "no"); + + if ((was_composing && !priv->compositors) || + (!was_composing && priv->compositors)) { + /* Composing state changed */ + g_signal_emit (chat, signals[COMPOSING], 0, + priv->compositors != NULL); + } +} + static void chat_message_received_cb (EmpathyTpChat *tp_chat, EmpathyMessage *message, @@ -550,7 +574,7 @@ chat_message_received_cb (EmpathyTpChat *tp_chat, empathy_log_manager_add_message (priv->log_manager, empathy_chat_get_id (chat), - empathy_chat_is_group_chat (chat), + FALSE, message); empathy_chat_view_append_message (chat->view, message); @@ -586,212 +610,22 @@ chat_send_error_cb (EmpathyTpChat *tp_chat, case TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED: error = _("permission denied"); break; - case TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG: - error = _("too long message"); - break; - case TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED: - error = _("not implemented"); - break; - default: - error = _("unknown"); - break; - } - - str = g_strdup_printf (_("Error sending message '%s': %s"), - empathy_message_get_body (message), - error); - empathy_chat_view_append_event (chat->view, str); - g_free (str); -} - -static void -chat_sent_message_add (EmpathyChat *chat, - const gchar *str) -{ - EmpathyChatPriv *priv; - GSList *list; - GSList *item; - - priv = GET_PRIV (chat); - - /* Save the sent message in our repeat buffer */ - list = priv->sent_messages; - - /* Remove any other occurances of this msg */ - while ((item = g_slist_find_custom (list, str, (GCompareFunc) strcmp)) != NULL) { - list = g_slist_remove_link (list, item); - g_free (item->data); - g_slist_free1 (item); - } - - /* Trim the list to the last 10 items */ - while (g_slist_length (list) > 10) { - item = g_slist_last (list); - if (item) { - list = g_slist_remove_link (list, item); - g_free (item->data); - g_slist_free1 (item); - } - } - - /* Add new message */ - list = g_slist_prepend (list, g_strdup (str)); - - /* Set list and reset the index */ - priv->sent_messages = list; - priv->sent_messages_index = -1; -} - -static const gchar * -chat_sent_message_get_next (EmpathyChat *chat) -{ - EmpathyChatPriv *priv; - gint max; - - priv = GET_PRIV (chat); - - if (!priv->sent_messages) { - empathy_debug (DEBUG_DOMAIN, - "No sent messages, next message is NULL"); - return NULL; - } - - max = g_slist_length (priv->sent_messages) - 1; - - if (priv->sent_messages_index < max) { - priv->sent_messages_index++; - } - - empathy_debug (DEBUG_DOMAIN, - "Returning next message index:%d", - priv->sent_messages_index); - - return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index); -} - -static const gchar * -chat_sent_message_get_last (EmpathyChat *chat) -{ - EmpathyChatPriv *priv; - - g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); - - priv = GET_PRIV (chat); - - if (!priv->sent_messages) { - empathy_debug (DEBUG_DOMAIN, - "No sent messages, last message is NULL"); - return NULL; - } - - if (priv->sent_messages_index >= 0) { - priv->sent_messages_index--; - } - - empathy_debug (DEBUG_DOMAIN, - "Returning last message index:%d", - priv->sent_messages_index); - - return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index); -} - -static gboolean -chat_input_key_press_event_cb (GtkWidget *widget, - GdkEventKey *event, - EmpathyChat *chat) -{ - EmpathyChatPriv *priv; - GtkAdjustment *adj; - gdouble val; - GtkWidget *text_view_sw; - - priv = GET_PRIV (chat); - - /* Catch ctrl+up/down so we can traverse messages we sent */ - if ((event->state & GDK_CONTROL_MASK) && - (event->keyval == GDK_Up || - event->keyval == GDK_Down)) { - GtkTextBuffer *buffer; - const gchar *str; - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); - - if (event->keyval == GDK_Up) { - str = chat_sent_message_get_next (chat); - } else { - str = chat_sent_message_get_last (chat); - } - - g_signal_handlers_block_by_func (buffer, - chat_input_text_buffer_changed_cb, - chat); - gtk_text_buffer_set_text (buffer, str ? str : "", -1); - g_signal_handlers_unblock_by_func (buffer, - chat_input_text_buffer_changed_cb, - chat); - - return TRUE; - } - - /* Catch enter but not ctrl/shift-enter */ - if (IS_ENTER (event->keyval) && - !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) { - GtkTextView *view; - - /* This is to make sure that kinput2 gets the enter. And if - * it's handled there we shouldn't send on it. This is because - * kinput2 uses Enter to commit letters. See: - * http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=104299 - */ - - view = GTK_TEXT_VIEW (chat->input_text_view); - if (gtk_im_context_filter_keypress (view->im_context, event)) { - GTK_TEXT_VIEW (chat->input_text_view)->need_im_reset = TRUE; - return TRUE; - } - - chat_input_text_view_send (chat); - return TRUE; - } - - text_view_sw = gtk_widget_get_parent (GTK_WIDGET (chat->view)); - - if (IS_ENTER (event->keyval) && - (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) { - /* Newline for shift/control-enter. */ - return FALSE; - } - else if (!(event->state & GDK_CONTROL_MASK) && - event->keyval == GDK_Page_Up) { - adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw)); - gtk_adjustment_set_value (adj, adj->value - adj->page_size); - - return TRUE; - } - else if ((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK && - event->keyval == GDK_Page_Down) { - adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw)); - val = MIN (adj->value + adj->page_size, adj->upper - adj->page_size); - gtk_adjustment_set_value (adj, val); - - return TRUE; - } - - if (EMPATHY_CHAT_GET_CLASS (chat)->key_press_event) { - return EMPATHY_CHAT_GET_CLASS (chat)->key_press_event (chat, event); + case TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG: + error = _("too long message"); + break; + case TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED: + error = _("not implemented"); + break; + default: + error = _("unknown"); + break; } - return FALSE; -} - -static gboolean -chat_text_view_focus_in_event_cb (GtkWidget *widget, - GdkEvent *event, - EmpathyChat *chat) -{ - gtk_widget_grab_focus (chat->input_text_view); - - return TRUE; + str = g_strdup_printf (_("Error sending message '%s': %s"), + empathy_message_get_body (message), + error); + empathy_chat_view_append_event (chat->view, str); + g_free (str); } static void @@ -894,6 +728,101 @@ chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer, } } +static gboolean +chat_input_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + GtkAdjustment *adj; + gdouble val; + GtkWidget *text_view_sw; + + priv = GET_PRIV (chat); + + /* Catch ctrl+up/down so we can traverse messages we sent */ + if ((event->state & GDK_CONTROL_MASK) && + (event->keyval == GDK_Up || + event->keyval == GDK_Down)) { + GtkTextBuffer *buffer; + const gchar *str; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + + if (event->keyval == GDK_Up) { + str = chat_sent_message_get_next (chat); + } else { + str = chat_sent_message_get_last (chat); + } + + g_signal_handlers_block_by_func (buffer, + chat_input_text_buffer_changed_cb, + chat); + gtk_text_buffer_set_text (buffer, str ? str : "", -1); + g_signal_handlers_unblock_by_func (buffer, + chat_input_text_buffer_changed_cb, + chat); + + return TRUE; + } + + /* Catch enter but not ctrl/shift-enter */ + if (IS_ENTER (event->keyval) && + !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) { + GtkTextView *view; + + /* This is to make sure that kinput2 gets the enter. And if + * it's handled there we shouldn't send on it. This is because + * kinput2 uses Enter to commit letters. See: + * http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=104299 + */ + + view = GTK_TEXT_VIEW (chat->input_text_view); + if (gtk_im_context_filter_keypress (view->im_context, event)) { + GTK_TEXT_VIEW (chat->input_text_view)->need_im_reset = TRUE; + return TRUE; + } + + chat_input_text_view_send (chat); + return TRUE; + } + + text_view_sw = gtk_widget_get_parent (GTK_WIDGET (chat->view)); + + if (IS_ENTER (event->keyval) && + (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) { + /* Newline for shift/control-enter. */ + return FALSE; + } + else if (!(event->state & GDK_CONTROL_MASK) && + event->keyval == GDK_Page_Up) { + adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw)); + gtk_adjustment_set_value (adj, adj->value - adj->page_size); + + return TRUE; + } + else if ((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK && + event->keyval == GDK_Page_Down) { + adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw)); + val = MIN (adj->value + adj->page_size, adj->upper - adj->page_size); + gtk_adjustment_set_value (adj, val); + + return TRUE; + } + + return FALSE; +} + +static gboolean +chat_text_view_focus_in_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyChat *chat) +{ + gtk_widget_grab_focus (chat->input_text_view); + + return TRUE; +} + typedef struct { GtkWindow *window; gint width; @@ -1029,6 +958,50 @@ chat_insert_smiley_activate_cb (GtkWidget *menuitem, gtk_text_buffer_insert (buffer, &iter, " ", -1); } +typedef struct { + EmpathyChat *chat; + gchar *word; + + GtkTextIter start; + GtkTextIter end; +} EmpathyChatSpell; + +static EmpathyChatSpell * +chat_spell_new (EmpathyChat *chat, + const gchar *word, + GtkTextIter start, + GtkTextIter end) +{ + EmpathyChatSpell *chat_spell; + + chat_spell = g_slice_new0 (EmpathyChatSpell); + + chat_spell->chat = g_object_ref (chat); + chat_spell->word = g_strdup (word); + chat_spell->start = start; + chat_spell->end = end; + + return chat_spell; +} + +static void +chat_spell_free (EmpathyChatSpell *chat_spell) +{ + g_object_unref (chat_spell->chat); + g_free (chat_spell->word); + g_slice_free (EmpathyChatSpell, chat_spell); +} + +static void +chat_text_check_word_spelling_cb (GtkMenuItem *menuitem, + EmpathyChatSpell *chat_spell) +{ + empathy_spell_dialog_show (chat_spell->chat, + chat_spell->start, + chat_spell->end, + chat_spell->word); +} + static void chat_text_populate_popup_cb (GtkTextView *view, GtkMenu *menu, @@ -1069,200 +1042,58 @@ chat_text_populate_popup_cb (GtkTextView *view, gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); - gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view), - GTK_TEXT_WINDOW_WIDGET, - x, y, - &x, &y); - - gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &iter, x, y); - - start = end = iter; - - if (gtk_text_iter_backward_to_tag_toggle (&start, tag) && - gtk_text_iter_forward_to_tag_toggle (&end, tag)) { - - str = gtk_text_buffer_get_text (buffer, - &start, &end, FALSE); - } - - if (G_STR_EMPTY (str)) { - return; - } - - chat_spell = chat_spell_new (chat, str, start, end); - - g_object_set_data_full (G_OBJECT (menu), - "chat_spell", chat_spell, - (GDestroyNotify) chat_spell_free); - - item = gtk_separator_menu_item_new (); - gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); - gtk_widget_show (item); - - item = gtk_menu_item_new_with_mnemonic (_("_Check Word Spelling...")); - g_signal_connect (item, - "activate", - G_CALLBACK (chat_text_check_word_spelling_cb), - chat_spell); - gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); - gtk_widget_show (item); -} - -static void -chat_text_check_word_spelling_cb (GtkMenuItem *menuitem, - EmpathyChatSpell *chat_spell) -{ - empathy_spell_dialog_show (chat_spell->chat, - chat_spell->start, - chat_spell->end, - chat_spell->word); -} - -static EmpathyChatSpell * -chat_spell_new (EmpathyChat *chat, - const gchar *word, - GtkTextIter start, - GtkTextIter end) -{ - EmpathyChatSpell *chat_spell; - - chat_spell = g_new0 (EmpathyChatSpell, 1); - - chat_spell->chat = g_object_ref (chat); - chat_spell->word = g_strdup (word); - chat_spell->start = start; - chat_spell->end = end; - - return chat_spell; -} - -static void -chat_spell_free (EmpathyChatSpell *chat_spell) -{ - g_object_unref (chat_spell->chat); - g_free (chat_spell->word); - g_free (chat_spell); -} - -static void -chat_composing_start (EmpathyChat *chat) -{ - EmpathyChatPriv *priv; - - priv = GET_PRIV (chat); - - if (priv->composing_stop_timeout_id) { - /* Just restart the timeout */ - chat_composing_remove_timeout (chat); - } else { - empathy_tp_chat_set_state (priv->tp_chat, - TP_CHANNEL_CHAT_STATE_COMPOSING); - } - - priv->composing_stop_timeout_id = g_timeout_add_seconds ( - COMPOSING_STOP_TIMEOUT, - (GSourceFunc) chat_composing_stop_timeout_cb, - chat); -} - -static void -chat_composing_stop (EmpathyChat *chat) -{ - EmpathyChatPriv *priv; + gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view), + GTK_TEXT_WINDOW_WIDGET, + x, y, + &x, &y); - priv = GET_PRIV (chat); + gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &iter, x, y); - chat_composing_remove_timeout (chat); - empathy_tp_chat_set_state (priv->tp_chat, - TP_CHANNEL_CHAT_STATE_ACTIVE); -} + start = end = iter; -static void -chat_composing_remove_timeout (EmpathyChat *chat) -{ - EmpathyChatPriv *priv; + if (gtk_text_iter_backward_to_tag_toggle (&start, tag) && + gtk_text_iter_forward_to_tag_toggle (&end, tag)) { - priv = GET_PRIV (chat); + str = gtk_text_buffer_get_text (buffer, + &start, &end, FALSE); + } - if (priv->composing_stop_timeout_id) { - g_source_remove (priv->composing_stop_timeout_id); - priv->composing_stop_timeout_id = 0; + if (G_STR_EMPTY (str)) { + return; } -} -static gboolean -chat_composing_stop_timeout_cb (EmpathyChat *chat) -{ - EmpathyChatPriv *priv; + chat_spell = chat_spell_new (chat, str, start, end); - priv = GET_PRIV (chat); + g_object_set_data_full (G_OBJECT (menu), + "chat_spell", chat_spell, + (GDestroyNotify) chat_spell_free); - priv->composing_stop_timeout_id = 0; - empathy_tp_chat_set_state (priv->tp_chat, - TP_CHANNEL_CHAT_STATE_PAUSED); + item = gtk_separator_menu_item_new (); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); - return FALSE; + item = gtk_menu_item_new_with_mnemonic (_("_Check Word Spelling...")); + g_signal_connect (item, + "activate", + G_CALLBACK (chat_text_check_word_spelling_cb), + chat_spell); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); } -static void -chat_state_changed_cb (EmpathyTpChat *tp_chat, - EmpathyContact *contact, - TpChannelChatState state, - EmpathyChat *chat) +static gboolean +chat_scroll_down_idle_func (EmpathyChat *chat) { EmpathyChatPriv *priv; - GList *l; - gboolean was_composing; priv = GET_PRIV (chat); - if (empathy_contact_is_user (contact)) { - /* We don't care about our own chat state */ - return; - } - - was_composing = (priv->compositors != NULL); - - /* Find the contact in the list. After that l is the list elem or NULL */ - for (l = priv->compositors; l; l = l->next) { - if (contact == l->data) { - break; - } - } - - switch (state) { - case TP_CHANNEL_CHAT_STATE_GONE: - case TP_CHANNEL_CHAT_STATE_INACTIVE: - case TP_CHANNEL_CHAT_STATE_PAUSED: - case TP_CHANNEL_CHAT_STATE_ACTIVE: - /* Contact is not composing */ - if (l) { - priv->compositors = g_list_remove_link (priv->compositors, l); - g_object_unref (l->data); - g_list_free1 (l); - } - break; - case TP_CHANNEL_CHAT_STATE_COMPOSING: - /* Contact is composing */ - if (!l) { - priv->compositors = g_list_prepend (priv->compositors, - g_object_ref (contact)); - } - break; - default: - g_assert_not_reached (); - } + empathy_chat_scroll_down (chat); + g_object_unref (chat); - empathy_debug (DEBUG_DOMAIN, "Was composing: %s now composing: %s", - was_composing ? "yes" : "no", - priv->compositors ? "yes" : "no"); + priv->scroll_idle_id = 0; - if ((was_composing && !priv->compositors) || - (!was_composing && priv->compositors)) { - /* Composing state changed */ - g_signal_emit (chat, signals[COMPOSING], 0, - priv->compositors != NULL); - } + return FALSE; } static void @@ -1282,7 +1113,7 @@ chat_add_logs (EmpathyChat *chat) messages = empathy_log_manager_get_last_messages (priv->log_manager, priv->account, empathy_chat_get_id (chat), - empathy_chat_is_group_chat (chat)); + FALSE); num_messages = g_list_length (messages); /* Only keep the 10 last messages */ @@ -1309,20 +1140,145 @@ chat_add_logs (EmpathyChat *chat) g_object_ref (chat)); } -/* Scroll down after the back-log has been received. */ -static gboolean -chat_scroll_down_idle_func (EmpathyChat *chat) +static void +empathy_chat_class_init (EmpathyChatClass *klass) { - EmpathyChatPriv *priv; + GObjectClass *object_class; - priv = GET_PRIV (chat); + object_class = G_OBJECT_CLASS (klass); - empathy_chat_scroll_down (chat); - g_object_unref (chat); + object_class->finalize = chat_finalize; + object_class->get_property = chat_get_property; + object_class->set_property = chat_set_property; - priv->scroll_idle_id = 0; + 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)); + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "Chat's name", + "The name of the chat", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_TOOLTIP, + g_param_spec_string ("tooltip", + "Chat's tooltip", + "The tooltip of the chat", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_ICON_NAME, + g_param_spec_string ("icon-name", + "Chat's icon name", + "The icon name of the chat", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, + PROP_WIDGET, + g_param_spec_object ("widget", + "Chat's widget", + "The widget of the chat", + GTK_TYPE_WIDGET, + G_PARAM_READABLE)); - return FALSE; + signals[COMPOSING] = + g_signal_new ("composing", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, G_TYPE_BOOLEAN); + + signals[NEW_MESSAGE] = + g_signal_new ("new-message", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _empathy_gtk_marshal_VOID__OBJECT_BOOLEAN, + G_TYPE_NONE, + 2, EMPATHY_TYPE_MESSAGE, G_TYPE_BOOLEAN); + + g_type_class_add_private (object_class, sizeof (EmpathyChatPriv)); +} + +static void +empathy_chat_init (EmpathyChat *chat) +{ + EmpathyChatPriv *priv = GET_PRIV (chat); + GtkTextBuffer *buffer; + + chat->view = empathy_chat_view_new (); + chat->input_text_view = gtk_text_view_new (); + + priv->is_first_char = TRUE; + + g_object_set (chat->input_text_view, + "pixels-above-lines", 2, + "pixels-below-lines", 2, + "pixels-inside-wrap", 1, + "right-margin", 2, + "left-margin", 2, + "wrap-mode", GTK_WRAP_WORD_CHAR, + NULL); + + priv->log_manager = empathy_log_manager_new (); + priv->default_window_height = -1; + priv->vscroll_visible = FALSE; + priv->sensitive = TRUE; + 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_CALLBACK (chat_input_key_press_event_cb), + chat); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + g_signal_connect (buffer, + "changed", + G_CALLBACK (chat_input_text_buffer_changed_cb), + chat); + g_signal_connect (chat->view, + "focus_in_event", + G_CALLBACK (chat_text_view_focus_in_event_cb), + chat); + + g_signal_connect (chat->input_text_view, + "size_allocate", + G_CALLBACK (chat_text_view_size_allocate_cb), + chat); + + g_signal_connect (chat->input_text_view, + "realize", + G_CALLBACK (chat_text_view_realize_cb), + chat); + + g_signal_connect (GTK_TEXT_VIEW (chat->input_text_view), + "populate_popup", + G_CALLBACK (chat_text_populate_popup_cb), + chat); + + /* create misspelt words identification tag */ + gtk_text_buffer_create_tag (buffer, + "misspelled", + "underline", PANGO_UNDERLINE_ERROR, + NULL); } gboolean @@ -1369,37 +1325,31 @@ empathy_chat_correct_word (EmpathyChat *chat, const gchar * empathy_chat_get_name (EmpathyChat *chat) { - g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); + EmpathyChatPriv *priv = GET_PRIV (chat); - if (EMPATHY_CHAT_GET_CLASS (chat)->get_name) { - return EMPATHY_CHAT_GET_CLASS (chat)->get_name (chat); - } + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); - return NULL; + return priv->name; } -gchar * +const gchar * empathy_chat_get_tooltip (EmpathyChat *chat) { - g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); + EmpathyChatPriv *priv = GET_PRIV (chat); - if (EMPATHY_CHAT_GET_CLASS (chat)->get_tooltip) { - return EMPATHY_CHAT_GET_CLASS (chat)->get_tooltip (chat); - } + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); - return NULL; + return priv->tooltip; } const gchar * empathy_chat_get_status_icon_name (EmpathyChat *chat) { - g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); + EmpathyChatPriv *priv = GET_PRIV (chat); - if (EMPATHY_CHAT_GET_CLASS (chat)->get_status_icon_name) { - return EMPATHY_CHAT_GET_CLASS (chat)->get_status_icon_name (chat); - } + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); - return NULL; + return priv->icon_name; } GtkWidget * @@ -1409,25 +1359,9 @@ empathy_chat_get_widget (EmpathyChat *chat) g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); - if (!priv->widget && EMPATHY_CHAT_GET_CLASS (chat)->get_widget) { - priv->widget = EMPATHY_CHAT_GET_CLASS (chat)->get_widget (chat); - } - return priv->widget; } -gboolean -empathy_chat_is_group_chat (EmpathyChat *chat) -{ - g_return_val_if_fail (EMPATHY_IS_CHAT (chat), FALSE); - - if (EMPATHY_CHAT_GET_CLASS (chat)->is_group_chat) { - return EMPATHY_CHAT_GET_CLASS (chat)->is_group_chat (chat); - } - - return FALSE; -} - gboolean empathy_chat_is_connected (EmpathyChat *chat) { @@ -1493,6 +1427,7 @@ empathy_chat_set_tp_chat (EmpathyChat *chat, EmpathyTpChat *tp_chat) { EmpathyChatPriv *priv; + TpChan *tp_chan; g_return_if_fail (EMPATHY_IS_CHAT (chat)); g_return_if_fail (EMPATHY_IS_TP_CHAT (tp_chat)); @@ -1532,7 +1467,8 @@ empathy_chat_set_tp_chat (EmpathyChat *chat, 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); + tp_chan = empathy_tp_chat_get_channel (tp_chat); + priv->handle_type = tp_chan->handle_type; if (priv->first_tp_chat) { chat_add_logs (chat); @@ -1558,10 +1494,6 @@ empathy_chat_set_tp_chat (EmpathyChat *chat, priv->sensitive = TRUE; } - 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"); } diff --git a/libempathy-gtk/empathy-chat.h b/libempathy-gtk/empathy-chat.h index aa5c3f1b3..113c285e0 100644 --- a/libempathy-gtk/empathy-chat.h +++ b/libempathy-gtk/empathy-chat.h @@ -61,17 +61,6 @@ struct _EmpathyChat { struct _EmpathyChatClass { GObjectClass parent; - - /* VTable */ - const gchar * (*get_name) (EmpathyChat *chat); - gchar * (*get_tooltip) (EmpathyChat *chat); - const gchar * (*get_status_icon_name)(EmpathyChat *chat); - GtkWidget * (*get_widget) (EmpathyChat *chat); - gboolean (*is_group_chat) (EmpathyChat *chat); - void (*set_tp_chat) (EmpathyChat *chat, - EmpathyTpChat *tp_chat); - gboolean (*key_press_event) (EmpathyChat *chat, - GdkEventKey *event); }; GType empathy_chat_get_type (void); @@ -83,10 +72,9 @@ void empathy_chat_cut (EmpathyChat *chat); void empathy_chat_copy (EmpathyChat *chat); void empathy_chat_paste (EmpathyChat *chat); const gchar * empathy_chat_get_name (EmpathyChat *chat); -gchar * empathy_chat_get_tooltip (EmpathyChat *chat); +const gchar * empathy_chat_get_tooltip (EmpathyChat *chat); const gchar * empathy_chat_get_status_icon_name (EmpathyChat *chat); GtkWidget * empathy_chat_get_widget (EmpathyChat *chat); -gboolean empathy_chat_is_group_chat (EmpathyChat *chat); gboolean empathy_chat_is_connected (EmpathyChat *chat); void empathy_chat_save_geometry (EmpathyChat *chat, gint x, -- cgit v1.2.3