<commit_before>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 2003-2007 Imendio AB
 * Copyright (C) 2007-2008 Collabora Ltd.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 *
 * Authors: <NAME> <<EMAIL>>
 *          <NAME> <<EMAIL>>
 *          <NAME> <<EMAIL>>
 *          <NAME> <<EMAIL>>
 *          <NAME> <<EMAIL>>
 */

#include <config.h>

#include <string.h>

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n.h>
#include <libnotify/notification.h>

#include <telepathy-glib/util.h>
#include <libmissioncontrol/mission-control.h>

#include <libempathy/empathy-contact.h>
#include <libempathy/empathy-message.h>
#include <libempathy/empathy-dispatcher.h>
#include <libempathy/empathy-chatroom-manager.h>
#include <libempathy/empathy-account-manager.h>
#include <libempathy/empathy-utils.h>

#include <libempathy-gtk/empathy-images.h>
#include <libempathy-gtk/empathy-conf.h>
#include <libempathy-gtk/empathy-contact-dialogs.h>
#include <libempathy-gtk/empathy-log-window.h>
#include <libempathy-gtk/empathy-geometry.h>
#include <libempathy-gtk/empathy-smiley-manager.h>
#include <libempathy-gtk/empathy-ui-utils.h>

#include "empathy-chat-window.h"
#include "empathy-about-dialog.h"
#include "empathy-misc.h"

#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
#include <libempathy/empathy-debug.h>

#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
typedef struct {
	EmpathyChat *current_chat;
	GList       *chats;
	GList       *chats_new_msg;
	GList       *chats_composing;
	gboolean     page_added;
	gboolean     dnd_same_window;
	guint        save_geometry_id;
	EmpathyChatroomManager *chatroom_manager;
	GtkWidget   *dialog;
	GtkWidget   *notebook;
	NotifyNotification *notification;

	/* Menu items. */
	GtkUIManager *ui_manager;
	GtkAction   *menu_conv_insert_smiley;
	GtkAction   *menu_conv_favorite;

	GtkAction   *menu_edit_cut;
	GtkAction   *menu_edit_copy;
	GtkAction   *menu_edit_paste;

	GtkAction   *menu_tabs_next;
	GtkAction   *menu_tabs_prev;
	GtkAction   *menu_tabs_left;
	GtkAction   *menu_tabs_right;
	GtkAction   *menu_tabs_detach;
} EmpathyChatWindowPriv;

static GList *chat_windows = NULL;

static const guint tab_accel_keys[] = {
	GDK_1, GDK_2, GDK_3, GDK_4, GDK_5,
	GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
};

typedef enum {
	DND_DRAG_TYPE_CONTACT_ID,
	DND_DRAG_TYPE_TAB
} DndDragType;

static const GtkTargetEntry drag_types_dest[] = {
	{ "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
	{ "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
};

G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);

static void
chat_window_accel_cb (GtkAccelGroup    *accelgroup,
		      GObject          *object,
		      guint             key,
		      GdkModifierType   mod,
		      EmpathyChatWindow *window)
{
	EmpathyChatWindowPriv *priv;
	gint                  num = -1;
	gint                  i;

	priv = GET_PRIV (window);

	for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
		if (tab_accel_keys[i] == key) {
			num = i;
			break;
		}
	}

	if (num != -1) {
		gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
	}
}

static EmpathyChatWindow *
chat_window_find_chat (EmpathyChat *chat)
{
	EmpathyChatWindowPriv *priv;
	GList                 *l, *ll;

	for (l = chat_windows; l; l = l->next) {
		priv = GET_PRIV (l->data);
		ll = g_list_find (priv->chats, chat);
		if (ll) {
			return l->data;
		}
	}

	return NULL;
}

static void
chat_window_close_clicked_cb (GtkAction   *action,
			      EmpathyChat *chat)
{
	EmpathyChatWindow *window;

	window = chat_window_find_chat (chat);
	empathy_chat_window_remove_chat (window, chat);
}

static void
chat_tab_style_set_cb (GtkWidget *hbox,
				       GtkStyle  *previous_style,
				       gpointer   user_data)
{
	GtkWidget *button;
	int char_width, h, w;
	PangoContext *context;
	PangoFontMetrics *metrics;

	button = g_object_get_data (G_OBJECT (user_data),
		"chat-window-tab-close-button");
	context = gtk_widget_get_pango_context (hbox);

	metrics = pango_context_get_metrics (context, hbox->style->font_desc,
		pango_context_get_language (context));
	char_width = pango_font_metrics_get_approximate_char_width (metrics);
	pango_font_metrics_unref (metrics);

	gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
					   GTK_ICON_SIZE_MENU, &w, &h);

	/* Request at least about 12 chars width plus at least space for the status
	 * image and the close button */
	gtk_widget_set_size_request (hbox,
		12 * PANGO_PIXELS (char_width) + 2 * w, -1);

	gtk_widget_set_size_request (button, w, h);
}

static GtkWidget *
chat_window_create_label (EmpathyChatWindow *window,
			  EmpathyChat       *chat,
			  gboolean           is_tab_label)
{
	EmpathyChatWindowPriv *priv;
	GtkWidget            *hbox;
	GtkWidget            *name_label;
	GtkWidget            *status_image;
	GtkWidget            *close_button;
	GtkWidget            *close_image;
	GtkWidget            *event_box;
	GtkWidget            *event_box_hbox;
	PangoAttrList        *attr_list;
	PangoAttribute       *attr;

	priv = GET_PRIV (window);

	/* The spacing between the button and the label. */
	hbox = gtk_hbox_new (FALSE, 0);

	event_box = gtk_event_box_new ();
	gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);

	name_label = gtk_label_new (NULL);
	if (is_tab_label)
		gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);

	attr_list = pango_attr_list_new ();
	attr = pango_attr_scale_new (1/1.2);
	attr->start_index = 0;
	attr->end_index = -1;
	pango_attr_list_insert (attr_list, attr);
	gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
	pango_attr_list_unref (attr_list);

	gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
	gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
	g_object_set_data (G_OBJECT (chat),
		is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
		name_label);

	status_image = gtk_image_new ();

	/* Spacing between the icon and label. */
	event_box_hbox = gtk_hbox_new (FALSE, 0);

	gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
	gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);

	g_object_set_data (G_OBJECT (chat),
		is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
		status_image);
	g_object_set_data (G_OBJECT (chat),
		is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
		event_box);

	gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
	gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);

	if (is_tab_label) {
		close_button = gtk_button_new ();
		gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
		g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);

		/* We don't want focus/keynav for the button to avoid clutter, and
		 * Ctrl-W works anyway.
		 */
		GTK_WIDGET_UNSET_FLAGS (close_button, GTK_CAN_FOCUS);
		GTK_WIDGET_UNSET_FLAGS (close_button, GTK_CAN_DEFAULT);

		/* Set the name to make the special rc style match. */
		gtk_widget_set_name (close_button, "empathy-close-button");

		close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);

		gtk_container_add (GTK_CONTAINER (close_button), close_image);

		gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);

		g_signal_connect (close_button,
				  "clicked",
				  G_CALLBACK (chat_window_close_clicked_cb),
				  chat);

		/* React to theme changes and also setup the size correctly.  */
		g_signal_connect (hbox,
				  "style-set",
				  G_CALLBACK (chat_tab_style_set_cb),
				  chat);
	}

	gtk_widget_show_all (hbox);

	return hbox;
}

static void
chat_window_update (EmpathyChatWindow *window)
{
	EmpathyChatWindowPriv *priv = GET_PRIV (window);
	gboolean               first_page;
	gboolean               last_page;
	gboolean               is_connected;
	gint                   num_pages;
	gint                   page_num;
	gint                   i;
	const gchar           *name;
	guint                  n_chats;
	GdkPixbuf             *icon;
	EmpathyContact        *remote_contact;
	gboolean               avatar_in_icon;
	GtkWidget             *chat;
	GtkWidget             *chat_close_button;
	GtkWidget             *submenu;
	GtkWidget             *menu;

	/* Get information */
	page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
	num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
	first_page = (page_num == 0);
	last_page = (page_num == (num_pages - 1));
	is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
	name = empathy_chat_get_name (priv->current_chat);
	n_chats = g_list_length (priv->chats);

	DEBUG ("Update window");

	/* Update menu */
	gtk_action_set_sensitive (priv->menu_tabs_next, !last_page);
	gtk_action_set_sensitive (priv->menu_tabs_prev, !first_page);
	gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
	gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
	gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
	gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);

	/* Update Contact menu */
	menu = gtk_ui_manager_get_widget (priv->ui_manager,
		"/chats_menubar/menu_contact");
	submenu = empathy_chat_get_contact_menu (priv->current_chat);
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
	gtk_widget_show (menu);

	/* Update window title */
	if (n_chats == 1) {
		gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
	} else {
		gchar *title;

		title = g_strdup_printf (_("Conversations (%d)"), n_chats);
		gtk_window_set_title (GTK_WINDOW (priv->dialog), title);
		g_free (title);
	}

	/* Update window icon */
	if (priv->chats_new_msg) {
		gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
					  EMPATHY_IMAGE_MESSAGE);
	} else {
		empathy_conf_get_bool (empathy_conf_get (),
				       EMPATHY_PREFS_CHAT_AVATAR_IN_ICON,
				       &avatar_in_icon);

		if (n_chats == 1 && avatar_in_icon) {
			remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
			icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
			gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);

			if (icon != NULL) {
				g_object_unref (icon);
			}
		} else {
			gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
		}
	}

	if (num_pages == 1) {
		chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
		chat_close_button = g_object_get_data (G_OBJECT (chat),
				"chat-window-tab-close-button");
		gtk_widget_hide (chat_close_button);
	} else {
		for (i=0; i<num_pages; i++) {
			chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
			chat_close_button = g_object_get_data (G_OBJECT (chat),
					"chat-window-tab-close-button");
			gtk_widget_show (chat_close_button);
		}
	}
}

static void
chat_window_update_chat_tab (EmpathyChat *chat)
{
	EmpathyChatWindow     *window;
	EmpathyChatWindowPriv *priv;
	EmpathyContact        *remote_contact;
	const gchar           *name;
	McAccount             *account;
	const gchar           *subject;
	GtkWidget             *widget;
	GString               *tooltip;
	gchar                 *markup;
	const gchar           *icon_name;

	window = chat_window_find_chat (chat);
	if (!window) {
		return;
	}
	priv = GET_PRIV (window);

	/* Get information */
	name = empathy_chat_get_name (chat);
	account = empathy_chat_get_account (chat);
	subject = empathy_chat_get_subject (chat);
	remote_contact = empathy_chat_get_remote_contact (chat);

	DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
		name, mc_account_get_unique_name (account), subject, remote_contact);

	/* Update tab image */
	if (g_list_find (priv->chats_new_msg, chat)) {
		icon_name = EMPATHY_IMAGE_MESSAGE;
	}
	else if (g_list_find (priv->chats_composing, chat)) {
		icon_name = EMPATHY_IMAGE_TYPING;
	}
	else if (remote_contact) {
		icon_name = empathy_icon_name_for_contact (remote_contact);
	} else {
		icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
	}
	widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
	gtk_image_set_from_icon_name (GTK_IMAGE (widget), icon_name, GTK_ICON_SIZE_MENU);
	widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
	gtk_image_set_from_icon_name (GTK_IMAGE (widget), icon_name, GTK_ICON_SIZE_MENU);

	/* Update tab tooltip */
	tooltip = g_string_new (NULL);

	if (remote_contact) {
		markup = g_markup_printf_escaped ("<b>%s</b><small> (%s)</small>\n%s",
						  empathy_contact_get_id (remote_contact),
						  mc_account_get_display_name (account),
						  empathy_contact_get_status (remote_contact));
		g_string_append (tooltip, markup);
		g_free (markup);
	}
	else {
		markup = g_markup_printf_escaped ("<b>%s</b><small>  (%s)</small>", name,
						  mc_account_get_display_name (account));
		g_string_append (tooltip, markup);
		g_free (markup);
	}

	if (subject) {
		markup = g_markup_printf_escaped ("\n<b>%s</b> %s", _("Topic:"), subject);
		g_string_append (tooltip, markup);
		g_free (markup);
	}
	if (g_list_find (priv->chats_composing, chat)) {
		markup = g_markup_printf_escaped ("\n%s", _("Typing a message."));
		g_string_append (tooltip, markup);
		g_free (markup);
	}

	markup = g_string_free (tooltip, FALSE);
	widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
	gtk_widget_set_tooltip_markup (widget, markup);
	widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
	gtk_widget_set_tooltip_markup (widget, markup);
	g_free (markup);<commit_msg>Only show status in tooltip when it exists<commit_after>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 2003-2007 Imendio AB
 * Copyright (C) 2007-2008 Collabora Ltd.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 *
 * Authors: <NAME> <<EMAIL>>
 *          <NAME> <<EMAIL>>
 *          <NAME> <<EMAIL>>
 *          <NAME> <<EMAIL>>
 *          <NAME> <<EMAIL>>
 */

#include <config.h>

#include <string.h>

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n.h>
#include <libnotify/notification.h>

#include <telepathy-glib/util.h>
#include <libmissioncontrol/mission-control.h>

#include <libempathy/empathy-contact.h>
#include <libempathy/empathy-message.h>
#include <libempathy/empathy-dispatcher.h>
#include <libempathy/empathy-chatroom-manager.h>
#include <libempathy/empathy-account-manager.h>
#include <libempathy/empathy-utils.h>

#include <libempathy-gtk/empathy-images.h>
#include <libempathy-gtk/empathy-conf.h>
#include <libempathy-gtk/empathy-contact-dialogs.h>
#include <libempathy-gtk/empathy-log-window.h>
#include <libempathy-gtk/empathy-geometry.h>
#include <libempathy-gtk/empathy-smiley-manager.h>
#include <libempathy-gtk/empathy-ui-utils.h>

#include "empathy-chat-window.h"
#include "empathy-about-dialog.h"
#include "empathy-misc.h"

#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
#include <libempathy/empathy-debug.h>

#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyChatWindow)
typedef struct {
	EmpathyChat *current_chat;
	GList       *chats;
	GList       *chats_new_msg;
	GList       *chats_composing;
	gboolean     page_added;
	gboolean     dnd_same_window;
	guint        save_geometry_id;
	EmpathyChatroomManager *chatroom_manager;
	GtkWidget   *dialog;
	GtkWidget   *notebook;
	NotifyNotification *notification;

	/* Menu items. */
	GtkUIManager *ui_manager;
	GtkAction   *menu_conv_insert_smiley;
	GtkAction   *menu_conv_favorite;

	GtkAction   *menu_edit_cut;
	GtkAction   *menu_edit_copy;
	GtkAction   *menu_edit_paste;

	GtkAction   *menu_tabs_next;
	GtkAction   *menu_tabs_prev;
	GtkAction   *menu_tabs_left;
	GtkAction   *menu_tabs_right;
	GtkAction   *menu_tabs_detach;
} EmpathyChatWindowPriv;

static GList *chat_windows = NULL;

static const guint tab_accel_keys[] = {
	GDK_1, GDK_2, GDK_3, GDK_4, GDK_5,
	GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
};

typedef enum {
	DND_DRAG_TYPE_CONTACT_ID,
	DND_DRAG_TYPE_TAB
} DndDragType;

static const GtkTargetEntry drag_types_dest[] = {
	{ "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
	{ "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB },
};

G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);

static void
chat_window_accel_cb (GtkAccelGroup    *accelgroup,
		      GObject          *object,
		      guint             key,
		      GdkModifierType   mod,
		      EmpathyChatWindow *window)
{
	EmpathyChatWindowPriv *priv;
	gint                  num = -1;
	gint                  i;

	priv = GET_PRIV (window);

	for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
		if (tab_accel_keys[i] == key) {
			num = i;
			break;
		}
	}

	if (num != -1) {
		gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
	}
}

static EmpathyChatWindow *
chat_window_find_chat (EmpathyChat *chat)
{
	EmpathyChatWindowPriv *priv;
	GList                 *l, *ll;

	for (l = chat_windows; l; l = l->next) {
		priv = GET_PRIV (l->data);
		ll = g_list_find (priv->chats, chat);
		if (ll) {
			return l->data;
		}
	}

	return NULL;
}

static void
chat_window_close_clicked_cb (GtkAction   *action,
			      EmpathyChat *chat)
{
	EmpathyChatWindow *window;

	window = chat_window_find_chat (chat);
	empathy_chat_window_remove_chat (window, chat);
}

static void
chat_tab_style_set_cb (GtkWidget *hbox,
				       GtkStyle  *previous_style,
				       gpointer   user_data)
{
	GtkWidget *button;
	int char_width, h, w;
	PangoContext *context;
	PangoFontMetrics *metrics;

	button = g_object_get_data (G_OBJECT (user_data),
		"chat-window-tab-close-button");
	context = gtk_widget_get_pango_context (hbox);

	metrics = pango_context_get_metrics (context, hbox->style->font_desc,
		pango_context_get_language (context));
	char_width = pango_font_metrics_get_approximate_char_width (metrics);
	pango_font_metrics_unref (metrics);

	gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button),
					   GTK_ICON_SIZE_MENU, &w, &h);

	/* Request at least about 12 chars width plus at least space for the status
	 * image and the close button */
	gtk_widget_set_size_request (hbox,
		12 * PANGO_PIXELS (char_width) + 2 * w, -1);

	gtk_widget_set_size_request (button, w, h);
}

static GtkWidget *
chat_window_create_label (EmpathyChatWindow *window,
			  EmpathyChat       *chat,
			  gboolean           is_tab_label)
{
	EmpathyChatWindowPriv *priv;
	GtkWidget            *hbox;
	GtkWidget            *name_label;
	GtkWidget            *status_image;
	GtkWidget            *close_button;
	GtkWidget            *close_image;
	GtkWidget            *event_box;
	GtkWidget            *event_box_hbox;
	PangoAttrList        *attr_list;
	PangoAttribute       *attr;

	priv = GET_PRIV (window);

	/* The spacing between the button and the label. */
	hbox = gtk_hbox_new (FALSE, 0);

	event_box = gtk_event_box_new ();
	gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);

	name_label = gtk_label_new (NULL);
	if (is_tab_label)
		gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END);

	attr_list = pango_attr_list_new ();
	attr = pango_attr_scale_new (1/1.2);
	attr->start_index = 0;
	attr->end_index = -1;
	pango_attr_list_insert (attr_list, attr);
	gtk_label_set_attributes (GTK_LABEL (name_label), attr_list);
	pango_attr_list_unref (attr_list);

	gtk_misc_set_padding (GTK_MISC (name_label), 2, 0);
	gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5);
	g_object_set_data (G_OBJECT (chat),
		is_tab_label ? "chat-window-tab-label" : "chat-window-menu-label",
		name_label);

	status_image = gtk_image_new ();

	/* Spacing between the icon and label. */
	event_box_hbox = gtk_hbox_new (FALSE, 0);

	gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0);
	gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0);

	g_object_set_data (G_OBJECT (chat),
		is_tab_label ? "chat-window-tab-image" : "chat-window-menu-image",
		status_image);
	g_object_set_data (G_OBJECT (chat),
		is_tab_label ? "chat-window-tab-tooltip-widget" : "chat-window-menu-tooltip-widget",
		event_box);

	gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox);
	gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);

	if (is_tab_label) {
		close_button = gtk_button_new ();
		gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
		g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);

		/* We don't want focus/keynav for the button to avoid clutter, and
		 * Ctrl-W works anyway.
		 */
		GTK_WIDGET_UNSET_FLAGS (close_button, GTK_CAN_FOCUS);
		GTK_WIDGET_UNSET_FLAGS (close_button, GTK_CAN_DEFAULT);

		/* Set the name to make the special rc style match. */
		gtk_widget_set_name (close_button, "empathy-close-button");

		close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);

		gtk_container_add (GTK_CONTAINER (close_button), close_image);

		gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);

		g_signal_connect (close_button,
				  "clicked",
				  G_CALLBACK (chat_window_close_clicked_cb),
				  chat);

		/* React to theme changes and also setup the size correctly.  */
		g_signal_connect (hbox,
				  "style-set",
				  G_CALLBACK (chat_tab_style_set_cb),
				  chat);
	}

	gtk_widget_show_all (hbox);

	return hbox;
}

static void
chat_window_update (EmpathyChatWindow *window)
{
	EmpathyChatWindowPriv *priv = GET_PRIV (window);
	gboolean               first_page;
	gboolean               last_page;
	gboolean               is_connected;
	gint                   num_pages;
	gint                   page_num;
	gint                   i;
	const gchar           *name;
	guint                  n_chats;
	GdkPixbuf             *icon;
	EmpathyContact        *remote_contact;
	gboolean               avatar_in_icon;
	GtkWidget             *chat;
	GtkWidget             *chat_close_button;
	GtkWidget             *submenu;
	GtkWidget             *menu;

	/* Get information */
	page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
	num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
	first_page = (page_num == 0);
	last_page = (page_num == (num_pages - 1));
	is_connected = empathy_chat_get_tp_chat (priv->current_chat) != NULL;
	name = empathy_chat_get_name (priv->current_chat);
	n_chats = g_list_length (priv->chats);

	DEBUG ("Update window");

	/* Update menu */
	gtk_action_set_sensitive (priv->menu_tabs_next, !last_page);
	gtk_action_set_sensitive (priv->menu_tabs_prev, !first_page);
	gtk_action_set_sensitive (priv->menu_tabs_detach, num_pages > 1);
	gtk_action_set_sensitive (priv->menu_tabs_left, !first_page);
	gtk_action_set_sensitive (priv->menu_tabs_right, !last_page);
	gtk_action_set_sensitive (priv->menu_conv_insert_smiley, is_connected);

	/* Update Contact menu */
	menu = gtk_ui_manager_get_widget (priv->ui_manager,
		"/chats_menubar/menu_contact");
	submenu = empathy_chat_get_contact_menu (priv->current_chat);
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), submenu);
	gtk_widget_show (menu);

	/* Update window title */
	if (n_chats == 1) {
		gtk_window_set_title (GTK_WINDOW (priv->dialog), name);
	} else {
		gchar *title;

		title = g_strdup_printf (_("Conversations (%d)"), n_chats);
		gtk_window_set_title (GTK_WINDOW (priv->dialog), title);
		g_free (title);
	}

	/* Update window icon */
	if (priv->chats_new_msg) {
		gtk_window_set_icon_name (GTK_WINDOW (priv->dialog),
					  EMPATHY_IMAGE_MESSAGE);
	} else {
		empathy_conf_get_bool (empathy_conf_get (),
				       EMPATHY_PREFS_CHAT_AVATAR_IN_ICON,
				       &avatar_in_icon);

		if (n_chats == 1 && avatar_in_icon) {
			remote_contact = empathy_chat_get_remote_contact (priv->current_chat);
			icon = empathy_pixbuf_avatar_from_contact_scaled (remote_contact, 0, 0);
			gtk_window_set_icon (GTK_WINDOW (priv->dialog), icon);

			if (icon != NULL) {
				g_object_unref (icon);
			}
		} else {
			gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL);
		}
	}

	if (num_pages == 1) {
		chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 0);
		chat_close_button = g_object_get_data (G_OBJECT (chat),
				"chat-window-tab-close-button");
		gtk_widget_hide (chat_close_button);
	} else {
		for (i=0; i<num_pages; i++) {
			chat = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), i);
			chat_close_button = g_object_get_data (G_OBJECT (chat),
					"chat-window-tab-close-button");
			gtk_widget_show (chat_close_button);
		}
	}
}

static void
chat_window_update_chat_tab (EmpathyChat *chat)
{
	EmpathyChatWindow     *window;
	EmpathyChatWindowPriv *priv;
	EmpathyContact        *remote_contact;
	const gchar           *name;
	McAccount             *account;
	const gchar           *subject;
	const gchar           *status;
	GtkWidget             *widget;
	GString               *tooltip;
	gchar                 *markup;
	const gchar           *icon_name;

	window = chat_window_find_chat (chat);
	if (!window) {
		return;
	}
	priv = GET_PRIV (window);

	/* Get information */
	name = empathy_chat_get_name (chat);
	account = empathy_chat_get_account (chat);
	subject = empathy_chat_get_subject (chat);
	remote_contact = empathy_chat_get_remote_contact (chat);

	DEBUG ("Updating chat tab, name=%s, account=%s, subject=%s, remote_contact=%p",
		name, mc_account_get_unique_name (account), subject, remote_contact);

	/* Update tab image */
	if (g_list_find (priv->chats_new_msg, chat)) {
		icon_name = EMPATHY_IMAGE_MESSAGE;
	}
	else if (g_list_find (priv->chats_composing, chat)) {
		icon_name = EMPATHY_IMAGE_TYPING;
	}
	else if (remote_contact) {
		icon_name = empathy_icon_name_for_contact (remote_contact);
	} else {
		icon_name = EMPATHY_IMAGE_GROUP_MESSAGE;
	}
	widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image");
	gtk_image_set_from_icon_name (GTK_IMAGE (widget), icon_name, GTK_ICON_SIZE_MENU);
	widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-image");
	gtk_image_set_from_icon_name (GTK_IMAGE (widget), icon_name, GTK_ICON_SIZE_MENU);

	/* Update tab tooltip */
	tooltip = g_string_new (NULL);

	if (remote_contact) {
		markup = g_markup_printf_escaped ("<b>%s</b><small> (%s)</small>",
						  empathy_contact_get_id (remote_contact),
						  mc_account_get_display_name (account));
		g_string_append (tooltip, markup);
		g_free (markup);

		status = empathy_contact_get_status (remote_contact);

		if (status) {
			markup = g_markup_printf_escaped ("\n%s", status);
			g_string_append (tooltip, markup);
			g_free (markup);
		}
	}
	else {
		markup = g_markup_printf_escaped ("<b>%s</b><small>  (%s)</small>", name,
						  mc_account_get_display_name (account));
		g_string_append (tooltip, markup);
		g_free (markup);
	}

	if (subject) {
		markup = g_markup_printf_escaped ("\n<b>%s</b> %s", _("Topic:"), subject);
		g_string_append (tooltip, markup);
		g_free (markup);
	}
	if (g_list_find (priv->chats_composing, chat)) {
		markup = g_markup_printf_escaped ("\n%s", _("Typing a message."));
		g_string_append (tooltip, markup);
		g_free (markup);
	}

	markup = g_string_free (tooltip, FALSE);
	widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget");
	gtk_widget_set_tooltip_markup (widget, markup);
	widget = g_object_get_data (G_OBJECT (chat), "chat-window-menu-tooltip-widget");
	gtk_widget_set_tooltip_markup (widget, markup);
	g_free (markup);
{'fraction_non_alphanumeric': 0.11202033567797201, 'fraction_numerical': 0.005501775889685911, 'mean_word_length': 4.0929242773541405, 'pattern_counts': {'":': 0, '<': 101, '<?xml version=': 0, '>': 167, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '326394', 'n_tokens_mistral': 11486, 'n_tokens_neox': 10623, 'n_words': 2179}