#include "empathy-smiley-manager.h"
#include "empathy-ui-utils.h"
#include "empathy-plist.h"
-#include "empathy-string-parser.h"
#include "empathy-images.h"
+#include "empathy-webkit-utils.h"
#define DEBUG_FLAG EMPATHY_DEBUG_CHAT
#include <libempathy/empathy-debug.h>
guint pages_loading;
/* Queue of QueuedItem*s containing an EmpathyMessage or string */
GQueue message_queue;
- /* Queue of owned gchar* of message token to remove unread
+ /* Queue of guint32 of pending message id to remove unread
* marker for when we lose focus. */
GQueue acked_messages;
GtkWidget *inspector_window;
+
GSettings *gsettings_chat;
+ GSettings *gsettings_desktop;
+
gboolean has_focus;
gboolean has_unread_message;
gboolean allow_scrolling;
guint type;
EmpathyMessage *msg;
char *str;
+ gboolean should_highlight;
} QueuedItem;
static QueuedItem *
queue_item (GQueue *queue,
guint type,
EmpathyMessage *msg,
- const char *str)
+ const char *str,
+ gboolean should_highlight)
{
QueuedItem *item = g_slice_new0 (QueuedItem);
if (msg != NULL)
item->msg = g_object_ref (msg);
item->str = g_strdup (str);
+ item->should_highlight = should_highlight;
g_queue_push_tail (queue, item);
return TRUE;
}
-static void
-theme_adium_copy_address_cb (GtkMenuItem *menuitem,
- gpointer user_data)
-{
- WebKitHitTestResult *hit_test_result = WEBKIT_HIT_TEST_RESULT (user_data);
- gchar *uri;
- GtkClipboard *clipboard;
-
- g_object_get (G_OBJECT (hit_test_result), "link-uri", &uri, NULL);
-
- clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_set_text (clipboard, uri, -1);
-
- clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
- gtk_clipboard_set_text (clipboard, uri, -1);
-
- g_free (uri);
-}
-
-static void
-theme_adium_open_address_cb (GtkMenuItem *menuitem,
- gpointer user_data)
-{
- WebKitHitTestResult *hit_test_result = WEBKIT_HIT_TEST_RESULT (user_data);
- gchar *uri;
-
- g_object_get (G_OBJECT (hit_test_result), "link-uri", &uri, NULL);
-
- empathy_url_show (GTK_WIDGET (menuitem), uri);
-
- g_free (uri);
-}
-
/* Replace each %@ in format with string passed in args */
static gchar *
string_with_format (const gchar *format,
g_free (template);
}
-static void
-theme_adium_match_newline (const gchar *text,
- gssize len,
- EmpathyStringReplace replace_func,
- EmpathyStringParser *sub_parsers,
- gpointer user_data)
-{
- GString *string = user_data;
- gint i;
- gint prev = 0;
-
- if (len < 0) {
- len = G_MAXSSIZE;
- }
-
- /* Replace \n by <br/> */
- for (i = 0; i < len && text[i] != '\0'; i++) {
- if (text[i] == '\n') {
- empathy_string_parser_substr (text + prev,
- i - prev, sub_parsers,
- user_data);
- g_string_append (string, "<br/>");
- prev = i + 1;
- }
- }
- empathy_string_parser_substr (text + prev, i - prev,
- sub_parsers, user_data);
-}
-
-static void
-theme_adium_replace_smiley (const gchar *text,
- gssize len,
- gpointer match_data,
- gpointer user_data)
-{
- EmpathySmileyHit *hit = match_data;
- GString *string = user_data;
-
- /* Replace smiley by a <img/> tag */
- g_string_append_printf (string,
- "<img src=\"%s\" alt=\"%.*s\" title=\"%.*s\"/>",
- hit->path, (int)len, text, (int)len, text);
-}
-
-static EmpathyStringParser string_parsers[] = {
- {empathy_string_match_link, empathy_string_replace_link},
- {theme_adium_match_newline, NULL},
- {empathy_string_match_all, empathy_string_replace_escaped},
- {NULL, NULL}
-};
-
-static EmpathyStringParser string_parsers_with_smiley[] = {
- {empathy_string_match_link, empathy_string_replace_link},
- {empathy_string_match_smiley, theme_adium_replace_smiley},
- {theme_adium_match_newline, NULL},
- {empathy_string_match_all, empathy_string_replace_escaped},
- {NULL, NULL}
-};
-
static gchar *
theme_adium_parse_body (EmpathyThemeAdium *self,
const gchar *text,
GString *string;
/* Check if we have to parse smileys */
- if (g_settings_get_boolean (priv->gsettings_chat,
- EMPATHY_PREFS_CHAT_SHOW_SMILEYS))
- parsers = string_parsers_with_smiley;
- else
- parsers = string_parsers;
+ parsers = empathy_webkit_get_string_parser (
+ g_settings_get_boolean (priv->gsettings_chat,
+ EMPATHY_PREFS_CHAT_SHOW_SMILEYS));
/* Parse text and construct string with links and smileys replaced
* by html tags. Also escape text to make sure html code is
const gchar *service_name,
const gchar *message_classes,
gint64 timestamp,
- gboolean is_backlog)
+ gboolean is_backlog,
+ gboolean outgoing)
{
EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
GString *string;
* Incoming/SenderColors.txt it will be used instead of
* the default colors.
*/
- if (contact_id != NULL) {
+
+ /* Ensure we always use the same color when sending messages
+ * (bgo #658821) */
+ if (outgoing) {
+ replace = "inherit";
+ } else if (contact_id != NULL) {
guint hash = g_str_hash (contact_id);
replace = colors[hash % G_N_ELEMENTS (colors)];
}
theme_adium_append_html (theme, "appendMessage",
priv->data->status_html, escaped, NULL, NULL, NULL,
NULL, "event",
- empathy_time_get_current (), FALSE);
+ empathy_time_get_current (), FALSE, FALSE);
/* There is no last contact */
if (priv->last_contact) {
static void
theme_adium_append_message (EmpathyChatView *view,
- EmpathyMessage *msg)
+ EmpathyMessage *msg,
+ gboolean should_highlight)
{
EmpathyThemeAdium *theme = EMPATHY_THEME_ADIUM (view);
EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
EmpathyContact *sender;
TpMessage *tp_msg;
TpAccount *account;
- gchar *body_escaped;
+ gchar *body_escaped, *name_escaped;
const gchar *name;
const gchar *contact_id;
EmpathyAvatar *avatar;
gboolean action;
if (priv->pages_loading != 0) {
- queue_item (&priv->message_queue, QUEUED_MESSAGE, msg, NULL);
+ queue_item (&priv->message_queue, QUEUED_MESSAGE, msg, NULL, should_highlight);
return;
}
contact_id = empathy_contact_get_id (sender);
action = (empathy_message_get_tptype (msg) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION);
+ name_escaped = g_markup_escape_text (name, -1);
+
/* If this is a /me probably */
if (action) {
gchar *str;
if (priv->data->version >= 4 || !priv->data->custom_template) {
str = g_strdup_printf ("<span class='actionMessageUserName'>%s</span>"
"<span class='actionMessageBody'>%s</span>",
- name, body_escaped);
+ name_escaped, body_escaped);
} else {
str = g_strdup_printf ("*%s*", body_escaped);
}
} else {
g_string_append (message_classes, " incoming");
}
- if (empathy_message_should_highlight (msg)) {
+ if (should_highlight) {
g_string_append (message_classes, " mention");
}
if (empathy_message_get_tptype (msg) == TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY) {
* message later. */
tp_msg = empathy_message_get_tp_message (msg);
if (tp_msg != NULL) {
- gchar *tmp = tp_escape_as_identifier (
- tp_message_get_token (tp_msg));
- g_string_append_printf (message_classes,
- " x-empathy-message-id-%s", tmp);
- g_free (tmp);
+ guint32 id;
+ gboolean valid;
+
+ id = tp_message_get_pending_message_id (tp_msg, &valid);
+ if (valid) {
+ g_string_append_printf (message_classes,
+ " x-empathy-message-id-%u", id);
+ }
}
/* Define javascript function to use */
}
theme_adium_append_html (theme, func, html, body_escaped,
- avatar_filename, name, contact_id,
+ avatar_filename, name_escaped, contact_id,
service_name, message_classes->str,
- timestamp, is_backlog);
+ timestamp, is_backlog, empathy_contact_is_user (sender));
/* Keep the sender of the last displayed message */
if (priv->last_contact) {
priv->last_is_backlog = is_backlog;
g_free (body_escaped);
+ g_free (name_escaped);
g_string_free (message_classes, TRUE);
}
gchar *str_escaped;
if (priv->pages_loading != 0) {
- queue_item (&priv->message_queue, QUEUED_EVENT, NULL, str);
+ queue_item (&priv->message_queue, QUEUED_EVENT, NULL, str, FALSE);
return;
}
g_free (str_escaped);
}
+static void
+theme_adium_append_event_markup (EmpathyChatView *view,
+ const gchar *markup_text,
+ const gchar *fallback_text)
+{
+ theme_adium_append_event_escaped (view, markup_text);
+}
+
static void
theme_adium_edit_message (EmpathyChatView *view,
EmpathyMessage *message)
GError *error = NULL;
if (priv->pages_loading != 0) {
- queue_item (&priv->message_queue, QUEUED_EDIT, message, NULL);
+ queue_item (&priv->message_queue, QUEUED_EDIT, message, NULL, FALSE);
return;
}
static void
theme_adium_remove_mark_from_message (EmpathyThemeAdium *self,
- const gchar *token)
+ guint32 id)
{
WebKitDOMDocument *dom;
WebKitDOMNodeList *nodes;
- gchar *class, *tmp;
+ gchar *class;
GError *error = NULL;
dom = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (self));
return;
}
- tmp = tp_escape_as_identifier (token);
- class = g_strdup_printf (".x-empathy-message-id-%s", tmp);
- g_free (tmp);
+ class = g_strdup_printf (".x-empathy-message-id-%u", id);
/* Get all nodes with focus class */
nodes = webkit_dom_document_query_selector_all (dom, class, &error);
gpointer user_data)
{
EmpathyThemeAdium *self = user_data;
- gchar *token = data;
+ guint32 id = GPOINTER_TO_UINT (data);
- theme_adium_remove_mark_from_message (self, token);
- g_free (token);
+ theme_adium_remove_mark_from_message (self, id);
}
static void
EmpathyThemeAdium *self = (EmpathyThemeAdium *) view;
EmpathyThemeAdiumPriv *priv = GET_PRIV (view);
TpMessage *tp_msg;
+ guint32 id;
+ gboolean valid;
tp_msg = empathy_message_get_tp_message (message);
return;
}
+ id = tp_message_get_pending_message_id (tp_msg, &valid);
+ if (!valid) {
+ g_warning ("Acknoledged message doesn't have a pending ID");
+ return;
+ }
+
/* We only want to actually remove the unread marker if the
* view doesn't have focus. If we did it all the time we would
* never see the unread markers, ever! So, we'll queue these
* up, and when we lose focus, we'll remove the markers. */
if (priv->has_focus) {
g_queue_push_tail (&priv->acked_messages,
- g_strdup (tp_message_get_token (tp_msg)));
+ GUINT_TO_POINTER (id));
return;
}
- theme_adium_remove_mark_from_message (self,
- tp_message_get_token (tp_msg));
-}
-
-static void
-theme_adium_context_menu_selection_done_cb (GtkMenuShell *menu, gpointer user_data)
-{
- WebKitHitTestResult *hit_test_result = WEBKIT_HIT_TEST_RESULT (user_data);
-
- g_object_unref (hit_test_result);
-}
-
-static void
-theme_adium_context_menu_for_event (EmpathyThemeAdium *theme, GdkEventButton *event)
-{
- WebKitWebView *view = WEBKIT_WEB_VIEW (theme);
- WebKitHitTestResult *hit_test_result;
- WebKitHitTestResultContext context;
- GtkWidget *menu;
- GtkWidget *item;
-
- hit_test_result = webkit_web_view_get_hit_test_result (view, event);
- g_object_get (G_OBJECT (hit_test_result), "context", &context, NULL);
-
- /* The menu */
- menu = empathy_context_menu_new (GTK_WIDGET (view));
-
- /* Select all item */
- item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-
- g_signal_connect_swapped (item, "activate",
- G_CALLBACK (webkit_web_view_select_all),
- view);
-
- /* Copy menu item */
- if (webkit_web_view_can_copy_clipboard (view)) {
- item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-
- g_signal_connect_swapped (item, "activate",
- G_CALLBACK (webkit_web_view_copy_clipboard),
- view);
- }
-
- /* Clear menu item */
- item = gtk_separator_menu_item_new ();
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-
- item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-
- g_signal_connect_swapped (item, "activate",
- G_CALLBACK (empathy_chat_view_clear),
- view);
-
- /* We will only add the following menu items if we are
- * right-clicking a link */
- if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
- /* Separator */
- item = gtk_separator_menu_item_new ();
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-
- /* Copy Link Address menu item */
- item = gtk_menu_item_new_with_mnemonic (_("_Copy Link Address"));
- g_signal_connect (item, "activate",
- G_CALLBACK (theme_adium_copy_address_cb),
- hit_test_result);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
-
- /* Open Link menu item */
- item = gtk_menu_item_new_with_mnemonic (_("_Open Link"));
- g_signal_connect (item, "activate",
- G_CALLBACK (theme_adium_open_address_cb),
- hit_test_result);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
- }
-
- g_signal_connect (GTK_MENU_SHELL (menu), "selection-done",
- G_CALLBACK (theme_adium_context_menu_selection_done_cb),
- hit_test_result);
-
- /* Display the menu */
- gtk_widget_show_all (menu);
- gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
- event->button, event->time);
+ theme_adium_remove_mark_from_message (self, id);
}
static gboolean
* item ourselves, so we disable our customized menu
* if the developer extras are enabled. */
if (!developer_tools_enabled) {
- theme_adium_context_menu_for_event (EMPATHY_THEME_ADIUM (widget), event);
+ empathy_webkit_context_menu_for_event (
+ WEBKIT_WEB_VIEW (widget), event,
+ EMPATHY_WEBKIT_MENU_CLEAR);
return TRUE;
}
}
{
iface->append_message = theme_adium_append_message;
iface->append_event = theme_adium_append_event;
+ iface->append_event_markup = theme_adium_append_event_markup;
iface->edit_message = theme_adium_edit_message;
iface->scroll = theme_adium_scroll;
iface->scroll_down = theme_adium_scroll_down;
switch (item->type)
{
case QUEUED_MESSAGE:
- theme_adium_append_message (chat_view, item->msg);
+ theme_adium_append_message (chat_view, item->msg,
+ item->should_highlight);
break;
case QUEUED_EDIT:
EmpathyThemeAdiumPriv *priv = GET_PRIV (object);
empathy_adium_data_unref (priv->data);
+
g_object_unref (priv->gsettings_chat);
+ g_object_unref (priv->gsettings_desktop);
G_OBJECT_CLASS (empathy_theme_adium_parent_class)->finalize (object);
}
}
if (priv->acked_messages.length > 0) {
- g_queue_foreach (&priv->acked_messages, (GFunc) g_free, NULL);
g_queue_clear (&priv->acked_messages);
}
return NULL;
}
-static PangoFontDescription *
-theme_adium_get_default_font (void)
-{
- GSettings *gsettings;
- PangoFontDescription *pango_fd;
- gchar *font_family;
-
- gsettings = g_settings_new (EMPATHY_PREFS_DESKTOP_INTERFACE_SCHEMA);
-
- font_family = g_settings_get_string (gsettings,
- EMPATHY_PREFS_DESKTOP_INTERFACE_DOCUMENT_FONT_NAME);
-
- if (font_family == NULL)
- return NULL;
-
- pango_fd = pango_font_description_from_string (font_family);
- g_free (font_family);
- g_object_unref (gsettings);
- return pango_fd;
-}
-
-static void
-theme_adium_set_webkit_font (WebKitWebSettings *w_settings,
- const gchar *name,
- gint size)
-{
- g_object_set (w_settings, "default-font-family", name, NULL);
- g_object_set (w_settings, "default-font-size", size, NULL);
-}
-
-static void
-theme_adium_set_default_font (WebKitWebSettings *w_settings)
-{
- PangoFontDescription *default_font_desc;
- GdkScreen *current_screen;
- gdouble dpi = 0;
- gint pango_font_size = 0;
-
- default_font_desc = theme_adium_get_default_font ();
- if (default_font_desc == NULL)
- return ;
- pango_font_size = pango_font_description_get_size (default_font_desc)
- / PANGO_SCALE ;
- if (pango_font_description_get_size_is_absolute (default_font_desc)) {
- current_screen = gdk_screen_get_default ();
- if (current_screen != NULL) {
- dpi = gdk_screen_get_resolution (current_screen);
- } else {
- dpi = BORING_DPI_DEFAULT;
- }
- pango_font_size = (gint) (pango_font_size / (dpi / 72));
- }
- theme_adium_set_webkit_font (w_settings,
- pango_font_description_get_family (default_font_desc),
- pango_font_size);
- pango_font_description_free (default_font_desc);
-}
-
static void
theme_adium_constructed (GObject *object)
{
const gchar *font_family = NULL;
gint font_size = 0;
WebKitWebView *webkit_view = WEBKIT_WEB_VIEW (object);
- WebKitWebSettings *webkit_settings;
WebKitWebInspector *webkit_inspector;
/* Set default settings */
font_family = tp_asv_get_string (priv->data->info, "DefaultFontFamily");
font_size = tp_asv_get_int32 (priv->data->info, "DefaultFontSize", NULL);
- webkit_settings = webkit_web_view_get_settings (webkit_view);
if (font_family && font_size) {
- theme_adium_set_webkit_font (webkit_settings, font_family, font_size);
+ g_object_set (webkit_web_view_get_settings (webkit_view),
+ "default-font-family", font_family,
+ "default-font-size", font_size,
+ NULL);
} else {
- theme_adium_set_default_font (webkit_settings);
+ empathy_webkit_bind_font_setting (webkit_view,
+ priv->gsettings_desktop,
+ EMPATHY_PREFS_DESKTOP_INTERFACE_DOCUMENT_FONT_NAME);
}
/* Setup webkit inspector */
NULL);
priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
+ priv->gsettings_desktop = g_settings_new (
+ EMPATHY_PREFS_DESKTOP_INTERFACE_SCHEMA);
+
g_signal_connect (priv->gsettings_chat,
"changed::" EMPATHY_PREFS_CHAT_WEBKIT_DEVELOPER_TOOLS,
G_CALLBACK (theme_adium_notify_enable_webkit_developer_tools_cb),
g_object_notify (G_OBJECT (theme), "variant");
}
+void
+empathy_theme_adium_show_inspector (EmpathyThemeAdium *theme)
+{
+ WebKitWebView *web_view = WEBKIT_WEB_VIEW (theme);
+ WebKitWebInspector *inspector;
+
+ g_object_set (G_OBJECT (webkit_web_view_get_settings (web_view)),
+ "enable-developer-extras", TRUE,
+ NULL);
+
+ inspector = webkit_web_view_get_inspector (web_view);
+ webkit_web_inspector_show (inspector);
+}
+
gboolean
empathy_adium_path_is_valid (const gchar *path)
{
return g_strdup ("main.css");
}
- /* Verify the variant exists, fallback to the first one */
variants = empathy_adium_info_get_available_variants (info);
+ if (variants->len == 0)
+ return g_strdup ("main.css");
+
+ /* Verify the variant exists, fallback to the first one */
for (i = 0; i < variants->len; i++) {
if (!tp_strdiff (variant, g_ptr_array_index (variants, i))) {
break;
/* template -> empathy's template */
data->custom_template = (template_html != NULL);
if (template_html == NULL) {
+ GError *error = NULL;
+
tmp = empathy_file_lookup ("Template.html", "data");
- g_file_get_contents (tmp, &template_html, NULL, NULL);
+
+ if (!g_file_get_contents (tmp, &template_html, NULL, &error)) {
+ g_warning ("couldn't load Empathy's default theme "
+ "template: %s", error->message);
+ g_return_val_if_reached (data);
+ }
+
g_free (tmp);
}