/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2002-2007 Imendio AB
- * Copyright (C) 2007-2008 Collabora Ltd.
+ * Copyright (C) 2007-2010 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 <string.h>
#include <stdlib.h>
+#undef G_DISABLE_DEPRECATED /* for GCompletion */
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include <telepathy-glib/account-manager.h>
#include <telepathy-glib/util.h>
-#ifdef ENABLE_TPL
#include <telepathy-logger/log-manager.h>
-#else
-
-#include <libempathy/empathy-log-manager.h>
-#endif /* ENABLE_TPL */
+#include <telepathy-logger/text-event.h>
#include <libempathy/empathy-contact-list.h>
+#include <libempathy/empathy-gsettings.h>
+#include <libempathy/empathy-keyring.h>
#include <libempathy/empathy-utils.h>
-#include <libempathy/empathy-dispatcher.h>
+#include <libempathy/empathy-request-util.h>
+#include <libempathy/empathy-chatroom-manager.h>
#include "empathy-chat.h"
-#include "empathy-conf.h"
#include "empathy-spell.h"
-#include "empathy-contact-list-store.h"
-#include "empathy-contact-list-view.h"
-#include "empathy-contact-menu.h"
+#include "empathy-contact-dialogs.h"
+#include "empathy-individual-store-channel.h"
+#include "empathy-individual-view.h"
+#include "empathy-input-text-view.h"
#include "empathy-search-bar.h"
#include "empathy-theme-manager.h"
+#include "empathy-theme-adium.h"
#include "empathy-smiley-manager.h"
#include "empathy-ui-utils.h"
#include "empathy-string-parser.h"
+#include "extensions/extensions.h"
#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
#include <libempathy/empathy-debug.h>
-#define CHAT_DIR_CREATE_MODE (S_IRUSR | S_IWUSR | S_IXUSR)
-#define CHAT_FILE_CREATE_MODE (S_IRUSR | S_IWUSR)
-#define IS_ENTER(v) (v == GDK_Return || v == GDK_ISO_Enter || v == GDK_KP_Enter)
-#define MAX_INPUT_HEIGHT 150
+#define IS_ENTER(v) (v == GDK_KEY_Return || v == GDK_KEY_ISO_Enter || v == GDK_KEY_KP_Enter)
#define COMPOSING_STOP_TIMEOUT 5
#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChat)
-typedef struct {
+struct _EmpathyChatPriv {
EmpathyTpChat *tp_chat;
TpAccount *account;
gchar *id;
gchar *name;
gchar *subject;
+ EmpathyContact *self_contact;
EmpathyContact *remote_contact;
gboolean show_contacts;
-#ifdef ENABLE_TPL
+ GSettings *gsettings_chat;
+ GSettings *gsettings_ui;
+
TplLogManager *log_manager;
-#else
- EmpathyLogManager *log_manager;
-#endif /* ENABLE_TPL */
TpAccountManager *account_manager;
GList *input_history;
GList *input_history_current;
TpHandleType handle_type;
gint contacts_width;
gboolean has_input_vscroll;
- gint topic_width;
+
+ /* TRUE if spell checking is enabled, FALSE otherwise.
+ * This is to keep track of the last state of spell checking
+ * when it changes. */
+ gboolean spell_checking_enabled;
+
+ /* These store the signal handler ids for the enclosed text entry. */
+ gulong insert_text_id;
+ gulong delete_range_id;
+ gulong notify_cursor_position_id;
+
+ /* Source func ID for update_misspelled_words () */
+ guint update_misspelled_words_id;
+ /* Source func ID for save_paned_pos_timeout () */
+ guint save_paned_pos_id;
+ /* Source func ID for chat_contacts_visible_timeout_cb () */
+ guint contacts_visible_id;
GtkWidget *widget;
GtkWidget *hpaned;
GtkWidget *search_bar;
guint unread_messages;
+ guint unread_messages_when_offline;
/* TRUE if the pending messages can be displayed. This is to avoid to show
* pending messages *before* messages from logs. (#603980) */
gboolean can_show_pending;
* notified again about the already notified pending messages when the
* messages in tab will be properly shown */
gboolean retrieving_backlogs;
-} EmpathyChatPriv;
+ gboolean sms_channel;
+
+ /* we need to know whether populate-popup happened in response to
+ * the keyboard or the mouse. We can't ask GTK for the most recent
+ * event, because it will be a notify event. Instead we track it here */
+ GdkEventType most_recent_event_type;
+
+ /* A regex matching our own current nickname in the room, or %NULL if
+ * !empathy_chat_is_room (). */
+ GRegex *highlight_regex;
+
+ /* TRUE if empathy_chat_is_room () and there are unread highlighted messages.
+ * Cleared by empathy_chat_messages_read (). */
+ gboolean highlighted;
+};
typedef struct {
gchar *text; /* Original message that was specified
enum {
COMPOSING,
NEW_MESSAGE,
+ PART_COMMAND_ENTERED,
LAST_SIGNAL
};
PROP_SUBJECT,
PROP_REMOTE_CONTACT,
PROP_SHOW_CONTACTS,
+ PROP_SMS_CHANNEL,
+ PROP_N_MESSAGES_SENDING,
+ PROP_NB_UNREAD_MESSAGES,
};
static guint signals[LAST_SIGNAL] = { 0 };
-G_DEFINE_TYPE (EmpathyChat, empathy_chat, GTK_TYPE_BIN);
+G_DEFINE_TYPE (EmpathyChat, empathy_chat, GTK_TYPE_BOX);
+
+static gboolean update_misspelled_words (gpointer data);
static void
chat_get_property (GObject *object,
g_value_set_object (value, priv->account);
break;
case PROP_NAME:
- g_value_set_string (value, empathy_chat_get_name (chat));
+ g_value_take_string (value, empathy_chat_dup_name (chat));
break;
case PROP_ID:
g_value_set_string (value, priv->id);
case PROP_SHOW_CONTACTS:
g_value_set_boolean (value, priv->show_contacts);
break;
+ case PROP_SMS_CHANNEL:
+ g_value_set_boolean (value, priv->sms_channel);
+ break;
+ case PROP_N_MESSAGES_SENDING:
+ g_value_set_uint (value,
+ empathy_chat_get_n_messages_sending (chat));
+ break;
+ case PROP_NB_UNREAD_MESSAGES:
+ g_value_set_uint (value,
+ empathy_chat_get_nb_unread_messages (chat));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
static void
-chat_connect_channel_reconnected (EmpathyDispatchOperation *dispatch,
- const GError *error,
- gpointer user_data)
-{
- EmpathyChat *chat = EMPATHY_CHAT (user_data);
- EmpathyTpChat *tpchat;
-
- if (error != NULL) {
- empathy_chat_view_append_event (chat->view,
- _("Failed to reconnect this chat"));
- return;
- }
-
- tpchat = EMPATHY_TP_CHAT (
- empathy_dispatch_operation_get_channel_wrapper (dispatch));
-
- if (empathy_dispatch_operation_claim (dispatch)) {
- empathy_chat_set_tp_chat (chat, tpchat);
- }
-}
-
-static void
-reconnected_connection_ready_cb (TpConnection *connection,
- const GError *error,
- gpointer user_data)
+account_reconnected (EmpathyChat *chat,
+ TpAccount *account)
{
- EmpathyChat *chat = user_data;
EmpathyChatPriv *priv = GET_PRIV (chat);
- if (error != NULL) {
- DEBUG ("connection is not ready: %s", error->message);
- goto out;
- }
-
DEBUG ("Account reconnected, request a new Text channel");
+ /* FIXME: Ideally we should ask to handle ourself the channel so we can
+ * report the error if any but this is blocked by
+ * https://bugs.freedesktop.org/show_bug.cgi?id=13422 */
switch (priv->handle_type) {
case TP_HANDLE_TYPE_CONTACT:
- empathy_dispatcher_chat_with_contact_id (
- connection, priv->id, EMPATHY_DISPATCHER_NON_USER_ACTION,
- chat_connect_channel_reconnected,
- chat);
+ if (priv->sms_channel)
+ empathy_sms_contact_id (
+ account, priv->id,
+ TP_USER_ACTION_TIME_NOT_USER_ACTION,
+ NULL, NULL);
+ else
+ empathy_chat_with_contact_id (
+ account, priv->id,
+ TP_USER_ACTION_TIME_NOT_USER_ACTION,
+ NULL, NULL);
break;
case TP_HANDLE_TYPE_ROOM:
- empathy_dispatcher_join_muc (connection,
- priv->id, EMPATHY_DISPATCHER_NON_USER_ACTION,
- chat_connect_channel_reconnected,
- chat);
+ empathy_join_muc (account, priv->id,
+ TP_USER_ACTION_TIME_NOT_USER_ACTION);
break;
+ case TP_HANDLE_TYPE_NONE:
+ case TP_HANDLE_TYPE_LIST:
+ case TP_HANDLE_TYPE_GROUP:
default:
g_assert_not_reached ();
break;
}
-out:
g_object_unref (chat);
}
EmpathyChat *chat)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- TpConnection *connection;
if (new_status != TP_CONNECTION_STATUS_CONNECTED)
return;
- connection = tp_account_get_connection (account);
-
if (priv->tp_chat != NULL || account != priv->account ||
priv->handle_type == TP_HANDLE_TYPE_NONE ||
EMP_STR_EMPTY (priv->id))
return;
g_object_ref (chat);
- tp_connection_call_when_ready (connection, reconnected_connection_ready_cb,
- chat);
+
+ account_reconnected (chat, account);
}
static void
}
}
+static void
+set_chate_state_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (!tp_text_channel_set_chat_state_finish (TP_TEXT_CHANNEL (source), result,
+ &error)) {
+ DEBUG ("Failed to set chat state: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+set_chat_state (EmpathyChat *self,
+ TpChannelChatState state)
+{
+ EmpathyChatPriv *priv = GET_PRIV (self);
+
+ if (!tp_proxy_has_interface_by_id (priv->tp_chat,
+ TP_IFACE_QUARK_CHANNEL_INTERFACE_CHAT_STATE))
+ return;
+
+ tp_text_channel_set_chat_state_async (TP_TEXT_CHANNEL (priv->tp_chat), state,
+ set_chate_state_cb, self);
+}
+
static gboolean
chat_composing_stop_timeout_cb (EmpathyChat *chat)
{
EmpathyChatPriv *priv;
+ gboolean send_chat_states;
priv = GET_PRIV (chat);
priv->composing_stop_timeout_id = 0;
- empathy_tp_chat_set_state (priv->tp_chat,
- TP_CHANNEL_CHAT_STATE_PAUSED);
+ send_chat_states = g_settings_get_boolean (priv->gsettings_chat,
+ EMPATHY_PREFS_CHAT_SEND_CHAT_STATES);
+ if (!send_chat_states) {
+ set_chat_state (chat, TP_CHANNEL_CHAT_STATE_ACTIVE);
+ } else {
+ set_chat_state (chat, TP_CHANNEL_CHAT_STATE_PAUSED);
+ }
return FALSE;
}
chat_composing_start (EmpathyChat *chat)
{
EmpathyChatPriv *priv;
+ gboolean send_chat_states;
priv = GET_PRIV (chat);
+ send_chat_states = g_settings_get_boolean (priv->gsettings_chat,
+ EMPATHY_PREFS_CHAT_SEND_CHAT_STATES);
+ if (!send_chat_states) {
+ return;
+ }
+
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);
+ set_chat_state (chat, TP_CHANNEL_CHAT_STATE_COMPOSING);
}
priv->composing_stop_timeout_id = g_timeout_add_seconds (
static void
chat_composing_stop (EmpathyChat *chat)
{
- EmpathyChatPriv *priv;
-
- priv = GET_PRIV (chat);
-
chat_composing_remove_timeout (chat);
- empathy_tp_chat_set_state (priv->tp_chat,
- TP_CHANNEL_CHAT_STATE_ACTIVE);
+ set_chat_state (chat, TP_CHANNEL_CHAT_STATE_ACTIVE);
}
static gint
g_free (text);
}
-static void
-chat_command_join_cb (EmpathyDispatchOperation *dispatch,
- const GError *error,
- gpointer user_data)
-{
- EmpathyChat *chat = user_data;
-
- if (error != NULL) {
- DEBUG ("Error: %s", error->message);
- empathy_chat_view_append_event (chat->view,
- _("Failed to join chat room"));
- }
-}
-
typedef struct {
EmpathyChat *chat;
gchar *message;
} ChatCommandMsgData;
static void
-chat_command_msg_cb (EmpathyDispatchOperation *dispatch,
- const GError *error,
+chat_command_msg_cb (GObject *source,
+ GAsyncResult *result,
gpointer user_data)
{
ChatCommandMsgData *data = user_data;
+ GError *error = NULL;
+ TpChannel *channel;
+
+ channel = tp_account_channel_request_ensure_and_observe_channel_finish (
+ TP_ACCOUNT_CHANNEL_REQUEST (source), result, &error);
+
+ if (channel == NULL) {
+ DEBUG ("Failed to get channel: %s", error->message);
+ g_error_free (error);
- if (error != NULL) {
empathy_chat_view_append_event (data->chat->view,
_("Failed to open private chat"));
goto OUT;
}
- if (!EMP_STR_EMPTY (data->message)) {
- EmpathyTpChat *tpchat;
- EmpathyMessage *message;
+ if (!EMP_STR_EMPTY (data->message) && TP_IS_TEXT_CHANNEL (channel)) {
+ TpTextChannel *text = (TpTextChannel *) channel;
+ TpMessage *msg;
- tpchat = EMPATHY_TP_CHAT (
- empathy_dispatch_operation_get_channel_wrapper (dispatch));
+ msg = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ data->message);
- message = empathy_message_new (data->message);
- empathy_tp_chat_send (tpchat, message);
- g_object_unref (message);
+ tp_text_channel_send_message_async (text, msg, 0, NULL, NULL);
+
+ g_object_unref (msg);
}
+ g_object_unref (channel);
+
OUT:
g_free (data->message);
g_slice_free (ChatCommandMsgData, data);
}
+static gboolean
+nick_command_supported (EmpathyChat *chat)
+{
+ EmpathyChatPriv * priv = GET_PRIV (chat);
+ TpConnection *connection;
+
+ connection = tp_channel_borrow_connection (TP_CHANNEL (priv->tp_chat));
+ return tp_proxy_has_interface_by_id (connection,
+ EMP_IFACE_QUARK_CONNECTION_INTERFACE_RENAMING);
+}
+
+static gboolean
+part_command_supported (EmpathyChat *chat)
+{
+ EmpathyChatPriv * priv = GET_PRIV (chat);
+
+ return tp_proxy_has_interface_by_id (priv->tp_chat,
+ TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP);
+}
+
static void
chat_command_clear (EmpathyChat *chat,
GStrv strv)
GStrv strv)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- EmpathyTpChatProperty *property;
- GValue value = {0, };
- property = empathy_tp_chat_get_property (priv->tp_chat, "subject");
- if (property == NULL) {
+ if (!empathy_tp_chat_supports_subject (priv->tp_chat)) {
empathy_chat_view_append_event (chat->view,
_("Topic not supported on this conversation"));
return;
}
- if (!(property->flags & TP_PROPERTY_FLAG_WRITE)) {
+ if (!empathy_tp_chat_can_set_subject (priv->tp_chat)) {
empathy_chat_view_append_event (chat->view,
_("You are not allowed to change the topic"));
return;
}
- g_value_init (&value, G_TYPE_STRING);
- g_value_set_string (&value, strv[1]);
- empathy_tp_chat_set_property (priv->tp_chat, "subject", &value);
- g_value_unset (&value);
+ empathy_tp_chat_set_subject (priv->tp_chat, strv[1]);
+}
+
+void
+empathy_chat_join_muc (EmpathyChat *chat,
+ const gchar *room)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ empathy_join_muc (priv->account, room,
+ empathy_get_current_action_time ());
}
static void
GStrv strv)
{
guint i = 0;
- EmpathyChatPriv *priv = GET_PRIV (chat);
GStrv rooms = g_strsplit_set (strv[1], ", ", -1);
+ /* FIXME: Ideally we should ask to handle ourself the channel so we can
+ * report the error if any but this is blocked by
+ * https://bugs.freedesktop.org/show_bug.cgi?id=13422 */
while (rooms[i] != NULL) {
/* ignore empty strings */
if (!EMP_STR_EMPTY (rooms[i])) {
- TpConnection *connection;
-
- connection = empathy_tp_chat_get_connection (priv->tp_chat);
- empathy_dispatcher_join_muc (connection, rooms[i],
- gtk_get_current_event_time (),
- chat_command_join_cb,
- chat);
+ empathy_chat_join_muc (chat, rooms[i]);
}
i++;
}
g_strfreev (rooms);
}
+static void
+chat_command_part (EmpathyChat *chat,
+ GStrv strv)
+{
+ g_signal_emit (chat, signals[PART_COMMAND_ENTERED], 0, strv);
+}
+
static void
chat_command_msg_internal (EmpathyChat *chat,
const gchar *contact_id,
const gchar *message)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- TpConnection *connection;
ChatCommandMsgData *data;
+ TpAccountChannelRequest *req;
+ GHashTable *request;
+
+ request = tp_asv_new (
+ TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT,
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
+ TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, contact_id,
+ NULL);
+
+ req = tp_account_channel_request_new (priv->account, request,
+ empathy_get_current_action_time ());
/* FIXME: We should probably search in members alias. But this
* is enough for IRC */
data = g_slice_new (ChatCommandMsgData);
data->chat = chat;
data->message = g_strdup (message);
- connection = empathy_tp_chat_get_connection (priv->tp_chat);
- empathy_dispatcher_chat_with_contact_id (connection, contact_id,
- gtk_get_current_event_time (),
- chat_command_msg_cb,
- data);
+
+ tp_account_channel_request_ensure_and_observe_channel_async (req,
+ EMPATHY_CHAT_BUS_NAME, NULL, chat_command_msg_cb, data);
+
+ g_object_unref (req);
+ g_hash_table_unref (request);
}
static void
chat_command_msg_internal (chat, strv[1], strv[2]);
}
+static void
+callback_for_request_rename (TpProxy *proxy,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ if (error != NULL) {
+ DEBUG ("Call to RequestRename method failed: %s",error->message);
+ }
+}
+
static void
chat_command_nick (EmpathyChat *chat,
GStrv strv)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- TpConnection *connection;
- GHashTable *new_alias;
- TpHandle handle;
-
- connection = tp_account_get_connection (priv->account);
- handle = tp_connection_get_self_handle (connection);
- new_alias = g_hash_table_new (g_direct_hash, g_direct_equal);
- g_hash_table_insert (new_alias, GUINT_TO_POINTER (handle), strv[1]);
+ TpProxy *proxy;
- tp_cli_connection_interface_aliasing_call_set_aliases (connection, -1,
- new_alias, NULL, NULL, NULL, NULL);
+ proxy = TP_PROXY (tp_account_get_connection (priv->account));
- g_hash_table_destroy (new_alias);
+ emp_cli_connection_interface_renaming_call_request_rename (proxy, -1,
+ strv[1], callback_for_request_rename, NULL, NULL, NULL);
}
static void
GStrv strv)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- EmpathyMessage *message;
+ TpMessage *message;
+ TpTextChannel *channel;
+
+ channel = (TpTextChannel *) (priv->tp_chat);
+
+ if (!tp_text_channel_supports_message_type (channel,
+ TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)) {
+ /* Action message are not supported, 'simulate' the action */
+ gchar *tmp;
+
+ /* The TpChat can't be ready if it doesn't have the self contact */
+ g_assert (priv->self_contact != NULL);
+
+ tmp = g_strdup_printf ("%s %s", empathy_contact_get_alias (priv->self_contact),
+ strv[1]);
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ tmp);
+ g_free (tmp);
+ }
+ else {
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
+ strv[1]);
+ }
- message = empathy_message_new (strv[1]);
- empathy_message_set_tptype (message, TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION);
empathy_tp_chat_send (priv->tp_chat, message);
g_object_unref (message);
}
GStrv strv)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- EmpathyMessage *message;
+ TpMessage *message;
+
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ strv[1]);
+ empathy_tp_chat_send (priv->tp_chat, message);
+ g_object_unref (message);
+}
+
+static void
+whois_got_contact_cb (TpConnection *connection,
+ guint n_contacts,
+ TpContact * const *contacts,
+ const gchar * const *requested_ids,
+ GHashTable *failed_id_errors,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ EmpathyChat *chat = EMPATHY_CHAT (weak_object);
+
+ g_return_if_fail (n_contacts <= 1);
+
+ if (n_contacts == 0) {
+ GHashTableIter iter;
+ gpointer key = NULL, value = NULL;
+ gchar *id;
+ GError *id_error;
+
+ /* tp-glib guarantees that the contacts you requested will be
+ * in failed_id_errors regardless of whether the individual
+ * contact was invalid or the whole operation failed.
+ */
+ g_hash_table_iter_init (&iter, failed_id_errors);
+ g_hash_table_iter_next (&iter, &key, &value);
+ id = key;
+ id_error = value;
+
+ DEBUG ("Error getting TpContact for '%s': %s %u %s",
+ id, g_quark_to_string (id_error->domain),
+ id_error->code, id_error->message);
+
+ if (error == NULL) {
+ /* The specific ID failed. */
+ gchar *event = g_strdup_printf (
+ _("“%s” is not a valid contact ID"), id);
+ empathy_chat_view_append_event (chat->view, event);
+ g_free (event);
+ }
+ /* Otherwise we're disconnected or something; so the window
+ * will already say ‘Disconnected’, so let's not show anything.
+ */
+ } else {
+ EmpathyContact *empathy_contact;
+ GtkWidget *window;
+
+ g_return_if_fail (contacts[0] != NULL);
+ empathy_contact = empathy_contact_dup_from_tp_contact (
+ contacts[0]);
+
+ window = gtk_widget_get_toplevel (GTK_WIDGET (chat));
+ /* If we're alive and this command is running, we'd better be
+ * in a window. */
+ g_return_if_fail (window != NULL);
+ g_return_if_fail (gtk_widget_is_toplevel (window));
+ empathy_contact_information_dialog_show (empathy_contact,
+ GTK_WINDOW (window));
+ g_object_unref (empathy_contact);
+ }
+}
+
+static void
+chat_command_whois (EmpathyChat *chat,
+ GStrv strv)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+ TpConnection *conn;
+
+ conn = tp_channel_borrow_connection ((TpChannel *) priv->tp_chat);
+ tp_connection_get_contacts_by_id (conn,
+ /* Element 0 of 'strv' is "whois"; element 1 is the contact ID
+ * entered by the user (including spaces, if any). */
+ 1, (const gchar * const *) strv + 1,
+ 0, NULL,
+ whois_got_contact_cb, NULL, NULL, G_OBJECT (chat));
+}
- message = empathy_message_new (strv[1]);
+static void
+chat_command_whale (EmpathyChat *chat,
+ GStrv strv)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+ TpMessage *message;
+
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ "\n\n\n"
+ "•_______________•");
+ empathy_tp_chat_send (priv->tp_chat, message);
+ g_object_unref (message);
+}
+
+static void
+chat_command_babywhale (EmpathyChat *chat,
+ GStrv strv)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+ TpMessage *message;
+
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ "\n"
+ "•_____•");
empathy_tp_chat_send (priv->tp_chat, message);
g_object_unref (message);
}
+static void
+chat_command_inspector (EmpathyChat *chat,
+ GStrv strv)
+{
+ if (EMPATHY_IS_THEME_ADIUM (chat->view)) {
+ empathy_theme_adium_show_inspector (
+ EMPATHY_THEME_ADIUM (chat->view));
+ }
+}
+
static void chat_command_help (EmpathyChat *chat, GStrv strv);
typedef void (*ChatCommandFunc) (EmpathyChat *chat, GStrv strv);
guint min_parts;
guint max_parts;
ChatCommandFunc func;
+ gboolean (*is_supported)(EmpathyChat *chat);
const gchar *help;
} ChatCommandItem;
static ChatCommandItem commands[] = {
- {"clear", 1, 1, chat_command_clear,
+ {"clear", 1, 1, chat_command_clear, NULL,
N_("/clear: clear all messages from the current conversation")},
- {"topic", 2, 2, chat_command_topic,
+ {"topic", 2, 2, chat_command_topic, NULL,
N_("/topic <topic>: set the topic of the current conversation")},
- {"join", 2, 2, chat_command_join,
+ {"join", 2, 2, chat_command_join, NULL,
N_("/join <chat room ID>: join a new chat room")},
- {"j", 2, 2, chat_command_join,
+ {"j", 2, 2, chat_command_join, NULL,
N_("/j <chat room ID>: join a new chat room")},
- {"query", 2, 3, chat_command_query,
+ /* FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=643295 */
+ {"part", 1, 3, chat_command_part, part_command_supported,
+ N_("/part [<chat room ID>] [<reason>]: leave the chat room, "
+ "by default the current one")},
+
+ {"query", 2, 3, chat_command_query, NULL,
N_("/query <contact ID> [<message>]: open a private chat")},
- {"msg", 3, 3, chat_command_msg,
+ {"msg", 3, 3, chat_command_msg, NULL,
N_("/msg <contact ID> <message>: open a private chat")},
- {"nick", 2, 2, chat_command_nick,
+ {"nick", 2, 2, chat_command_nick, nick_command_supported,
N_("/nick <nickname>: change your nickname on the current server")},
- {"me", 2, 2, chat_command_me,
+ {"me", 2, 2, chat_command_me, NULL,
N_("/me <message>: send an ACTION message to the current conversation")},
- {"say", 2, 2, chat_command_say,
+ {"say", 2, 2, chat_command_say, NULL,
N_("/say <message>: send <message> to the current conversation. "
"This is used to send a message starting with a '/'. For example: "
"\"/say /join is used to join a new chat room\"")},
- {"help", 1, 2, chat_command_help,
+ {"whois", 2, 2, chat_command_whois, NULL,
+ N_("/whois <contact ID>: display information about a contact")},
+
+ {"help", 1, 2, chat_command_help, NULL,
N_("/help [<command>]: show all supported commands. "
"If <command> is defined, show its usage.")},
+
+ {"inspector", 1, 1, chat_command_inspector, NULL, NULL},
+
+ {"whale", 1, 1, chat_command_whale, NULL, NULL},
+ {"babywhale", 1, 1, chat_command_babywhale, NULL, NULL},
};
static void
{
gchar *str;
+ if (item->help == NULL) {
+ return;
+ }
+
str = g_strdup_printf (_("Usage: %s"), _(item->help));
empathy_chat_view_append_event (chat->view, str);
g_free (str);
* strv[1] will be the terminal NULL */
if (strv[1] == NULL) {
for (i = 0; i < G_N_ELEMENTS (commands); i++) {
+ if (commands[i].is_supported != NULL) {
+ if (!commands[i].is_supported (chat)) {
+ continue;
+ }
+ }
+ if (commands[i].help == NULL) {
+ continue;
+ }
empathy_chat_view_append_event (chat->view,
_(commands[i].help));
}
for (i = 0; i < G_N_ELEMENTS (commands); i++) {
if (g_ascii_strcasecmp (strv[1], commands[i].prefix) == 0) {
+ if (commands[i].is_supported != NULL) {
+ if (!commands[i].is_supported (chat)) {
+ break;
+ }
+ }
+ if (commands[i].help == NULL) {
+ break;
+ }
chat_command_show_help (chat, &commands[i]);
return;
}
const gchar *msg)
{
EmpathyChatPriv *priv;
- EmpathyMessage *message;
+ TpMessage *message;
guint i;
if (EMP_STR_EMPTY (msg)) {
if (c != '\0' && !g_ascii_isspace (c)) {
continue;
}
+ if (commands[i].is_supported != NULL) {
+ if (!commands[i].is_supported (chat)) {
+ continue;
+ }
+ }
/* We can't use g_strsplit here because it does
* not deal correctly if we have more than one space
}
}
- message = empathy_message_new (msg);
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ msg);
empathy_tp_chat_send (priv->tp_chat, message);
g_object_unref (message);
}
static void
chat_input_text_view_send (EmpathyChat *chat)
{
- EmpathyChatPriv *priv;
GtkTextBuffer *buffer;
GtkTextIter start, end;
gchar *msg;
- priv = GET_PRIV (chat);
-
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
gtk_text_buffer_get_bounds (buffer, &start, &end);
}
}
-static void
-chat_message_received (EmpathyChat *chat, EmpathyMessage *message)
+static GRegex *
+get_highlight_regex_for (const gchar *name)
{
- EmpathyChatPriv *priv = GET_PRIV (chat);
- EmpathyContact *sender;
-
- sender = empathy_message_get_sender (message);
-
- DEBUG ("Appending new message from %s (%d)",
- empathy_contact_get_name (sender),
- empathy_contact_get_handle (sender));
-
- empathy_chat_view_append_message (chat->view, message);
-
- /* We received a message so the contact is no longer composing */
- chat_state_changed_cb (priv->tp_chat, sender,
- TP_CHANNEL_CHAT_STATE_ACTIVE,
- chat);
-
- priv->unread_messages++;
- g_signal_emit (chat, signals[NEW_MESSAGE], 0, message);
-}
+ GRegex *regex;
+ gchar *name_esc, *pattern;
+ GError *error = NULL;
-static void
-chat_message_received_cb (EmpathyTpChat *tp_chat,
- EmpathyMessage *message,
- EmpathyChat *chat)
-{
- chat_message_received (chat, message);
-}
+ name_esc = g_regex_escape_string (name, -1);
+ pattern = g_strdup_printf ("\\b%s\\b", name_esc);
+ regex = g_regex_new (pattern, G_REGEX_CASELESS | G_REGEX_OPTIMIZE, 0,
+ &error);
-static void
-chat_send_error_cb (EmpathyTpChat *tp_chat,
- const gchar *message_body,
- TpChannelTextSendError error_code,
- EmpathyChat *chat)
-{
- const gchar *error;
- gchar *str;
+ if (regex == NULL) {
+ DEBUG ("couldn't compile regex /%s/: %s", pattern,
+ error->message);
- switch (error_code) {
- case TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE:
- error = _("offline");
- break;
- case TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT:
- error = _("invalid contact");
- break;
- 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;
+ g_error_free (error);
}
- str = g_strdup_printf (_("Error sending message '%s': %s"),
- message_body,
- error);
- empathy_chat_view_append_event (chat->view, str);
- g_free (str);
+ g_free (pattern);
+ g_free (name_esc);
+
+ return regex;
}
-/* WARNING: EXPLICIT CONTENT, keep away childrens!
- *
- * When a GtkLabel is set to wrap, it assume and hardcoded width. To change
- * that width we have to set a size request on the label... but that's not
- * possible because we want the window to be able to shrink, so we MUST request
- * width of 1. Note that the height of a wrapping label depends on its width.
- *
- * To work around that, here is what happens:
- * 1) size-request is first called, an hardcoded small width is requested by
- * GtkLabel, which means also a too big height. We do nothing.
- * 2) size-allocate is called with the full width available, that's the width
- * we really want to make wrap the label. We save that width and restart a
- * size-request/size-allocate round.
- * 3) size-request is called a 2nd time, now we can tell the pango layout its
- * width (we can't do that in step 2 because GtkLabel::size-request recreate
- * the layout each time). When the layout has its width, we can know the
- * height of the label and set its requisition. The width request is set to
- * 1px to make sure the window can shrink, the layout will fill all the
- * available width anyway.
+/* Called when priv->self_contact changes, or priv->self_contact:alias changes.
+ * Only connected if empathy_chat_is_room() is TRUE, for obvious-ish reasons.
*/
static void
-chat_topic_label_size_request_cb (GtkLabel *label,
- GtkRequisition *requisition,
- EmpathyChat *chat)
+chat_self_contact_alias_changed_cb (EmpathyChat *chat)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- if (gtk_label_get_line_wrap (label) && priv->topic_width > 0) {
- PangoLayout *layout;
- PangoRectangle rect;
- gint ypad;
+ tp_clear_pointer (&priv->highlight_regex, g_regex_unref);
- layout = gtk_label_get_layout (label);
- pango_layout_set_width (layout, priv->topic_width * PANGO_SCALE);
- pango_layout_get_extents (layout, NULL, &rect);
- gtk_misc_get_padding (GTK_MISC (label), NULL, &ypad);
+ if (priv->self_contact != NULL) {
+ const gchar *alias = empathy_contact_get_alias (priv->self_contact);
- requisition->width = 1;
- requisition->height = PANGO_PIXELS (rect.height) + ypad * 2;
+ g_return_if_fail (alias != NULL);
+ priv->highlight_regex = get_highlight_regex_for (alias);
}
}
-static void
-chat_topic_label_size_allocate_cb (GtkLabel *label,
- GtkAllocation *allocation,
- EmpathyChat *chat)
+static gboolean
+chat_should_highlight (EmpathyChat *chat,
+ EmpathyMessage *message)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
+ const gchar *msg;
+ TpChannelTextMessageFlags flags;
- if (!gtk_label_get_line_wrap (label)) {
- priv->topic_width = -1;
+ g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
- if (pango_layout_is_ellipsized (gtk_label_get_layout (label)))
- gtk_widget_show (priv->expander_topic);
- else
- gtk_widget_hide (priv->expander_topic);
+ if (!empathy_chat_is_room (chat)) {
+ return FALSE;
+ }
- return;
+ if (!empathy_message_is_incoming (message)) {
+ return FALSE;
}
- if (priv->topic_width != allocation->width) {
- priv->topic_width = allocation->width;
- gtk_widget_queue_resize (GTK_WIDGET (label));
+ msg = empathy_message_get_body (message);
+ if (!msg) {
+ return FALSE;
}
-}
-static void
-chat_topic_expander_activate_cb (GtkExpander *expander,
- GParamSpec *param_spec,
- EmpathyChat *chat)
-{
- EmpathyChatPriv *priv = GET_PRIV (chat);
+ flags = empathy_message_get_flags (message);
+ if (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK) {
+ /* FIXME: Ideally we shouldn't highlight scrollback messages only if they
+ * have already been received by the user before (and so are in the logs) */
+ return FALSE;
+ }
- if (gtk_expander_get_expanded (expander)) {
- gtk_label_set_ellipsize (GTK_LABEL (priv->label_topic), PANGO_ELLIPSIZE_NONE);
- gtk_label_set_line_wrap (GTK_LABEL (priv->label_topic), TRUE);
- } else {
- gtk_label_set_ellipsize (GTK_LABEL (priv->label_topic), PANGO_ELLIPSIZE_END);
- gtk_label_set_line_wrap (GTK_LABEL (priv->label_topic), FALSE);
+ if (priv->highlight_regex == NULL) {
+ return FALSE;
}
+
+ return g_regex_match (priv->highlight_regex, msg, 0, NULL);
}
static void
-chat_property_changed_cb (EmpathyTpChat *tp_chat,
- const gchar *name,
- GValue *value,
- EmpathyChat *chat)
+chat_message_received (EmpathyChat *chat,
+ EmpathyMessage *message,
+ gboolean pending)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
+ EmpathyContact *sender;
- if (!tp_strdiff (name, "subject")) {
- g_free (priv->subject);
- priv->subject = g_value_dup_string (value);
+ sender = empathy_message_get_sender (message);
+
+ if (empathy_message_is_edit (message)) {
+ DEBUG ("Editing message '%s' to '%s'",
+ empathy_message_get_supersedes (message),
+ empathy_message_get_body (message));
+
+ empathy_chat_view_edit_message (chat->view, message);
+ } else {
+ gboolean should_highlight = chat_should_highlight (chat, message);
+
+ if (should_highlight) {
+ priv->highlighted = TRUE;
+ }
+
+ DEBUG ("Appending new message '%s' from %s (%d)",
+ empathy_message_get_token (message),
+ empathy_contact_get_alias (sender),
+ empathy_contact_get_handle (sender));
+
+ empathy_chat_view_append_message (chat->view, message, should_highlight);
+
+ if (empathy_message_is_incoming (message)) {
+ priv->unread_messages++;
+ g_object_notify (G_OBJECT (chat), "nb-unread-messages");
+ }
+
+ g_signal_emit (chat, signals[NEW_MESSAGE], 0, message, pending,
+ should_highlight);
+ }
+
+ /* We received a message so the contact is no longer
+ * composing */
+ chat_state_changed_cb (priv->tp_chat, sender,
+ TP_CHANNEL_CHAT_STATE_ACTIVE,
+ chat);
+}
+
+static void
+chat_message_received_cb (EmpathyTpChat *tp_chat,
+ EmpathyMessage *message,
+ EmpathyChat *chat)
+{
+ chat_message_received (chat, message, FALSE);
+}
+
+static void
+chat_message_acknowledged_cb (EmpathyTpChat *tp_chat,
+ EmpathyMessage *message,
+ EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ empathy_chat_view_message_acknowledged (chat->view,
+ message);
+
+ if (!empathy_message_is_edit (message)) {
+ priv->unread_messages--;
+ g_object_notify (G_OBJECT (chat), "nb-unread-messages");
+ }
+}
+
+static void
+append_balance_error (EmpathyChat *chat,
+ const gchar *message_body)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+ TpConnection *conn = tp_channel_borrow_connection (TP_CHANNEL (priv->tp_chat));
+ const gchar *uri = tp_connection_get_balance_uri (conn);
+ const gchar *error = _("insufficient balance to send message");
+ gchar *str, *str_markup = NULL;
+
+ if (message_body != NULL) {
+ str = g_strdup_printf (_("Error sending message '%s': %s"), message_body, error);
+ } else {
+ str = g_strdup_printf (_("Error sending message: %s"), error);
+ }
+
+ if (!tp_str_empty (uri)) {
+ /* translators: error used when user doesn't have enough credit on his
+ * account to send the message. */
+ gchar *markup_error = g_strdup_printf (_("insufficient balance to send message."
+ " <a href='%s'>Top up</a>."), uri);
+
+ if (message_body != NULL) {
+ gchar *escaped_body = g_markup_escape_text (message_body, -1);
+
+ str_markup = g_strdup_printf (_("Error sending message '%s': %s"),
+ escaped_body, markup_error);
+
+ g_free (escaped_body);
+ } else {
+ str_markup = g_strdup_printf (_("Error sending message: %s"), markup_error);
+ }
+
+ g_free (markup_error);
+ }
+
+ if (str_markup != NULL)
+ empathy_chat_view_append_event_markup (chat->view, str_markup, str);
+ else
+ empathy_chat_view_append_event (chat->view, str);
+
+ g_free (str);
+ g_free (str_markup);
+}
+
+static void
+chat_send_error_cb (EmpathyTpChat *tp_chat,
+ const gchar *message_body,
+ TpChannelTextSendError error_code,
+ const gchar *dbus_error,
+ EmpathyChat *chat)
+{
+ const gchar *error = NULL;
+ gchar *str;
+
+ if (!tp_strdiff (dbus_error, TP_ERROR_STR_INSUFFICIENT_BALANCE)) {
+ append_balance_error (chat, message_body);
+ return;
+ } else if (!tp_strdiff (dbus_error, TP_ERROR_STR_NOT_CAPABLE)) {
+ error = _("not capable");
+ }
+
+ if (error == NULL) {
+ /* if we didn't find a dbus-error, try the old error */
+ switch (error_code) {
+ case TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE:
+ error = _("offline");
+ break;
+ case TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT:
+ error = _("invalid contact");
+ break;
+ 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;
+ case TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN:
+ default:
+ error = _("unknown");
+ break;
+ }
+ }
+
+ if (message_body != NULL) {
+ str = g_strdup_printf (_("Error sending message '%s': %s"),
+ message_body, error);
+ }
+ else {
+ str = g_strdup_printf (_("Error sending message: %s"), error);
+ }
+
+ empathy_chat_view_append_event (chat->view, str);
+ g_free (str);
+}
+
+static void
+chat_topic_label_size_allocate_cb (GtkLabel *label,
+ GtkAllocation *allocation,
+ EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ if (!gtk_label_get_line_wrap (label)) {
+ if (pango_layout_is_ellipsized (gtk_label_get_layout (label)))
+ gtk_widget_show (priv->expander_topic);
+ else
+ gtk_widget_hide (priv->expander_topic);
+
+ return;
+ }
+}
+
+static void
+chat_topic_expander_activate_cb (GtkExpander *expander,
+ GParamSpec *param_spec,
+ EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ if (gtk_expander_get_expanded (expander)) {
+ gtk_label_set_ellipsize (GTK_LABEL (priv->label_topic), PANGO_ELLIPSIZE_NONE);
+ gtk_label_set_line_wrap (GTK_LABEL (priv->label_topic), TRUE);
+ } else {
+ gtk_label_set_ellipsize (GTK_LABEL (priv->label_topic), PANGO_ELLIPSIZE_END);
+ gtk_label_set_line_wrap (GTK_LABEL (priv->label_topic), FALSE);
+ }
+}
+
+static void
+chat_subject_changed_cb (EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ g_free (priv->subject);
+ priv->subject = g_strdup (empathy_tp_chat_get_subject (priv->tp_chat));
g_object_notify (G_OBJECT (chat), "subject");
if (EMP_STR_EMPTY (priv->subject)) {
gtk_widget_show (priv->hbox_topic);
}
if (priv->block_events_timeout_id == 0) {
- gchar *str;
+ gchar *str = NULL;
if (!EMP_STR_EMPTY (priv->subject)) {
- str = g_strdup_printf (_("Topic set to: %s"), priv->subject);
- } else {
+ const gchar *actor = empathy_tp_chat_get_subject_actor (priv->tp_chat);
+
+ if (tp_str_empty (actor)) {
+ str = g_strdup_printf (_("Topic set to: %s"), priv->subject);
+ } else {
+ str = g_strdup_printf (_("Topic set by %s to: %s"),
+ actor, priv->subject);
+ }
+ } else if (empathy_tp_chat_supports_subject (priv->tp_chat)) {
+ /* No need to display this 'event' is no topic can be defined anyway */
str = g_strdup (_("No topic defined"));
}
- empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
- g_free (str);
+
+ if (str != NULL) {
+ empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
+ g_free (str);
+ }
}
- }
- else if (!tp_strdiff (name, "name")) {
+}
+
+static void
+chat_title_changed_cb (EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
g_free (priv->name);
- priv->name = g_value_dup_string (value);
+ priv->name = g_strdup (empathy_tp_chat_get_title (priv->tp_chat));
g_object_notify (G_OBJECT (chat), "name");
- }
}
-static void
-chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer,
- EmpathyChat *chat)
+static gboolean
+chat_input_text_get_word_from_iter (GtkTextIter *iter,
+ GtkTextIter *start,
+ GtkTextIter *end)
{
- GtkTextIter start, end;
- gchar *str;
- gboolean spell_checker = FALSE;
+ GtkTextIter word_start = *iter;
+ GtkTextIter word_end = *iter;
+ GtkTextIter tmp;
- if (gtk_text_buffer_get_char_count (buffer) == 0) {
- chat_composing_stop (chat);
- } else {
- chat_composing_start (chat);
+ if (gtk_text_iter_inside_word (&word_end) &&
+ !gtk_text_iter_ends_word (&word_end)) {
+ gtk_text_iter_forward_word_end (&word_end);
}
- empathy_conf_get_bool (empathy_conf_get (),
- EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
- &spell_checker);
+ tmp = word_end;
- gtk_text_buffer_get_start_iter (buffer, &start);
+ if (gtk_text_iter_get_char (&tmp) == '\'') {
+ gtk_text_iter_forward_char (&tmp);
- if (!spell_checker) {
- gtk_text_buffer_get_end_iter (buffer, &end);
- gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end);
- return;
+ if (g_unichar_isalpha (gtk_text_iter_get_char (&tmp))) {
+ gtk_text_iter_forward_word_end (&word_end);
+ }
}
- if (!empathy_spell_supported ()) {
- return;
- }
- /* NOTE: this is really inefficient, we shouldn't have to
- reiterate the whole buffer each time and check each work
- every time. */
- while (TRUE) {
- gboolean correct = FALSE;
+ if (gtk_text_iter_inside_word (&word_start) ||
+ gtk_text_iter_ends_word (&word_start)) {
+ if (!gtk_text_iter_starts_word (&word_start) ||
+ gtk_text_iter_equal (&word_start, &word_end)) {
+ gtk_text_iter_backward_word_start (&word_start);
+ }
- /* if at start */
- if (gtk_text_iter_is_start (&start)) {
- end = start;
+ tmp = word_start;
+ gtk_text_iter_backward_char (&tmp);
- if (!gtk_text_iter_forward_word_end (&end)) {
- /* no whole word yet */
- break;
- }
- } else {
- if (!gtk_text_iter_forward_word_end (&end)) {
- /* must be the end of the buffer */
- break;
- }
+ if (gtk_text_iter_get_char (&tmp) == '\'') {
+ gtk_text_iter_backward_char (&tmp);
- start = end;
- gtk_text_iter_backward_word_start (&start);
+ if (g_unichar_isalpha (gtk_text_iter_get_char (&tmp))) {
+ gtk_text_iter_backward_word_start (&word_start);
+ }
}
+ }
+
+ *start = word_start;
+ *end = word_end;
+ return TRUE;
+}
+
+static void
+chat_input_text_buffer_insert_text_cb (GtkTextBuffer *buffer,
+ GtkTextIter *location,
+ gchar *text,
+ gint len,
+ EmpathyChat *chat)
+{
+ GtkTextIter iter, pos;
+
+ /* Remove all misspelled tags in the inserted text.
+ * This happens when text is inserted within a misspelled word. */
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter,
+ gtk_text_iter_get_offset (location) - len);
+ gtk_text_buffer_remove_tag_by_name (buffer, "misspelled",
+ &iter, location);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &pos, gtk_text_buffer_get_insert (buffer));
+
+ do {
+ GtkTextIter start, end;
+ gchar *str;
+
+ if (!chat_input_text_get_word_from_iter (&iter, &start, &end))
+ continue;
str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
- /* spell check string if not a command */
- if (str[0] != '/') {
- correct = empathy_spell_check (str);
+ if (gtk_text_iter_in_range (&pos, &start, &end) ||
+ gtk_text_iter_equal (&pos, &end) ||
+ empathy_spell_check (str)) {
+ gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end);
} else {
- correct = TRUE;
+ gtk_text_buffer_apply_tag_by_name (buffer, "misspelled", &start, &end);
}
- if (!correct) {
- gtk_text_buffer_apply_tag_by_name (buffer, "misspelled", &start, &end);
+ g_free (str);
+
+ } while (gtk_text_iter_forward_word_end (&iter) &&
+ gtk_text_iter_compare (&iter, location) <= 0);
+}
+
+static void
+chat_input_text_buffer_delete_range_cb (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ EmpathyChat *chat)
+{
+ GtkTextIter word_start, word_end;
+
+ if (chat_input_text_get_word_from_iter (start, &word_start, &word_end)) {
+ gtk_text_buffer_remove_tag_by_name (buffer, "misspelled",
+ &word_start, &word_end);
+ }
+}
+
+static void
+chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer,
+ EmpathyChat *chat)
+{
+ if (gtk_text_buffer_get_char_count (buffer) == 0) {
+ chat_composing_stop (chat);
+ } else {
+ chat_composing_start (chat);
+ }
+}
+
+static void
+chat_input_text_buffer_notify_cursor_position_cb (GtkTextBuffer *buffer,
+ GParamSpec *pspec,
+ EmpathyChat *chat)
+{
+ GtkTextIter pos;
+ GtkTextIter prev_pos;
+ GtkTextIter word_start;
+ GtkTextIter word_end;
+ GtkTextMark *mark;
+ gchar *str;
+
+ mark = gtk_text_buffer_get_mark (buffer, "previous-cursor-position");
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &pos,
+ gtk_text_buffer_get_insert (buffer));
+ gtk_text_buffer_get_iter_at_mark (buffer, &prev_pos, mark);
+
+ if (!chat_input_text_get_word_from_iter (&prev_pos, &word_start, &word_end))
+ goto out;
+
+ if (!gtk_text_iter_in_range (&pos, &word_start, &word_end) &&
+ !gtk_text_iter_equal (&pos, &word_end)) {
+ str = gtk_text_buffer_get_text (buffer,
+ &word_start, &word_end, FALSE);
+
+ if (!empathy_spell_check (str)) {
+ gtk_text_buffer_apply_tag_by_name (buffer,
+ "misspelled", &word_start, &word_end);
} else {
- gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end);
+ gtk_text_buffer_remove_tag_by_name (buffer,
+ "misspelled", &word_start, &word_end);
}
g_free (str);
-
- /* set start iter to the end iters position */
- start = end;
}
+
+out:
+ gtk_text_buffer_move_mark (buffer, mark, &pos);
}
static gboolean
priv = GET_PRIV (chat);
+ priv->most_recent_event_type = event->type;
+
/* 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)) {
+ (event->keyval == GDK_KEY_Up ||
+ event->keyval == GDK_KEY_Down)) {
GtkTextBuffer *buffer;
const gchar *str;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
chat_input_history_update (chat, buffer);
- if (event->keyval == GDK_Up) {
+ if (event->keyval == GDK_KEY_Up) {
str = chat_input_history_get_next (chat);
} else {
str = chat_input_history_get_prev (chat);
*/
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;
+ if (gtk_text_view_im_context_filter_keypress (view, event)) {
+ gtk_text_view_reset_im_context (view);
return TRUE;
}
return FALSE;
}
if (!(event->state & GDK_CONTROL_MASK) &&
- event->keyval == GDK_Page_Up) {
+ event->keyval == GDK_KEY_Page_Up) {
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw));
gtk_adjustment_set_value (adj, gtk_adjustment_get_value (adj) - gtk_adjustment_get_page_size (adj));
return TRUE;
}
if ((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK &&
- event->keyval == GDK_Page_Down) {
+ event->keyval == GDK_KEY_Page_Down) {
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw));
val = MIN (gtk_adjustment_get_value (adj) + gtk_adjustment_get_page_size (adj),
gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj));
gtk_adjustment_set_value (adj, val);
return TRUE;
}
- if (event->keyval == GDK_Escape) {
+ if (event->keyval == GDK_KEY_Escape) {
empathy_search_bar_hide (EMPATHY_SEARCH_BAR (priv->search_bar));
}
if (!(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) &&
- event->keyval == GDK_Tab) {
+ event->keyval == GDK_KEY_Tab) {
GtkTextBuffer *buffer;
GtkTextIter start, current;
gchar *nick, *completed;
if (completed) {
guint len;
const gchar *text;
- gchar *complete_char = NULL;
GString *message = NULL;
GList *l;
* which might be cased all wrong.
* Fixes #120876
* */
- text = empathy_contact_get_name (completed_list->data);
+ text = empathy_contact_get_alias (completed_list->data);
} else {
text = completed;
* */
message = g_string_new ("");
for (l = completed_list; l != NULL; l = l->next) {
- g_string_append (message, empathy_contact_get_name (l->data));
+ g_string_append (message, empathy_contact_get_alias (l->data));
g_string_append (message, " - ");
}
empathy_chat_view_append_event (chat->view, message->str);
gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
- if (len == 1 && is_start_of_buffer &&
- empathy_conf_get_string (empathy_conf_get (),
- EMPATHY_PREFS_CHAT_NICK_COMPLETION_CHAR,
- &complete_char) &&
- complete_char != NULL) {
+ if (len == 1 && is_start_of_buffer) {
+ gchar *complete_char;
+
+ complete_char = g_settings_get_string (
+ priv->gsettings_chat,
+ EMPATHY_PREFS_CHAT_NICK_COMPLETION_CHAR);
+
+ if (complete_char != NULL) {
gtk_text_buffer_insert_at_cursor (buffer,
complete_char,
strlen (complete_char));
gtk_text_buffer_insert_at_cursor (buffer, " ", 1);
g_free (complete_char);
+ }
}
g_free (completed);
return TRUE;
}
-static gboolean
-chat_input_set_size_request_idle (gpointer sw)
-{
- gtk_widget_set_size_request (sw, -1, MAX_INPUT_HEIGHT);
-
- return FALSE;
-}
-
-static void
-chat_input_size_request_cb (GtkWidget *widget,
- GtkRequisition *requisition,
- EmpathyChat *chat)
-{
- EmpathyChatPriv *priv = GET_PRIV (chat);
- GtkWidget *sw;
-
- sw = gtk_widget_get_parent (widget);
- if (requisition->height >= MAX_INPUT_HEIGHT && !priv->has_input_vscroll) {
- g_idle_add (chat_input_set_size_request_idle, sw);
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
- GTK_POLICY_NEVER,
- GTK_POLICY_ALWAYS);
- priv->has_input_vscroll = TRUE;
- }
-
- if (requisition->height < MAX_INPUT_HEIGHT && priv->has_input_vscroll) {
- gtk_widget_set_size_request (sw, -1, -1);
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
- GTK_POLICY_NEVER,
- GTK_POLICY_NEVER);
- priv->has_input_vscroll = FALSE;
- }
-}
-
static void
chat_input_realize_cb (GtkWidget *widget,
EmpathyChat *chat)
}
}
+static void
+chat_input_has_focus_notify_cb (GtkWidget *widget,
+ GParamSpec *pspec,
+ EmpathyChat *chat)
+{
+ empathy_chat_view_focus_toggled (chat->view, gtk_widget_has_focus (widget));
+}
+
static void
chat_insert_smiley_activate_cb (EmpathySmileyManager *manager,
EmpathySmiley *smiley,
gtk_menu_item_get_label (menu_item));
}
+
+static GtkWidget *
+chat_spelling_build_suggestions_menu (const gchar *code,
+ EmpathyChatSpell *chat_spell)
+{
+ GList *suggestions, *l;
+ GtkWidget *menu, *menu_item;
+
+ suggestions = empathy_spell_get_suggestions (code, chat_spell->word);
+ if (suggestions == NULL)
+ return NULL;
+
+ menu = gtk_menu_new ();
+ for (l = suggestions; l; l = l->next) {
+ menu_item = gtk_menu_item_new_with_label (l->data);
+ g_signal_connect (G_OBJECT (menu_item), "activate",
+ G_CALLBACK (chat_spelling_menu_activate_cb),
+ chat_spell);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+ }
+ empathy_spell_free_suggestions (suggestions);
+
+ gtk_widget_show_all (menu);
+
+ return menu;
+}
+
static GtkWidget *
chat_spelling_build_menu (EmpathyChatSpell *chat_spell)
{
- GtkWidget *menu, *menu_item;
- GList *suggestions, *l;
+ GtkWidget *menu, *submenu, *item;
+ GList *codes, *l;
+
+ codes = empathy_spell_get_enabled_language_codes ();
+ g_assert (codes != NULL);
+
+ if (g_list_length (codes) > 1) {
+ menu = gtk_menu_new ();
+
+ for (l = codes; l; l = l->next) {
+ const gchar *code, *name;
+
+ code = l->data;
+ name = empathy_spell_get_language_name (code);
+ if (!name)
+ continue;
+
+ item = gtk_image_menu_item_new_with_label (name);
+
+ submenu = chat_spelling_build_suggestions_menu (
+ code, chat_spell);
+ if (submenu == NULL)
+ gtk_widget_set_sensitive (item, FALSE);
+ else
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item),
+ submenu);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+ }
+ } else {
+ menu = chat_spelling_build_suggestions_menu (codes->data,
+ chat_spell);
+ if (menu == NULL) {
+ menu = gtk_menu_new ();
+ item = gtk_menu_item_new_with_label (_("(No Suggestions)"));
+ gtk_widget_set_sensitive (item, FALSE);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ }
+ }
+ g_list_free (codes);
+
+ gtk_widget_show_all (menu);
+
+ return menu;
+}
+
+typedef struct {
+ EmpathyChat *chat;
+ gchar *word;
+ gchar *code;
+} EmpathyChatWord;
+
+static EmpathyChatWord *
+chat_word_new (EmpathyChat *chat,
+ const gchar *word,
+ const gchar *code)
+{
+ EmpathyChatWord *chat_word;
+
+ chat_word = g_slice_new0 (EmpathyChatWord);
+
+ chat_word->chat = g_object_ref (chat);
+ chat_word->word = g_strdup (word);
+ chat_word->code = g_strdup (code);
+
+ return chat_word;
+}
+
+static void
+chat_word_free (EmpathyChatWord *chat_word)
+{
+ g_object_unref (chat_word->chat);
+ g_free (chat_word->word);
+ g_free (chat_word->code);
+ g_slice_free (EmpathyChatWord, chat_word);
+}
+
+static void
+chat_add_to_dictionary_activate_cb (GtkMenuItem *menu_item,
+ EmpathyChatWord *chat_word)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat_word->chat);
+
+ empathy_spell_add_to_dictionary (chat_word->code,
+ chat_word->word);
+ priv->update_misspelled_words_id =
+ g_idle_add (update_misspelled_words, chat_word->chat);
+}
+
+static GtkWidget *
+chat_spelling_build_add_to_dictionary_item (EmpathyChatSpell *chat_spell)
+{
+ GtkWidget *menu, *item, *lang_item, *image;
+ GList *codes, *l;
+ gchar *label;
+ const gchar *code, *name;
+ EmpathyChatWord *chat_word;
+
+ codes = empathy_spell_get_enabled_language_codes ();
+ g_assert (codes != NULL);
+ if (g_list_length (codes) > 1) {
+ /* translators: %s is the selected word */
+ label = g_strdup_printf (_("Add '%s' to Dictionary"),
+ chat_spell->word);
+ item = gtk_image_menu_item_new_with_mnemonic (label);
+ g_free (label);
+ image = gtk_image_new_from_icon_name (GTK_STOCK_ADD,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+ image);
+
+ menu = gtk_menu_new ();
+
+ for (l = codes; l; l = l->next) {
+ code = l->data;
+ name = empathy_spell_get_language_name (code);
+ if (name == NULL)
+ continue;
- menu = gtk_menu_new ();
- suggestions = empathy_spell_get_suggestions (chat_spell->word);
- if (suggestions == NULL) {
- menu_item = gtk_menu_item_new_with_label (_("(No Suggestions)"));
- gtk_widget_set_sensitive (menu_item, FALSE);
- gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
- } else {
- for (l = suggestions; l; l = l->next) {
- menu_item = gtk_menu_item_new_with_label (l->data);
- g_signal_connect (G_OBJECT (menu_item),
- "activate",
- G_CALLBACK (chat_spelling_menu_activate_cb),
- chat_spell);
- gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
- }
- }
- empathy_spell_free_suggestions (suggestions);
+ lang_item = gtk_image_menu_item_new_with_label (name);
- gtk_widget_show_all (menu);
+ chat_word= chat_word_new (chat_spell->chat,
+ chat_spell->word, code);
+ g_object_set_data_full (G_OBJECT (lang_item),
+ "chat-word", chat_word,
+ (GDestroyNotify) chat_word_free);
- return menu;
+ g_signal_connect (G_OBJECT (lang_item), "activate",
+ G_CALLBACK (chat_add_to_dictionary_activate_cb),
+ chat_word);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), lang_item);
+ }
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
+ } else {
+ code = codes->data;
+ name = empathy_spell_get_language_name (code);
+ g_assert (name != NULL);
+ /* translators: first %s is the selected word,
+ * second %s is the language name of the target dictionary */
+ label = g_strdup_printf (_("Add '%s' to %s Dictionary"),
+ chat_spell->word, name);
+ item = gtk_image_menu_item_new_with_mnemonic (label);
+ g_free (label);
+ image = gtk_image_new_from_icon_name (GTK_STOCK_ADD,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+
+ chat_word = chat_word_new (chat_spell->chat, chat_spell->word,
+ code);
+ g_object_set_data_full (G_OBJECT (item), "chat-word", chat_word,
+ (GDestroyNotify) chat_word_free);
+
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (chat_add_to_dictionary_activate_cb),
+ chat_word);
+ }
+ g_list_free (codes);
+
+ gtk_widget_show_all (item);
+
+ return item;
}
static void
chat_input_text_view_send (chat);
}
+static gboolean
+chat_input_button_press_event_cb (GtkTextView *view,
+ GdkEventButton *event,
+ EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ priv->most_recent_event_type = event->type;
+
+ return FALSE;
+}
+
static void
chat_input_populate_popup_cb (GtkTextView *view,
GtkMenu *menu,
EmpathyChat *chat)
{
- EmpathyChatPriv *priv;
+ EmpathyChatPriv *priv = GET_PRIV (chat);
GtkTextBuffer *buffer;
GtkTextTagTable *table;
GtkTextTag *tag;
gchar *str = NULL;
EmpathyChatSpell *chat_spell;
GtkWidget *spell_menu;
+ GtkWidget *spell_item;
EmpathySmileyManager *smiley_manager;
GtkWidget *smiley_menu;
GtkWidget *image;
- priv = GET_PRIV (chat);
buffer = gtk_text_view_get_buffer (view);
/* Add the emoticon menu. */
/* Add the spell check menu item. */
table = gtk_text_buffer_get_tag_table (buffer);
tag = gtk_text_tag_table_lookup (table, "misspelled");
- 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);
+
+ switch (priv->most_recent_event_type) {
+ case GDK_BUTTON_PRESS:
+ /* get the location from the pointer */
+ gdk_window_get_device_position (gtk_widget_get_window (GTK_WIDGET (view)),
+ gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (
+ gtk_widget_get_display (GTK_WIDGET (view)))), &x, &y, NULL);
+
+ 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);
+ break;
+
+ default:
+ g_warn_if_reached ();
+ /* assume the KEY_PRESS case */
+
+ case GDK_KEY_PRESS:
+ /* get the location from the cursor */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+ gtk_text_buffer_get_insert (buffer));
+ break;
+
+ }
+
start = end = iter;
if (gtk_text_iter_backward_to_tag_toggle (&start, tag) &&
gtk_text_iter_forward_to_tag_toggle (&end, tag)) {
if (!EMP_STR_EMPTY (str)) {
chat_spell = chat_spell_new (chat, str, start, end);
g_object_set_data_full (G_OBJECT (menu),
- "chat_spell", chat_spell,
+ "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);
+ /* Spelling suggestions */
item = gtk_image_menu_item_new_with_mnemonic (_("_Spelling Suggestions"));
image = gtk_image_new_from_icon_name (GTK_STOCK_SPELL_CHECK,
GTK_ICON_SIZE_MENU);
spell_menu = chat_spelling_build_menu (chat_spell);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), spell_menu);
+
+ spell_item = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (spell_menu), spell_item);
+ gtk_widget_show (spell_item);
+
+ /* Add to dictionary */
+ spell_item = chat_spelling_build_add_to_dictionary_item (chat_spell);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (spell_menu), spell_item);
+ gtk_widget_show (spell_item);
+
gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
gtk_widget_show (item);
}
}
-#ifdef ENABLE_TPL
static gboolean
-chat_log_filter (TplLogEntry *log,
+chat_log_filter (TplEvent *event,
gpointer user_data)
-#else
-static gboolean
-chat_log_filter (EmpathyMessage *message,
- gpointer user_data)
-#endif /* ENABLE_TPL */
{
EmpathyChat *chat = user_data;
-#ifdef ENABLE_TPL
EmpathyMessage *message;
-#endif /* ENABLE_TPL */
EmpathyChatPriv *priv = GET_PRIV (chat);
const GList *pending;
-#ifdef ENABLE_TPL
- g_return_val_if_fail (TPL_IS_LOG_ENTRY (log), FALSE);
-#else
- g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
-#endif /* ENABLE_TPL */
+ g_return_val_if_fail (TPL_IS_EVENT (event), FALSE);
g_return_val_if_fail (EMPATHY_IS_CHAT (chat), FALSE);
pending = empathy_tp_chat_get_pending_messages (priv->tp_chat);
-#ifdef ENABLE_TPL
- message = empathy_message_from_tpl_log_entry (log);
-#endif /* ENABLE_TPL */
+ message = empathy_message_from_tpl_log_event (event);
for (; pending; pending = g_list_next (pending)) {
if (empathy_message_equal (message, pending->data)) {
+ g_object_unref (message);
return FALSE;
}
}
-#ifdef ENABLE_TPL
+
g_object_unref (message);
-#endif
return TRUE;
}
for (l = messages; l != NULL ; l = g_list_next (l)) {
EmpathyMessage *message = EMPATHY_MESSAGE (l->data);
- chat_message_received (chat, message);
+ chat_message_received (chat, message, TRUE);
}
}
-#ifdef ENABLE_TPL
static void
got_filtered_messages_cb (GObject *manager,
GAsyncResult *result,
EmpathyChatPriv *priv = GET_PRIV (chat);
GError *error = NULL;
- messages = tpl_log_manager_get_filtered_messages_async_finish (result, &error);
-
- if (error != NULL) {
+ if (!tpl_log_manager_get_filtered_events_finish (TPL_LOG_MANAGER (manager),
+ result, &messages, &error)) {
DEBUG ("%s. Aborting.", error->message);
empathy_chat_view_append_event (chat->view,
_("Failed to retrieve recent logs"));
for (l = messages; l; l = g_list_next (l)) {
EmpathyMessage *message;
- g_assert (TPL_IS_LOG_ENTRY (l->data));
- message = empathy_message_from_tpl_log_entry (l->data);
+ g_assert (TPL_IS_EVENT (l->data));
+
+ message = empathy_message_from_tpl_log_event (l->data);
g_object_unref (l->data);
- empathy_chat_view_append_message (chat->view, message);
+ if (empathy_message_is_edit (message)) {
+ /* this is an edited message, create a synthetic event
+ * using the supersedes token and
+ * original-message-sent timestamp, that we can then
+ * replace */
+ EmpathyMessage *syn_msg = g_object_new (
+ EMPATHY_TYPE_MESSAGE,
+ "body", "",
+ "token", empathy_message_get_supersedes (message),
+ "type", empathy_message_get_tptype (message),
+ "timestamp", empathy_message_get_original_timestamp (message),
+ "incoming", empathy_message_is_incoming (message),
+ "is-backlog", TRUE,
+ "receiver", empathy_message_get_receiver (message),
+ "sender", empathy_message_get_sender (message),
+ NULL);
+
+ empathy_chat_view_append_message (chat->view, syn_msg,
+ chat_should_highlight (chat, syn_msg));
+ empathy_chat_view_edit_message (chat->view, message);
+
+ g_object_unref (syn_msg);
+ } else {
+ /* append the latest message */
+ empathy_chat_view_append_message (chat->view, message,
+ chat_should_highlight (chat, message));
+ }
+
g_object_unref (message);
}
g_list_free (messages);
/* Turn back on scrolling */
empathy_chat_view_scroll (chat->view, TRUE);
}
-#endif /* ENABLE_TPL */
-
static void
chat_add_logs (EmpathyChat *chat)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- gboolean is_chatroom;
-#ifndef ENABLE_TPL
- GList *messages, *l;
-#endif /* ENABLE_TPL */
+ TplEntity *target;
if (!priv->id) {
return;
empathy_chat_view_scroll (chat->view, FALSE);
/* Add messages from last conversation */
- is_chatroom = priv->handle_type == TP_HANDLE_TYPE_ROOM;
-
-#ifndef ENABLE_TPL
- messages = empathy_log_manager_get_filtered_messages (priv->log_manager,
- priv->account,
- priv->id,
- is_chatroom,
- 5,
- chat_log_filter,
- chat);
+ if (priv->handle_type == TP_HANDLE_TYPE_ROOM)
+ target = tpl_entity_new_from_room_id (priv->id);
+ else
+ target = tpl_entity_new (priv->id, TPL_ENTITY_CONTACT, NULL, NULL);
- for (l = messages; l; l = g_list_next (l)) {
- empathy_chat_view_append_message (chat->view, l->data);
- g_object_unref (l->data);
- }
-
- g_list_free (messages);
-
- /* Turn back on scrolling */
- empathy_chat_view_scroll (chat->view, TRUE);
-#else
priv->retrieving_backlogs = TRUE;
- tpl_log_manager_get_filtered_messages_async (priv->log_manager,
- priv->account,
- priv->id,
- is_chatroom,
- 5,
- chat_log_filter,
- chat,
- got_filtered_messages_cb,
- (gpointer) chat);
-#endif /* ENABLE_TPL */
+ tpl_log_manager_get_filtered_events_async (priv->log_manager,
+ priv->account,
+ target,
+ TPL_EVENT_MASK_TEXT,
+ 5,
+ chat_log_filter,
+ chat,
+ got_filtered_messages_cb,
+ (gpointer) chat);
+
+ g_object_unref (target);
}
static gint
const gchar *actor_name = NULL;
if (actor != NULL) {
- actor_name = empathy_contact_get_name (actor);
+ actor_name = empathy_contact_get_alias (actor);
}
/* Having an actor only really makes sense for a few actions... */
EmpathyChat *chat)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- const gchar *name = empathy_contact_get_name (contact);
+ const gchar *name = empathy_contact_get_alias (contact);
gchar *str;
g_return_if_fail (TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED != reason);
gchar *str;
str = g_strdup_printf (_("%s is now known as %s"),
- empathy_contact_get_name (old_contact),
- empathy_contact_get_name (new_contact));
+ empathy_contact_get_alias (old_contact),
+ empathy_contact_get_alias (new_contact));
empathy_chat_view_append_event (chat->view, str);
g_free (str);
}
}
static gboolean
-chat_reset_size_request (gpointer widget)
+chat_contacts_visible_timeout_cb (gpointer chat)
{
- gtk_widget_set_size_request (widget, -1, -1);
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ /* Relax the size request */
+ gtk_widget_set_size_request (priv->vbox_left, -1, -1);
+
+ /* Set the position of the slider. This must be done here because
+ * GtkPaned need to know its size allocation and it will be settled only
+ * after the gtk_window_resize () tough effect. */
+ if (priv->contacts_width > 0) {
+ gtk_paned_set_position (GTK_PANED (priv->hpaned),
+ priv->contacts_width);
+ }
+
+ priv->contacts_visible_id = 0;
return FALSE;
}
gboolean show)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- GtkAllocation allocation;
if (!priv->scrolled_window_contacts) {
return;
}
if (show && priv->contact_list_view == NULL) {
- EmpathyContactListStore *store;
+ EmpathyIndividualStore *store;
gint min_width;
+ GtkAllocation allocation;
/* We are adding the contact list to the chat, we don't want the
* chat view to become too small. If the chat view is already
gtk_widget_get_allocation (priv->vbox_left, &allocation);
min_width = MIN (allocation.width, 250);
gtk_widget_set_size_request (priv->vbox_left, min_width, -1);
- g_idle_add (chat_reset_size_request, priv->vbox_left);
- if (priv->contacts_width > 0) {
- gtk_paned_set_position (GTK_PANED (priv->hpaned),
- priv->contacts_width);
- }
+ /* There is no way to know when the window resize will happen
+ * since it is WM's decision. Let's hope it won't be longer. */
+ if (priv->contacts_visible_id != 0)
+ g_source_remove (priv->contacts_visible_id);
+ priv->contacts_visible_id = g_timeout_add (500,
+ chat_contacts_visible_timeout_cb, chat);
+
+ store = EMPATHY_INDIVIDUAL_STORE (
+ empathy_individual_store_channel_new ((TpChannel *) priv->tp_chat));
+
+ empathy_individual_store_set_show_groups (store, FALSE);
+
+ priv->contact_list_view = GTK_WIDGET (empathy_individual_view_new (store,
+ EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_TOOLTIP,
+ EMPATHY_INDIVIDUAL_FEATURE_ADD_CONTACT |
+ EMPATHY_INDIVIDUAL_FEATURE_CHAT |
+ EMPATHY_INDIVIDUAL_FEATURE_CALL |
+ EMPATHY_INDIVIDUAL_FEATURE_LOG |
+ EMPATHY_INDIVIDUAL_FEATURE_INFO));
+
+ empathy_individual_view_set_show_offline (
+ EMPATHY_INDIVIDUAL_VIEW (priv->contact_list_view), TRUE);
+ empathy_individual_view_set_show_uninteresting (
+ EMPATHY_INDIVIDUAL_VIEW (priv->contact_list_view), TRUE);
- store = empathy_contact_list_store_new (
- EMPATHY_CONTACT_LIST (priv->tp_chat));
- empathy_contact_list_store_set_show_groups (
- EMPATHY_CONTACT_LIST_STORE (store), FALSE);
-
- priv->contact_list_view = GTK_WIDGET (empathy_contact_list_view_new (store,
- EMPATHY_CONTACT_LIST_FEATURE_CONTACT_TOOLTIP,
- EMPATHY_CONTACT_FEATURE_CHAT |
- EMPATHY_CONTACT_FEATURE_CALL |
- EMPATHY_CONTACT_FEATURE_LOG |
- EMPATHY_CONTACT_FEATURE_INFO));
gtk_container_add (GTK_CONTAINER (priv->scrolled_window_contacts),
priv->contact_list_view);
+
gtk_widget_show (priv->contact_list_view);
gtk_widget_show (priv->scrolled_window_contacts);
g_object_unref (store);
g_object_notify (G_OBJECT (chat), "show-contacts");
}
+static void
+chat_self_contact_changed_cb (EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ if (priv->self_contact != NULL) {
+ g_signal_handlers_disconnect_by_func (priv->self_contact,
+ chat_self_contact_alias_changed_cb,
+ chat);
+ }
+ g_clear_object (&priv->self_contact);
+
+ priv->self_contact = empathy_tp_chat_get_self_contact (priv->tp_chat);
+ if (priv->self_contact != NULL) {
+ g_object_ref (priv->self_contact);
+
+ if (empathy_chat_is_room (chat)) {
+ g_signal_connect_swapped (priv->self_contact, "notify::alias",
+ G_CALLBACK (chat_self_contact_alias_changed_cb),
+ chat);
+ }
+ }
+
+ chat_self_contact_alias_changed_cb (chat);
+}
+
static void
chat_remote_contact_changed_cb (EmpathyChat *chat)
{
priv->handle_type = TP_HANDLE_TYPE_CONTACT;
}
else if (priv->tp_chat != NULL) {
- TpChannel *channel;
-
- channel = empathy_tp_chat_get_channel (priv->tp_chat);
- g_object_get (channel, "handle-type", &priv->handle_type, NULL);
+ tp_channel_get_handle ((TpChannel *) priv->tp_chat, &priv->handle_type);
}
chat_update_contacts_visibility (chat, priv->show_contacts);
}
static void
-chat_destroy_cb (EmpathyTpChat *tp_chat,
+chat_invalidated_cb (EmpathyTpChat *tp_chat,
+ guint domain,
+ gint code,
+ gchar *message,
EmpathyChat *chat)
{
EmpathyChatPriv *priv;
gtk_widget_set_sensitive (chat->input_text_view, FALSE);
chat_update_contacts_visibility (chat, FALSE);
+
+ priv->unread_messages_when_offline = priv->unread_messages;
}
static gboolean
-chat_hpaned_pos_changed_cb (GtkWidget* hpaned, gpointer user_data)
+update_misspelled_words (gpointer data)
{
+ EmpathyChat *chat = EMPATHY_CHAT (data);
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ gint length;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
+
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ length = gtk_text_iter_get_offset (&iter);
+ chat_input_text_buffer_insert_text_cb (buffer, &iter,
+ NULL, length, chat);
+
+ priv->update_misspelled_words_id = 0;
+
+ return FALSE;
+}
+
+static void
+conf_spell_checking_cb (GSettings *gsettings_chat,
+ const gchar *key,
+ gpointer user_data)
+{
+ EmpathyChat *chat = EMPATHY_CHAT (user_data);
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+ gboolean spell_checker;
+ GtkTextBuffer *buffer;
+
+ if (strcmp (key, EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED) != 0)
+ return;
+
+ spell_checker = g_settings_get_boolean (gsettings_chat,
+ EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED);
+
+ if (!empathy_spell_supported ()) {
+ spell_checker = FALSE;
+ }
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
+
+ if (spell_checker == priv->spell_checking_enabled) {
+ if (spell_checker) {
+ /* Possibly changed dictionaries,
+ * update misspelled words. Need to do so in idle
+ * so the spell checker is updated. */
+ priv->update_misspelled_words_id =
+ g_idle_add (update_misspelled_words, chat);
+ }
+
+ return;
+ }
+
+ if (spell_checker) {
+ GtkTextIter iter;
+
+ priv->notify_cursor_position_id = tp_g_signal_connect_object (
+ buffer, "notify::cursor-position",
+ G_CALLBACK (chat_input_text_buffer_notify_cursor_position_cb),
+ chat, 0);
+ priv->insert_text_id = tp_g_signal_connect_object (
+ buffer, "insert-text",
+ G_CALLBACK (chat_input_text_buffer_insert_text_cb),
+ chat, G_CONNECT_AFTER);
+ priv->delete_range_id = tp_g_signal_connect_object (
+ buffer, "delete-range",
+ G_CALLBACK (chat_input_text_buffer_delete_range_cb),
+ chat, G_CONNECT_AFTER);
+
+ gtk_text_buffer_create_tag (buffer, "misspelled",
+ "underline", PANGO_UNDERLINE_ERROR,
+ NULL);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+ gtk_text_buffer_get_insert (buffer));
+ gtk_text_buffer_create_mark (buffer, "previous-cursor-position",
+ &iter, TRUE);
+
+ /* Mark misspelled words in the existing buffer.
+ * Need to do so in idle so the spell checker is updated. */
+ priv->update_misspelled_words_id =
+ g_idle_add (update_misspelled_words, chat);
+ } else {
+ GtkTextTagTable *table;
+ GtkTextTag *tag;
+
+ g_signal_handler_disconnect (buffer, priv->notify_cursor_position_id);
+ priv->notify_cursor_position_id = 0;
+ g_signal_handler_disconnect (buffer, priv->insert_text_id);
+ priv->insert_text_id = 0;
+ g_signal_handler_disconnect (buffer, priv->delete_range_id);
+ priv->delete_range_id = 0;
+
+ table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (table, "misspelled");
+ gtk_text_tag_table_remove (table, tag);
+
+ gtk_text_buffer_delete_mark_by_name (buffer,
+ "previous-cursor-position");
+ }
+
+ priv->spell_checking_enabled = spell_checker;
+}
+
+static gboolean
+save_paned_pos_timeout (gpointer data)
+{
+ EmpathyChat *self = data;
gint hpaned_pos;
- hpaned_pos = gtk_paned_get_position (GTK_PANED(hpaned));
- empathy_conf_set_int (empathy_conf_get (),
- EMPATHY_PREFS_UI_CHAT_WINDOW_PANED_POS,
- hpaned_pos);
+
+ hpaned_pos = gtk_paned_get_position (GTK_PANED (self->priv->hpaned));
+
+ g_settings_set_int (self->priv->gsettings_ui,
+ EMPATHY_PREFS_UI_CHAT_WINDOW_PANED_POS,
+ hpaned_pos);
+
+ return FALSE;
+}
+
+static gboolean
+chat_hpaned_pos_changed_cb (GtkWidget* hpaned,
+ GParamSpec *spec,
+ gpointer user_data)
+{
+ EmpathyChat *chat = EMPATHY_CHAT (user_data);
+
+ if (chat->priv->save_paned_pos_id != 0)
+ g_source_remove (chat->priv->save_paned_pos_id);
+
+ chat->priv->save_paned_pos_id = g_timeout_add_seconds (1,
+ save_paned_pos_timeout, chat);
+
return TRUE;
}
GList *list = NULL;
gchar *filename;
GtkTextBuffer *buffer;
- gint paned_pos;
+ EmpathyThemeManager *theme_mgr;
filename = empathy_file_lookup ("empathy-chat.ui",
"libempathy-gtk");
empathy_builder_connect (gui, chat,
"expander_topic", "notify::expanded", chat_topic_expander_activate_cb,
"label_topic", "size-allocate", chat_topic_label_size_allocate_cb,
- "label_topic", "size-request", chat_topic_label_size_request_cb,
NULL);
g_free (filename);
/* Add message view. */
- chat->view = empathy_theme_manager_create_view (empathy_theme_manager_get ());
+ theme_mgr = empathy_theme_manager_dup_singleton ();
+ chat->view = empathy_theme_manager_create_view (theme_mgr);
+ g_object_unref (theme_mgr);
/* If this is a GtkTextView, it's set as a drag destination for text/plain
and other types, even though it's non-editable and doesn't accept any
drags. This steals drag motion for anything inside the scrollbars,
gtk_widget_show (GTK_WIDGET (chat->view));
/* Add input GtkTextView */
- chat->input_text_view = g_object_new (GTK_TYPE_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);
+ chat->input_text_view = empathy_input_text_view_new ();
+ g_signal_connect (chat->input_text_view, "notify::has-focus",
+ G_CALLBACK (chat_input_has_focus_notify_cb),
+ chat);
g_signal_connect (chat->input_text_view, "key-press-event",
G_CALLBACK (chat_input_key_press_event_cb),
chat);
- g_signal_connect (chat->input_text_view, "size-request",
- G_CALLBACK (chat_input_size_request_cb),
- chat);
g_signal_connect (chat->input_text_view, "realize",
G_CALLBACK (chat_input_realize_cb),
chat);
+ g_signal_connect (chat->input_text_view, "button-press-event",
+ G_CALLBACK (chat_input_button_press_event_cb),
+ chat);
g_signal_connect (chat->input_text_view, "populate-popup",
G_CALLBACK (chat_input_populate_popup_cb),
chat);
tp_g_signal_connect_object (buffer, "changed",
G_CALLBACK (chat_input_text_buffer_changed_cb),
chat, 0);
- gtk_text_buffer_create_tag (buffer, "misspelled",
- "underline", PANGO_UNDERLINE_ERROR,
- NULL);
+ tp_g_signal_connect_object (priv->gsettings_chat,
+ "changed::" EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED,
+ G_CALLBACK (conf_spell_checking_cb), chat, 0);
+ conf_spell_checking_cb (priv->gsettings_chat,
+ EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED, chat);
gtk_container_add (GTK_CONTAINER (priv->scrolled_window_input),
chat->input_text_view);
gtk_widget_show (chat->input_text_view);
g_signal_connect (priv->hpaned, "notify::position",
G_CALLBACK (chat_hpaned_pos_changed_cb),
- NULL);
-
- /* Load the paned position */
- if (empathy_conf_get_int (empathy_conf_get (),
- EMPATHY_PREFS_UI_CHAT_WINDOW_PANED_POS,
- &paned_pos)
- && paned_pos)
- gtk_paned_set_position (GTK_PANED(priv->hpaned), paned_pos);
-
- /* Set widget focus order */
- list = g_list_append (NULL, priv->search_bar);
- list = g_list_append (list, priv->scrolled_window_input);
- gtk_container_set_focus_chain (GTK_CONTAINER (priv->vbox_left), list);
- g_list_free (list);
-
- list = g_list_append (NULL, priv->vbox_left);
- list = g_list_append (list, priv->scrolled_window_contacts);
- gtk_container_set_focus_chain (GTK_CONTAINER (priv->hpaned), list);
- g_list_free (list);
-
- list = g_list_append (NULL, priv->hpaned);
- list = g_list_append (list, priv->hbox_topic);
- gtk_container_set_focus_chain (GTK_CONTAINER (priv->widget), list);
- g_list_free (list);
-
- /* Add the main widget in the chat widget */
- gtk_container_add (GTK_CONTAINER (chat), priv->widget);
- g_object_unref (gui);
-}
-
-static void
-chat_size_request (GtkWidget *widget,
- GtkRequisition *requisition)
-{
- GtkBin *bin = GTK_BIN (widget);
- GtkWidget *child;
-
- requisition->width = gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2;
- requisition->height = gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2;
-
- child = gtk_bin_get_child (bin);
-
- if (child && gtk_widget_get_visible (child))
- {
- GtkRequisition child_requisition;
-
- gtk_widget_size_request (child, &child_requisition);
-
- requisition->width += child_requisition.width;
- requisition->height += child_requisition.height;
- }
-}
-
-static void
-chat_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation)
-{
- GtkBin *bin = GTK_BIN (widget);
- GtkAllocation child_allocation;
- GtkWidget *child;
+ chat);
- gtk_widget_set_allocation (widget, allocation);
+ /* Set widget focus order */
+ list = g_list_append (NULL, priv->search_bar);
+ list = g_list_append (list, priv->scrolled_window_input);
+ gtk_container_set_focus_chain (GTK_CONTAINER (priv->vbox_left), list);
+ g_list_free (list);
- child = gtk_bin_get_child (bin);
+ list = g_list_append (NULL, priv->vbox_left);
+ list = g_list_append (list, priv->scrolled_window_contacts);
+ gtk_container_set_focus_chain (GTK_CONTAINER (priv->hpaned), list);
+ g_list_free (list);
- if (child && gtk_widget_get_visible (child))
- {
- child_allocation.x = allocation->x + gtk_container_get_border_width (GTK_CONTAINER (widget));
- child_allocation.y = allocation->y + gtk_container_get_border_width (GTK_CONTAINER (widget));
- child_allocation.width = MAX (allocation->width - gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2, 0);
- child_allocation.height = MAX (allocation->height - gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2, 0);
+ list = g_list_append (NULL, priv->hpaned);
+ list = g_list_append (list, priv->hbox_topic);
+ gtk_container_set_focus_chain (GTK_CONTAINER (priv->widget), list);
+ g_list_free (list);
- gtk_widget_size_allocate (child, &child_allocation);
- }
+ /* Add the main widget in the chat widget */
+ gtk_box_pack_start (GTK_BOX (chat), priv->widget, TRUE, TRUE, 0);
+ g_object_unref (gui);
}
static void
DEBUG ("Finalized: %p", object);
+ if (priv->update_misspelled_words_id != 0)
+ g_source_remove (priv->update_misspelled_words_id);
+
+ if (priv->save_paned_pos_id != 0)
+ g_source_remove (priv->save_paned_pos_id);
+
+ if (priv->contacts_visible_id != 0)
+ g_source_remove (priv->contacts_visible_id);
+
+ g_object_unref (priv->gsettings_chat);
+ g_object_unref (priv->gsettings_ui);
+
g_list_foreach (priv->input_history, (GFunc) chat_input_history_entry_free, NULL);
g_list_free (priv->input_history);
if (priv->tp_chat) {
g_signal_handlers_disconnect_by_func (priv->tp_chat,
- chat_destroy_cb, chat);
+ chat_invalidated_cb, chat);
g_signal_handlers_disconnect_by_func (priv->tp_chat,
chat_message_received_cb, chat);
+ g_signal_handlers_disconnect_by_func (priv->tp_chat,
+ chat_message_acknowledged_cb, chat);
g_signal_handlers_disconnect_by_func (priv->tp_chat,
chat_send_error_cb, chat);
g_signal_handlers_disconnect_by_func (priv->tp_chat,
chat_state_changed_cb, chat);
- g_signal_handlers_disconnect_by_func (priv->tp_chat,
- chat_property_changed_cb, chat);
g_signal_handlers_disconnect_by_func (priv->tp_chat,
chat_members_changed_cb, chat);
+ g_signal_handlers_disconnect_by_func (priv->tp_chat,
+ chat_self_contact_changed_cb, chat);
g_signal_handlers_disconnect_by_func (priv->tp_chat,
chat_remote_contact_changed_cb, chat);
- empathy_tp_chat_leave (priv->tp_chat);
+ g_signal_handlers_disconnect_by_func (priv->tp_chat,
+ chat_title_changed_cb, chat);
+ g_signal_handlers_disconnect_by_func (priv->tp_chat,
+ chat_subject_changed_cb, chat);
+ empathy_tp_chat_leave (priv->tp_chat, "");
g_object_unref (priv->tp_chat);
}
if (priv->account) {
g_object_unref (priv->account);
}
+ if (priv->self_contact) {
+ g_signal_handlers_disconnect_by_func (priv->self_contact,
+ chat_self_contact_alias_changed_cb,
+ chat);
+ g_object_unref (priv->self_contact);
+ }
if (priv->remote_contact) {
g_object_unref (priv->remote_contact);
}
g_free (priv->subject);
g_completion_free (priv->completion);
+ tp_clear_pointer (&priv->highlight_regex, g_regex_unref);
+
G_OBJECT_CLASS (empathy_chat_parent_class)->finalize (object);
}
EmpathyChat *chat = EMPATHY_CHAT (object);
EmpathyChatPriv *priv = GET_PRIV (chat);
- if (priv->handle_type != TP_HANDLE_TYPE_ROOM)
+ if (priv->handle_type != TP_HANDLE_TYPE_ROOM) {
+ /* First display logs from the logger and then display pending messages */
chat_add_logs (chat);
-#ifndef ENABLE_TPL
- /* When async API are involved, pending message are shown at the end of the
- * callbacks' chain fired by chat_add_logs */
- priv->can_show_pending = TRUE;
- show_pending_messages (chat);
-#endif /* ENABLE_TPL */
+ }
+ else {
+ /* Just display pending messages for rooms */
+ priv->can_show_pending = TRUE;
+ show_pending_messages (chat);
+ }
}
static void
empathy_chat_class_init (EmpathyChatClass *klass)
{
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = chat_finalize;
object_class->set_property = chat_set_property;
object_class->constructed = chat_constructed;
- widget_class->size_request = chat_size_request;
- widget_class->size_allocate = chat_size_allocate;
-
g_object_class_install_property (object_class,
PROP_TP_CHAT,
g_param_spec_object ("tp-chat",
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_SMS_CHANNEL,
+ g_param_spec_boolean ("sms-channel",
+ "SMS Channel",
+ "TRUE if this channel is for sending SMSes",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_N_MESSAGES_SENDING,
+ g_param_spec_uint ("n-messages-sending",
+ "Num Messages Sending",
+ "The number of messages being sent",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_NB_UNREAD_MESSAGES,
+ g_param_spec_uint ("nb-unread-messages",
+ "Num Unread Messages",
+ "The number of unread messages",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
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_cclosure_marshal_generic,
G_TYPE_NONE,
1, G_TYPE_BOOLEAN);
+ /**
+ * EmpathyChat::new-message:
+ * @self: the #EmpathyChat
+ * @message: the new message
+ * @pending: whether the message was in the pending queue when @self
+ * was created
+ * @should_highlight: %TRUE if the message mentions the local user
+ */
signals[NEW_MESSAGE] =
g_signal_new ("new-message",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
+ g_cclosure_marshal_generic,
G_TYPE_NONE,
- 1, EMPATHY_TYPE_MESSAGE);
+ 3, EMPATHY_TYPE_MESSAGE, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+
+ signals[PART_COMMAND_ENTERED] =
+ g_signal_new ("part-command-entered",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRV);
g_type_class_add_private (object_class, sizeof (EmpathyChatPriv));
}
EmpathyChat *chat = user_data;
GError *error = NULL;
- if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
+ if (!tp_proxy_prepare_finish (account_manager, result, &error)) {
DEBUG ("Failed to prepare the account manager: %s", error->message);
g_error_free (error);
return;
EMPATHY_TYPE_CHAT, EmpathyChatPriv);
chat->priv = priv;
-#ifndef ENABLE_TPL
- priv->log_manager = empathy_log_manager_dup_singleton ();
-#else
priv->log_manager = tpl_log_manager_dup_singleton ();
-#endif /* ENABLE_TPL */
- priv->contacts_width = -1;
+ priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
+ priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA);
+
+ priv->contacts_width = g_settings_get_int (priv->gsettings_ui,
+ EMPATHY_PREFS_UI_CHAT_WINDOW_PANED_POS);
priv->input_history = NULL;
priv->input_history_current = NULL;
priv->account_manager = tp_account_manager_dup ();
- tp_account_manager_prepare_async (priv->account_manager, NULL,
+ tp_proxy_prepare_async (priv->account_manager, NULL,
account_manager_prepared_cb, chat);
- empathy_conf_get_bool (empathy_conf_get (),
- EMPATHY_PREFS_CHAT_SHOW_CONTACTS_IN_ROOMS,
- &priv->show_contacts);
+ priv->show_contacts = g_settings_get_boolean (priv->gsettings_chat,
+ EMPATHY_PREFS_CHAT_SHOW_CONTACTS_IN_ROOMS);
/* Block events for some time to avoid having "has come online" or
* "joined" messages. */
g_timeout_add_seconds (1, chat_block_events_timeout_cb, chat);
/* Add nick name completion */
- priv->completion = g_completion_new ((GCompletionFunc) empathy_contact_get_name);
+ priv->completion = g_completion_new ((GCompletionFunc) empathy_contact_get_alias);
g_completion_set_compare (priv->completion, chat_contacts_completion_func);
chat_create_ui (chat);
return priv->tp_chat;
}
-static void display_password_info_bar (EmpathyChat *self,
- gboolean retry);
+typedef struct
+{
+ EmpathyChat *self;
+ GtkWidget *info_bar;
+ gulong response_id;
+ GtkWidget *button;
+ GtkWidget *label;
+ GtkWidget *entry;
+ GtkWidget *spinner;
+ gchar *password;
+} PasswordData;
+
+static void
+passwd_remember_button_cb (GtkButton *button,
+ PasswordData *data)
+{
+ gtk_info_bar_response (GTK_INFO_BAR (data->info_bar), GTK_RESPONSE_OK);
+}
+
+static void
+passwd_not_now_button_cb (GtkButton *button,
+ PasswordData *data)
+{
+ gtk_info_bar_response (GTK_INFO_BAR (data->info_bar), GTK_RESPONSE_NO);
+}
+
+static void
+remember_password_infobar_response_cb (GtkWidget *info_bar,
+ gint response_id,
+ PasswordData *data)
+{
+ EmpathyChatPriv *priv = GET_PRIV (data->self);
+
+ if (response_id == GTK_RESPONSE_OK) {
+ DEBUG ("Saving room password");
+ empathy_keyring_set_room_password_async (priv->account,
+ empathy_tp_chat_get_id (priv->tp_chat),
+ data->password,
+ NULL, NULL);
+ }
+
+ gtk_widget_destroy (info_bar);
+ g_free (data->password);
+ g_slice_free (PasswordData, data);
+}
+
+static void
+chat_prompt_to_save_password (EmpathyChat *self,
+ PasswordData *data)
+{
+ GtkWidget *content_area;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GtkWidget *label;
+ GtkWidget *alig;
+ GtkWidget *button;
+
+ /* save the password in case it needs to be saved */
+ data->password = g_strdup (gtk_entry_get_text (GTK_ENTRY (data->entry)));
+
+ /* Remove all previous widgets */
+ content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (data->info_bar));
+ gtk_container_forall (GTK_CONTAINER (content_area),
+ (GtkCallback) gtk_widget_destroy, NULL);
+ data->button = NULL;
+ data->label = NULL;
+ data->entry = NULL;
+ data->spinner = NULL;
+
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (data->info_bar),
+ GTK_MESSAGE_QUESTION);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
+
+ /* Add image */
+ image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
+ GTK_ICON_SIZE_DIALOG);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+ /* Add message */
+ label = gtk_label_new (_("Would you like to store this password?"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+
+ /* Add 'Remember' button */
+ alig = gtk_alignment_new (0, 0.5, 1, 0);
+
+ button = gtk_button_new_with_label (_("Remember"));
+ gtk_container_add (GTK_CONTAINER (alig), button);
+ gtk_box_pack_start (GTK_BOX (hbox), alig, FALSE, FALSE, 0);
+
+ g_signal_connect (button, "clicked", G_CALLBACK (passwd_remember_button_cb),
+ data);
+
+ /* Add 'Not now' button */
+ alig = gtk_alignment_new (0, 0.5, 1, 0);
+
+ button = gtk_button_new_with_label (_("Not now"));
+ gtk_container_add (GTK_CONTAINER (alig), button);
+ gtk_box_pack_start (GTK_BOX (hbox), alig, FALSE, FALSE, 0);
+
+ g_signal_connect (button, "clicked", G_CALLBACK (passwd_not_now_button_cb),
+ data);
+
+ /* go! */
+ g_signal_handler_disconnect (data->info_bar, data->response_id);
+ g_signal_connect (data->info_bar, "response",
+ G_CALLBACK (remember_password_infobar_response_cb), data);
+
+ gtk_widget_show_all (data->info_bar);
+}
static void
provide_password_cb (GObject *tp_chat,
GAsyncResult *res,
gpointer user_data)
{
- EmpathyChat *self = EMPATHY_CHAT (user_data);
+ PasswordData *data = user_data;
+ EmpathyChat *self = data->self;
EmpathyChatPriv *priv = GET_PRIV (self);
GError *error = NULL;
- if (!empathy_tp_chat_provide_password_finish (EMPATHY_TP_CHAT (tp_chat), res,
+ if (!tp_channel_provide_password_finish (TP_CHANNEL (tp_chat), res,
&error)) {
DEBUG ("error: %s", error->message);
/* FIXME: what should we do if that's another error? Close the channel?
* Display the raw D-Bus error to the user isn't very useful */
- if (g_error_matches (error, TP_ERRORS, TP_ERROR_AUTHENTICATION_FAILED))
- display_password_info_bar (self, TRUE);
+ if (g_error_matches (error, TP_ERRORS, TP_ERROR_AUTHENTICATION_FAILED)) {
+ /* entry */
+ gtk_entry_set_text (GTK_ENTRY (data->entry), "");
+ gtk_widget_set_sensitive (data->entry, TRUE);
+ gtk_widget_grab_focus (data->entry);
+
+ /* info bar */
+ gtk_info_bar_set_message_type (
+ GTK_INFO_BAR (data->info_bar),
+ GTK_MESSAGE_ERROR);
+
+ /* button */
+ gtk_widget_set_sensitive (data->button, TRUE);
+ gtk_button_set_label (GTK_BUTTON (data->button),
+ _("Retry"));
+
+ /* label */
+ gtk_label_set_text (GTK_LABEL (data->label),
+ _("Wrong password; please try again:"));
+
+ /* spinner */
+ gtk_spinner_stop (GTK_SPINNER (data->spinner));
+ gtk_widget_hide (data->spinner);
+ }
g_error_free (error);
return;
}
+ if (empathy_keyring_is_available ()) {
+ /* ask whether they want to save the password */
+ chat_prompt_to_save_password (self, data);
+ } else {
+ /* Get rid of the password info bar finally */
+ gtk_widget_destroy (data->info_bar);
+ g_slice_free (PasswordData, data);
+ }
+
/* Room joined */
gtk_widget_set_sensitive (priv->hpaned, TRUE);
+ gtk_widget_set_sensitive (self->input_text_view, TRUE);
gtk_widget_grab_focus (self->input_text_view);
}
static void
password_infobar_response_cb (GtkWidget *info_bar,
gint response_id,
- EmpathyChat *self)
+ PasswordData *data)
{
- EmpathyChatPriv *priv = GET_PRIV (self);
- GtkWidget *entry;
+ EmpathyChatPriv *priv = GET_PRIV (data->self);
const gchar *password;
- if (response_id != GTK_RESPONSE_OK)
- goto out;
+ if (response_id != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (info_bar);
+ g_slice_free (PasswordData, data);
+ return;
+ }
- entry = g_object_get_data (G_OBJECT (info_bar), "password-entry");
- g_assert (entry != NULL);
+ password = gtk_entry_get_text (GTK_ENTRY (data->entry));
- password = gtk_entry_get_text (GTK_ENTRY (entry));
+ tp_channel_provide_password_async (TP_CHANNEL (priv->tp_chat), password,
+ provide_password_cb, data);
- empathy_tp_chat_provide_password_async (priv->tp_chat, password,
- provide_password_cb, self);
+ gtk_widget_set_sensitive (data->button, FALSE);
+ gtk_widget_set_sensitive (data->entry, FALSE);
- out:
- gtk_widget_destroy (info_bar);
+ gtk_spinner_start (GTK_SPINNER (data->spinner));
+ gtk_widget_show (data->spinner);
}
static void
password_entry_activate_cb (GtkWidget *entry,
- GtkWidget *info_bar)
+ PasswordData *data)
{
- gtk_info_bar_response (GTK_INFO_BAR (info_bar), GTK_RESPONSE_OK);
+ gtk_info_bar_response (GTK_INFO_BAR (data->info_bar), GTK_RESPONSE_OK);
}
static void
passwd_join_button_cb (GtkButton *button,
- GtkWidget *info_bar)
+ PasswordData *data)
+{
+ gtk_info_bar_response (GTK_INFO_BAR (data->info_bar), GTK_RESPONSE_OK);
+}
+
+static void
+clear_icon_released_cb (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkEvent *event,
+ PasswordData *data)
+{
+ gtk_entry_set_text (entry, "");
+}
+
+static void
+password_entry_changed_cb (GtkEditable *entry,
+ PasswordData *data)
+{
+ const gchar *str;
+
+ str = gtk_entry_get_text (GTK_ENTRY (entry));
+
+ gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
+ GTK_ENTRY_ICON_SECONDARY, !EMP_STR_EMPTY (str));
+}
+
+static void
+infobar_chat_invalidated_cb (TpProxy *proxy,
+ guint domain,
+ gint code,
+ gchar *message,
+ gpointer password_infobar)
{
- gtk_info_bar_response (GTK_INFO_BAR (info_bar), GTK_RESPONSE_OK);
+ /* Destroy the password infobar whenever a channel is invalidated
+ * so we don't have multiple infobars when the MUC is rejoined */
+ gtk_widget_destroy (GTK_WIDGET (password_infobar));
}
static void
-display_password_info_bar (EmpathyChat *self,
- gboolean retry)
+display_password_info_bar (EmpathyChat *self)
{
EmpathyChatPriv *priv = GET_PRIV (self);
GtkWidget *info_bar;
GtkWidget *entry;
GtkWidget *alig;
GtkWidget *button;
- GtkMessageType type;
- const gchar *msg, *button_label;
+ GtkWidget *spinner;
+ PasswordData *data;
- if (retry) {
- /* Previous password was wrong */
- type = GTK_MESSAGE_ERROR;
- msg = _("Wrong password; please try again:");
- button_label = _("Retry");
- }
- else {
- /* First time we're trying to join */
- type = GTK_MESSAGE_QUESTION;
- msg = _("This room is protected by a password:");
- button_label = _("Join");
- }
+ data = g_slice_new0 (PasswordData);
info_bar = gtk_info_bar_new ();
- gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), type);
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
+ GTK_MESSAGE_QUESTION);
content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
- hbox = gtk_hbox_new (FALSE, 3);
- gtk_container_add (GTK_CONTAINER (content_area), hbox);
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
/* Add image */
image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
/* Add message */
- label = gtk_label_new (msg);
+ label = gtk_label_new (_("This room is protected by a password:"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
/* Add password entry */
gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
+ gtk_entry_set_icon_from_stock (GTK_ENTRY (entry),
+ GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
+ gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
+ GTK_ENTRY_ICON_SECONDARY, FALSE);
+
+ g_signal_connect (entry, "icon-release",
+ G_CALLBACK (clear_icon_released_cb), data);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (password_entry_changed_cb), data);
+
g_signal_connect (entry, "activate",
- G_CALLBACK (password_entry_activate_cb), info_bar);
+ G_CALLBACK (password_entry_activate_cb), data);
/* Focus the password entry once it's realized */
g_signal_connect (entry, "realize", G_CALLBACK (gtk_widget_grab_focus), NULL);
/* Add 'Join' button */
- alig = gtk_alignment_new (0, 0.5, 0, 0);
+ alig = gtk_alignment_new (0, 0.5, 1, 0);
- button = gtk_button_new_with_label (button_label);
+ button = gtk_button_new_with_label (_("Join"));
gtk_container_add (GTK_CONTAINER (alig), button);
gtk_box_pack_start (GTK_BOX (hbox), alig, FALSE, FALSE, 0);
g_signal_connect (button, "clicked", G_CALLBACK (passwd_join_button_cb),
- info_bar);
+ data);
- g_object_set_data (G_OBJECT (info_bar), "password-entry", entry);
+ /* Add spinner */
+ spinner = gtk_spinner_new ();
+ gtk_box_pack_end (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
+
+ /* Save some data for messing around with later */
+ data->self = self;
+ data->info_bar = info_bar;
+ data->button = button;
+ data->label = label;
+ data->entry = entry;
+ data->spinner = spinner;
gtk_box_pack_start (GTK_BOX (priv->info_bar_vbox), info_bar,
- FALSE, FALSE, 3);
+ TRUE, TRUE, 3);
gtk_widget_show_all (hbox);
- g_signal_connect (info_bar, "response",
- G_CALLBACK (password_infobar_response_cb), self);
+ tp_g_signal_connect_object (priv->tp_chat,
+ "invalidated", G_CALLBACK (infobar_chat_invalidated_cb),
+ info_bar, 0);
+
+ data->response_id = g_signal_connect (info_bar, "response",
+ G_CALLBACK (password_infobar_response_cb), data);
gtk_widget_show_all (info_bar);
+ /* ... but hide the spinner */
+ gtk_widget_hide (spinner);
+
+ /* prevent the user from typing anything */
+ gtk_widget_set_sensitive (self->input_text_view, FALSE);
}
static void
-chat_password_needed_changed_cb (EmpathyChat *self)
+provide_saved_password_cb (GObject *tp_chat,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ EmpathyChat *self = user_data;
+ EmpathyChatPriv *priv = GET_PRIV (self);
+ GError *error = NULL;
+
+ if (!tp_channel_provide_password_finish (TP_CHANNEL (tp_chat), res,
+ &error)) {
+ DEBUG ("error: %s", error->message);
+ /* FIXME: what should we do if that's another error? Close the channel?
+ * Display the raw D-Bus error to the user isn't very useful */
+ if (g_error_matches (error, TP_ERRORS, TP_ERROR_AUTHENTICATION_FAILED)) {
+ display_password_info_bar (self);
+ gtk_widget_set_sensitive (priv->hpaned, FALSE);
+ }
+ g_error_free (error);
+ return;
+ }
+
+ /* Room joined */
+ gtk_widget_set_sensitive (priv->hpaned, TRUE);
+ gtk_widget_grab_focus (self->input_text_view);
+}
+
+static void
+chat_room_got_password_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
+ EmpathyChat *self = user_data;
EmpathyChatPriv *priv = GET_PRIV (self);
+ const gchar *password;
+ GError *error = NULL;
+
+ password = empathy_keyring_get_room_password_finish (priv->account,
+ result, &error);
- if (empathy_tp_chat_password_needed (priv->tp_chat)) {
- display_password_info_bar (self, FALSE);
+ if (error != NULL) {
+ DEBUG ("Couldn't get room password: %s\n", error->message);
+ g_clear_error (&error);
+
+ display_password_info_bar (self);
gtk_widget_set_sensitive (priv->hpaned, FALSE);
+ return;
+ }
+
+ tp_channel_provide_password_async (TP_CHANNEL (priv->tp_chat), password,
+ provide_saved_password_cb, self);
+}
+
+static void
+chat_password_needed_changed_cb (EmpathyChat *self)
+{
+ EmpathyChatPriv *priv = GET_PRIV (self);
+
+ if (tp_channel_password_needed (TP_CHANNEL (priv->tp_chat))) {
+ empathy_keyring_get_room_password_async (priv->account,
+ empathy_tp_chat_get_id (priv->tp_chat),
+ chat_room_got_password_cb, self);
}
}
+static void
+chat_sms_channel_changed_cb (EmpathyChat *self)
+{
+ EmpathyChatPriv *priv = GET_PRIV (self);
+
+ priv->sms_channel = tp_text_channel_is_sms_channel (
+ (TpTextChannel *) priv->tp_chat);
+ g_object_notify (G_OBJECT (self), "sms-channel");
+}
+
+static void
+chat_n_messages_sending_changed_cb (EmpathyChat *self)
+{
+ g_object_notify (G_OBJECT (self), "n-messages-sending");
+}
+
void
empathy_chat_set_tp_chat (EmpathyChat *chat,
EmpathyTpChat *tp_chat)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- TpConnection *connection;
- GPtrArray *properties;
g_return_if_fail (EMPATHY_IS_CHAT (chat));
g_return_if_fail (EMPATHY_IS_TP_CHAT (tp_chat));
- g_return_if_fail (empathy_tp_chat_is_ready (tp_chat));
if (priv->tp_chat) {
return;
}
priv->tp_chat = g_object_ref (tp_chat);
- connection = empathy_tp_chat_get_connection (priv->tp_chat);
- priv->account = g_object_ref (empathy_get_account_for_connection (connection));
+ priv->account = g_object_ref (empathy_tp_chat_get_account (priv->tp_chat));
- g_signal_connect (tp_chat, "destroy",
- G_CALLBACK (chat_destroy_cb),
+ g_signal_connect (tp_chat, "invalidated",
+ G_CALLBACK (chat_invalidated_cb),
chat);
- g_signal_connect (tp_chat, "message-received",
+ g_signal_connect (tp_chat, "message-received-empathy",
G_CALLBACK (chat_message_received_cb),
chat);
+ g_signal_connect (tp_chat, "message_acknowledged",
+ G_CALLBACK (chat_message_acknowledged_cb),
+ chat);
g_signal_connect (tp_chat, "send-error",
G_CALLBACK (chat_send_error_cb),
chat);
- g_signal_connect (tp_chat, "chat-state-changed",
+ g_signal_connect (tp_chat, "chat-state-changed-empathy",
G_CALLBACK (chat_state_changed_cb),
chat);
- g_signal_connect (tp_chat, "property-changed",
- G_CALLBACK (chat_property_changed_cb),
- chat);
g_signal_connect (tp_chat, "members-changed",
G_CALLBACK (chat_members_changed_cb),
chat);
g_signal_connect (tp_chat, "member-renamed",
G_CALLBACK (chat_member_renamed_cb),
chat);
+ g_signal_connect_swapped (tp_chat, "notify::self-contact",
+ G_CALLBACK (chat_self_contact_changed_cb),
+ chat);
g_signal_connect_swapped (tp_chat, "notify::remote-contact",
G_CALLBACK (chat_remote_contact_changed_cb),
chat);
g_signal_connect_swapped (tp_chat, "notify::password-needed",
G_CALLBACK (chat_password_needed_changed_cb),
chat);
+ g_signal_connect_swapped (tp_chat, "notify::is-sms-channel",
+ G_CALLBACK (chat_sms_channel_changed_cb),
+ chat);
+ g_signal_connect_swapped (tp_chat, "notify::n-messages-sending",
+ G_CALLBACK (chat_n_messages_sending_changed_cb),
+ chat);
+ g_signal_connect_swapped (tp_chat, "notify::title",
+ G_CALLBACK (chat_title_changed_cb),
+ chat);
+ g_signal_connect_swapped (tp_chat, "notify::subject",
+ G_CALLBACK (chat_subject_changed_cb),
+ chat);
/* Get initial value of properties */
- properties = empathy_tp_chat_get_properties (priv->tp_chat);
- if (properties != NULL) {
- guint i;
-
- for (i = 0; i < properties->len; i++) {
- EmpathyTpChatProperty *property;
-
- property = g_ptr_array_index (properties, i);
- if (property->value == NULL)
- continue;
-
- chat_property_changed_cb (priv->tp_chat,
- property->name,
- property->value,
- chat);
- }
- }
-
+ chat_sms_channel_changed_cb (chat);
+ chat_self_contact_changed_cb (chat);
chat_remote_contact_changed_cb (chat);
+ chat_title_changed_cb (chat);
+ chat_subject_changed_cb (chat);
if (chat->input_text_view) {
gtk_widget_set_sensitive (chat->input_text_view, TRUE);
return priv->id;
}
-const gchar *
-empathy_chat_get_name (EmpathyChat *chat)
+gchar *
+empathy_chat_dup_name (EmpathyChat *chat)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
const gchar *ret;
g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL);
ret = priv->name;
+
if (!ret && priv->remote_contact) {
- ret = empathy_contact_get_name (priv->remote_contact);
+ ret = empathy_contact_get_alias (priv->remote_contact);
}
if (!ret)
ret = priv->id;
- return ret ? ret : _("Conversation");
+ if (!ret)
+ ret = _("Conversation");
+
+ if (priv->sms_channel)
+ /* Translators: this string is a something like
+ * "Escher Cat (SMS)" */
+ return g_strdup_printf (_("%s (SMS)"), ret);
+ else
+ return g_strdup (ret);
}
const gchar *
{
EmpathyChatPriv *priv = GET_PRIV (chat);
GtkWidget *menu = NULL;
+ FolksIndividual *individual;
+ TpContact *contact;
g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL);
- if (priv->remote_contact) {
- menu = empathy_contact_menu_new (priv->remote_contact,
- EMPATHY_CONTACT_FEATURE_CALL |
- EMPATHY_CONTACT_FEATURE_LOG |
- EMPATHY_CONTACT_FEATURE_INFO);
- }
- else if (priv->contact_list_view) {
- EmpathyContactListView *view;
+ if (priv->remote_contact == NULL)
+ return NULL;
- view = EMPATHY_CONTACT_LIST_VIEW (priv->contact_list_view);
- menu = empathy_contact_list_view_get_contact_menu (view);
- }
+ contact = empathy_contact_get_tp_contact (priv->remote_contact);
+ if (contact == NULL)
+ return NULL;
+
+ individual = empathy_ensure_individual_from_tp_contact (contact);
+
+ if (individual == NULL)
+ return NULL;
+
+ menu = empathy_individual_menu_new (individual,
+ EMPATHY_INDIVIDUAL_FEATURE_CALL |
+ EMPATHY_INDIVIDUAL_FEATURE_LOG |
+ EMPATHY_INDIVIDUAL_FEATURE_INFO |
+ EMPATHY_INDIVIDUAL_FEATURE_BLOCK, NULL);
+
+ g_object_unref (individual);
return menu;
}
gtk_text_buffer_copy_clipboard (buffer, clipboard);
}
+ else {
+ gint start_offset;
+ gint end_offset;
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ if (gtk_label_get_selection_bounds (GTK_LABEL (priv->label_topic),
+ &start_offset,
+ &end_offset)) {
+ gchar *start;
+ gchar *end;
+ gchar *selection;
+ const gchar *topic;
+ GtkClipboard *clipboard;
+
+ topic = gtk_label_get_text (GTK_LABEL (priv->label_topic));
+ start = g_utf8_offset_to_pointer (topic, start_offset);
+ end = g_utf8_offset_to_pointer (topic, end_offset);
+ selection = g_strndup (start, end - start);
+
+ clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clipboard, selection, -1);
+
+ g_free (selection);
+ }
+ }
}
void
priv = GET_PRIV (chat);
+ if (gtk_widget_get_visible (priv->search_bar)) {
+ empathy_search_bar_paste_clipboard (EMPATHY_SEARCH_BAR (priv->search_bar));
+ return;
+ }
+
if (priv->tp_chat == NULL ||
!gtk_widget_is_sensitive (chat->input_text_view))
return;
return (priv->handle_type == TP_HANDLE_TYPE_ROOM);
}
+gboolean
+empathy_chat_is_highlighted (EmpathyChat *chat)
+{
+ EmpathyChatPriv *priv = GET_PRIV (chat);
+
+ g_return_val_if_fail (EMPATHY_IS_CHAT (chat), FALSE);
+
+ return priv->highlighted;
+}
+
guint
empathy_chat_get_nb_unread_messages (EmpathyChat *self)
{
if (priv->retrieving_backlogs)
return;
- if (priv->tp_chat != NULL ) {
- empathy_tp_chat_acknowledge_all_messages (priv->tp_chat);
+ if (priv->tp_chat != NULL) {
+ tp_text_channel_ack_all_pending_messages_async (
+ TP_TEXT_CHANNEL (priv->tp_chat), NULL, NULL);
+ }
+
+ priv->highlighted = FALSE;
+
+ if (priv->unread_messages_when_offline > 0) {
+ /* We can't ack those as the connection has gone away so just consider
+ * them as read. */
+ priv->unread_messages -= priv->unread_messages_when_offline;
+ g_object_notify (G_OBJECT (self), "nb-unread-messages");
+ priv->unread_messages_when_offline = 0;
+ }
+}
+
+/* Return TRUE if on of the contacts in this chat is composing */
+gboolean
+empathy_chat_is_composing (EmpathyChat *chat)
+{
+ return chat->priv->compositors != NULL;
+}
+
+gboolean
+empathy_chat_is_sms_channel (EmpathyChat *self)
+{
+ EmpathyChatPriv *priv = GET_PRIV (self);
+
+ g_return_val_if_fail (EMPATHY_IS_CHAT (self), 0);
+
+ return priv->sms_channel;
+}
+
+guint
+empathy_chat_get_n_messages_sending (EmpathyChat *self)
+{
+ EmpathyChatPriv *priv;
+
+ g_return_val_if_fail (EMPATHY_IS_CHAT (self), 0);
+
+ priv = GET_PRIV (self);
+
+ if (priv->tp_chat == NULL) {
+ return 0;
+ } else {
+ guint n_messages;
+
+ g_object_get (priv->tp_chat,
+ "n-messages-sending", &n_messages,
+ NULL);
+
+ return n_messages;
}
- priv->unread_messages = 0;
+}
+
+gchar *
+empathy_chat_dup_text (EmpathyChat *self)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->input_text_view));
+
+ gtk_text_buffer_get_bounds (buffer, &start, &end);
+ return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+}
+
+void
+empathy_chat_set_text (EmpathyChat *self,
+ const gchar *text)
+{
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self->input_text_view));
+
+ gtk_text_buffer_set_text (buffer, text, -1);
}