]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-theme-adium.c
remove released flag
[empathy.git] / libempathy-gtk / empathy-theme-adium.c
index c00e3829827337d732a8df8d40c410dd12650f67..a870702ac975094ffb9ba27c746ecf7522f7aee8 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008-2012 Collabora Ltd.
+ * Copyright (C) 2012 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  */
 
 #include "config.h"
+#include "empathy-theme-adium.h"
 
-#include <string.h>
 #include <glib/gi18n-lib.h>
+#include <tp-account-widgets/tpaw-images.h>
+#include <tp-account-widgets/tpaw-time.h>
+#include <tp-account-widgets/tpaw-pixbuf-utils.h>
+#include <tp-account-widgets/tpaw-utils.h>
 
-#include <webkit/webkit.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/util.h>
-
-#include <pango/pango.h>
-#include <gdk/gdk.h>
-
-#include <libempathy/empathy-gsettings.h>
-#include <libempathy/empathy-time.h>
-#include <libempathy/empathy-utils.h>
-
-#include "empathy-theme-adium.h"
+#include "empathy-gsettings.h"
+#include "empathy-images.h"
+#include "empathy-plist.h"
 #include "empathy-smiley-manager.h"
 #include "empathy-ui-utils.h"
-#include "empathy-plist.h"
-#include "empathy-images.h"
+#include "empathy-utils.h"
 #include "empathy-webkit-utils.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
-#include <libempathy/empathy-debug.h>
+#include "empathy-debug.h"
 
 #define BORING_DPI_DEFAULT 96
 
@@ -53,8 +48,11 @@ struct _EmpathyThemeAdiumPriv
 {
   EmpathyAdiumData *data;
   EmpathySmileyManager *smiley_manager;
+  EmpathyContact *first_contact;
   EmpathyContact *last_contact;
+  gint64 first_timestamp;
   gint64 last_timestamp;
+  gboolean first_is_backlog;
   gboolean last_is_backlog;
   guint pages_loading;
   /* Queue of QueuedItem*s containing an EmpathyMessage or string */
@@ -108,8 +106,6 @@ struct _EmpathyAdiumData
   GPtrArray *strings_to_free;
 };
 
-static void theme_adium_iface_init (EmpathyChatViewIface *iface);
-
 static gchar * adium_info_dup_path_for_variant (GHashTable *info,
     const gchar *variant);
 
@@ -120,10 +116,8 @@ enum
   PROP_VARIANT,
 };
 
-G_DEFINE_TYPE_WITH_CODE (EmpathyThemeAdium, empathy_theme_adium,
-       WEBKIT_TYPE_WEB_VIEW,
-       G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CHAT_VIEW,
-            theme_adium_iface_init));
+G_DEFINE_TYPE (EmpathyThemeAdium, empathy_theme_adium,
+       WEBKIT_TYPE_WEB_VIEW)
 
 enum
 {
@@ -145,7 +139,8 @@ queue_item (GQueue *queue,
     guint type,
     EmpathyMessage *msg,
     const char *str,
-    gboolean should_highlight)
+    gboolean should_highlight,
+    gboolean prepend)
 {
   QueuedItem *item = g_slice_new0 (QueuedItem);
 
@@ -155,7 +150,10 @@ queue_item (GQueue *queue,
   item->str = g_strdup (str);
   item->should_highlight = should_highlight;
 
-  g_queue_push_tail (queue, item);
+  if (prepend)
+    g_queue_push_head (queue, item);
+  else
+    g_queue_push_tail (queue, item);
 
   return item;
 }
@@ -169,30 +167,6 @@ free_queued_item (QueuedItem *item)
   g_slice_free (QueuedItem, item);
 }
 
-static void
-theme_adium_update_enable_webkit_developer_tools (EmpathyThemeAdium *self)
-{
-  WebKitWebView *web_view = WEBKIT_WEB_VIEW (self);
-  gboolean enable_webkit_developer_tools;
-
-  enable_webkit_developer_tools = g_settings_get_boolean (
-      self->priv->gsettings_chat,
-      EMPATHY_PREFS_CHAT_WEBKIT_DEVELOPER_TOOLS);
-
-  g_object_set (G_OBJECT (webkit_web_view_get_settings (web_view)),
-      "enable-developer-extras", enable_webkit_developer_tools, NULL);
-}
-
-static void
-theme_adium_notify_enable_webkit_developer_tools_cb (GSettings *gsettings,
-    const gchar *key,
-    gpointer user_data)
-{
-  EmpathyThemeAdium *self = user_data;
-
-  theme_adium_update_enable_webkit_developer_tools (self);
-}
-
 static gboolean
 theme_adium_navigation_policy_decision_requested_cb (WebKitWebView *view,
     WebKitWebFrame *web_frame,
@@ -277,7 +251,7 @@ theme_adium_parse_body (EmpathyThemeAdium *self,
   const gchar *text,
   const gchar *token)
 {
-  EmpathyStringParser *parsers;
+  TpawStringParser *parsers;
   GString *string;
 
   /* Check if we have to parse smileys */
@@ -297,7 +271,7 @@ theme_adium_parse_body (EmpathyThemeAdium *self,
       "<span id=\"message-token-%s\">",
       token);
 
-  empathy_string_parser_substr (text, -1, parsers, string);
+  tpaw_string_parser_substr (text, -1, parsers, string);
 
   if (!tp_str_empty (token))
     g_string_append (string, "</span>");
@@ -547,9 +521,8 @@ nsdate_to_strftime (EmpathyAdiumData *data, const gchar *nsdate)
   return g_string_free (string, FALSE);
 }
 
-
 static void
-theme_adium_append_html (EmpathyThemeAdium *self,
+theme_adium_add_html (EmpathyThemeAdium *self,
     const gchar *func,
     const gchar *html,
     const gchar *message,
@@ -560,10 +533,13 @@ theme_adium_append_html (EmpathyThemeAdium *self,
     const gchar *message_classes,
     gint64 timestamp,
     gboolean is_backlog,
-    gboolean outgoing)
+    gboolean outgoing,
+    PangoDirection direction)
 {
+  GBytes *bytes;
   GString *string;
   const gchar *cur = NULL;
+  const gchar *js;
   gchar *script;
 
   /* Make some search-and-replace in the html code */
@@ -619,9 +595,22 @@ theme_adium_append_html (EmpathyThemeAdium *self,
         }
       else if (theme_adium_match (&cur, "%messageDirection%"))
         {
-          /* FIXME: The text direction of the message
-           * (either rtl or ltr)
-           */
+          switch (direction)
+            {
+              case PANGO_DIRECTION_LTR:
+              case PANGO_DIRECTION_TTB_LTR:
+              case PANGO_DIRECTION_WEAK_LTR:
+                replace = "ltr";
+                break;
+              case PANGO_DIRECTION_RTL:
+              case PANGO_DIRECTION_TTB_RTL:
+              case PANGO_DIRECTION_WEAK_RTL:
+                replace = "rtl";
+                break;
+              case PANGO_DIRECTION_NEUTRAL:
+              default:
+                break;
+            }
         }
       else if (theme_adium_match (&cur, "%senderDisplayName%"))
         {
@@ -661,20 +650,20 @@ theme_adium_append_html (EmpathyThemeAdium *self,
 
           strftime_format = nsdate_to_strftime (self->priv->data, format);
           if (is_backlog)
-            dup_replace = empathy_time_to_string_local (timestamp,
+            dup_replace = tpaw_time_to_string_local (timestamp,
               strftime_format ? strftime_format :
-              EMPATHY_TIME_DATE_FORMAT_DISPLAY_SHORT);
+              TPAW_TIME_DATE_FORMAT_DISPLAY_SHORT);
           else
-            dup_replace = empathy_time_to_string_local (timestamp,
+            dup_replace = tpaw_time_to_string_local (timestamp,
               strftime_format ? strftime_format :
-              EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
+              TPAW_TIME_FORMAT_DISPLAY_SHORT);
 
           replace = dup_replace;
         }
       else if (theme_adium_match (&cur, "%shortTime%"))
         {
-          dup_replace = empathy_time_to_string_local (timestamp,
-            EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
+          dup_replace = tpaw_time_to_string_local (timestamp,
+            TPAW_TIME_FORMAT_DISPLAY_SHORT);
           replace = dup_replace;
         }
       else if (theme_adium_match (&cur, "%service%"))
@@ -740,20 +729,30 @@ theme_adium_append_html (EmpathyThemeAdium *self,
     }
   g_string_append (string, "\")");
 
+  bytes = g_resources_lookup_data ("/org/gnome/Empathy/Chat/empathy-chat.js",
+      G_RESOURCE_LOOKUP_FLAGS_NONE,
+      NULL);
+
+  if (bytes != NULL)
+    {
+      js = (const gchar *) g_bytes_get_data (bytes, NULL);
+      g_string_prepend (string, js);
+      g_bytes_unref (bytes);
+    }
+
   script = g_string_free (string, FALSE);
   webkit_web_view_execute_script (WEBKIT_WEB_VIEW (self), script);
   g_free (script);
 }
 
 static void
-theme_adium_append_event_escaped (EmpathyChatView *view,
-    const gchar *escaped)
+theme_adium_append_event_escaped (EmpathyThemeAdium *self,
+    const gchar *escaped,
+    PangoDirection direction)
 {
-  EmpathyThemeAdium *self = EMPATHY_THEME_ADIUM (view);
-
-  theme_adium_append_html (self, "appendMessage",
+  theme_adium_add_html (self, "appendMessage",
       self->priv->data->status_html, escaped, NULL, NULL, NULL,
-      NULL, "event", empathy_time_get_current (), FALSE, FALSE);
+      NULL, "event", tpaw_time_get_current (), FALSE, FALSE, direction);
 
   /* There is no last contact */
   if (self->priv->last_contact)
@@ -837,12 +836,56 @@ theme_adium_remove_all_focus_marks (EmpathyThemeAdium *self)
   theme_adium_remove_focus_marks (self, nodes);
 }
 
+enum
+{
+  ADD_CONSECUTIVE_MSG_SCROLL = 0,
+  ADD_CONSECUTIVE_MSG_NO_SCROLL = 1,
+  ADD_MSG_SCROLL = 2,
+  ADD_MSG_NO_SCROLL = 3
+};
+
+/*
+ * theme_adium_add_message:
+ * @self: The #EmpathyThemeAdium used by the view.
+ * @msg: An #EmpathyMessage that is to be added to the view.
+ * @prev_contact: (out): The #EmpathyContact that sent the previous message.
+ * @prev_timestamp: (out): Timestamp of the previous message.
+ * @prev_is_backlog: (out): Whether the previous message was fetched
+ * from the logs.
+ * @should_highlight: Whether the message should be highlighted. eg.,
+ * if it matches the user's username in multi-user chat.
+ * @js_funcs: An array of JavaScript function names
+ *
+ * Shows @msg in the chat view by adding to @self. Addition is defined
+ * by the JavaScript functions listed in @js_funcs. Common examples
+ * are appending new incoming messages or prepending old messages from
+ * the logs.
+ *
+ * @js_funcs should be an array with exactly 4 entries. The entries
+ * should be the names of JavaScript functions that take the raw HTML
+ * that is to be added to the view as an argument and take the following
+ * actions, in this order:
+ * - add a new consecutive message and scroll to it if needed,
+ * - add a new consecutive message and do not scroll,
+ * - add a new non-consecutive message and scroll to it if needed, and
+ * - add a new non-consecutive message and do not scroll
+ *
+ * A message is considered to be consecutive with the previous one if
+ * all the following conditions are met:
+ * - senders are the same contact,
+ * - last message was recieved recently,
+ * - last message and this message both are/aren't backlog, and
+ * - DisableCombineConsecutive is not set in theme's settings
+ */
 static void
-theme_adium_append_message (EmpathyChatView *view,
+theme_adium_add_message (EmpathyThemeAdium *self,
     EmpathyMessage *msg,
-    gboolean should_highlight)
+    EmpathyContact **prev_contact,
+    gint64 *prev_timestamp,
+    gboolean *prev_is_backlog,
+    gboolean should_highlight,
+    const gchar *js_funcs[])
 {
-  EmpathyThemeAdium *self = EMPATHY_THEME_ADIUM (view);
   EmpathyContact *sender;
   TpMessage *tp_msg;
   TpAccount *account;
@@ -859,21 +902,16 @@ theme_adium_append_message (EmpathyChatView *view,
   gboolean is_backlog;
   gboolean consecutive;
   gboolean action;
+  PangoDirection direction;
 
-  if (self->priv->pages_loading != 0)
-    {
-      queue_item (&self->priv->message_queue, QUEUED_MESSAGE, msg, NULL,
-          should_highlight);
-      return;
-    }
 
   /* Get information */
   sender = empathy_message_get_sender (msg);
   account = empathy_contact_get_account (sender);
-  service_name = empathy_protocol_name_to_display_name
-    (tp_account_get_protocol (account));
+  service_name = tpaw_protocol_name_to_display_name
+    (tp_account_get_protocol_name (account));
   if (service_name == NULL)
-    service_name = tp_account_get_protocol (account);
+    service_name = tp_account_get_protocol_name (account);
   timestamp = empathy_message_get_timestamp (msg);
   body_escaped = theme_adium_parse_body (self,
     empathy_message_get_body (msg),
@@ -921,22 +959,17 @@ theme_adium_append_message (EmpathyChatView *view,
         {
           if (!self->priv->data->default_avatar_filename)
             self->priv->data->default_avatar_filename =
-              empathy_filename_from_icon_name (EMPATHY_IMAGE_AVATAR_DEFAULT,
+              tpaw_filename_from_icon_name (TPAW_IMAGE_AVATAR_DEFAULT,
                        GTK_ICON_SIZE_DIALOG);
 
           avatar_filename = self->priv->data->default_avatar_filename;
         }
     }
 
-  /* We want to join this message with the last one if
-   * - senders are the same contact,
-   * - last message was recieved recently,
-   * - last message and this message both are/aren't backlog, and
-   * - DisableCombineConsecutive is not set in theme's settings */
   is_backlog = empathy_message_is_backlog (msg);
-  consecutive = empathy_contact_equal (self->priv->last_contact, sender) &&
-    (timestamp - self->priv->last_timestamp < MESSAGE_JOIN_PERIOD) &&
-    (is_backlog == self->priv->last_is_backlog) &&
+  consecutive = empathy_contact_equal (*prev_contact, sender) &&
+    (ABS (timestamp - *prev_timestamp) < MESSAGE_JOIN_PERIOD) &&
+    (is_backlog == *prev_is_backlog) &&
     !tp_asv_get_boolean (self->priv->data->info,
              "DisableCombineConsecutive", NULL);
 
@@ -977,7 +1010,7 @@ theme_adium_append_message (EmpathyChatView *view,
    * status - the message is a status change
    * event - the message is a notification of something happening
    *         (for example, encryption being turned on)
-   * %status% - See %status% in theme_adium_append_html ()
+   * %status% - See %status% in theme_adium_add_html ()
    */
 
   /* This is slightly a hack, but it's the only way to add
@@ -999,10 +1032,11 @@ theme_adium_append_message (EmpathyChatView *view,
 
   /* Define javascript function to use */
   if (consecutive)
-    func = self->priv->allow_scrolling ? "appendNextMessage" :
-      "appendNextMessageNoScroll";
+    func = self->priv->allow_scrolling ? js_funcs[ADD_CONSECUTIVE_MSG_SCROLL] :
+      js_funcs[ADD_CONSECUTIVE_MSG_NO_SCROLL];
   else
-    func = self->priv->allow_scrolling ? "appendMessage" : "appendMessageNoScroll";
+    func = self->priv->allow_scrolling ? js_funcs[ADD_MSG_SCROLL] :
+      js_funcs[ADD_MSG_NO_SCROLL];
 
   if (empathy_contact_is_user (sender))
     {
@@ -1032,55 +1066,104 @@ theme_adium_append_message (EmpathyChatView *view,
           self->priv->data->in_content_html;
     }
 
-  theme_adium_append_html (self, func, html, body_escaped,
+  direction = pango_find_base_dir (empathy_message_get_body (msg), -1);
+
+  theme_adium_add_html (self, func, html, body_escaped,
       avatar_filename, name_escaped, contact_id,
       service_name, message_classes->str,
-      timestamp, is_backlog, empathy_contact_is_user (sender));
+      timestamp, is_backlog, empathy_contact_is_user (sender), direction);
 
   /* Keep the sender of the last displayed message */
-  if (self->priv->last_contact)
-    g_object_unref (self->priv->last_contact);
+  if (*prev_contact)
+    g_object_unref (*prev_contact);
 
-  self->priv->last_contact = g_object_ref (sender);
-  self->priv->last_timestamp = timestamp;
-  self->priv->last_is_backlog = is_backlog;
+  *prev_contact = g_object_ref (sender);
+  *prev_timestamp = timestamp;
+  *prev_is_backlog = is_backlog;
 
   g_free (body_escaped);
   g_free (name_escaped);
   g_string_free (message_classes, TRUE);
 }
 
-static void
-theme_adium_append_event (EmpathyChatView *view,
+void
+empathy_theme_adium_append_message (EmpathyThemeAdium *self,
+    EmpathyMessage *msg,
+    gboolean should_highlight)
+{
+  const gchar *js_funcs[] = { "appendNextMessage",
+      "appendNextMessageNoScroll",
+      "appendMessage",
+      "appendMessageNoScroll" };
+
+  if (self->priv->pages_loading != 0)
+    {
+      queue_item (&self->priv->message_queue, QUEUED_MESSAGE, msg, NULL,
+          should_highlight, FALSE);
+      return;
+    }
+
+  theme_adium_add_message (self, msg, &self->priv->last_contact,
+      &self->priv->last_timestamp, &self->priv->last_is_backlog,
+      should_highlight, js_funcs);
+}
+
+void
+empathy_theme_adium_append_event (EmpathyThemeAdium *self,
     const gchar *str)
 {
-  EmpathyThemeAdium *self = EMPATHY_THEME_ADIUM (view);
   gchar *str_escaped;
+  PangoDirection direction;
 
   if (self->priv->pages_loading != 0)
     {
-      queue_item (&self->priv->message_queue, QUEUED_EVENT, NULL, str, FALSE);
+      queue_item (&self->priv->message_queue, QUEUED_EVENT, NULL, str, FALSE, FALSE);
       return;
     }
 
+  direction = pango_find_base_dir (str, -1);
   str_escaped = g_markup_escape_text (str, -1);
-  theme_adium_append_event_escaped (view, str_escaped);
+  theme_adium_append_event_escaped (self, str_escaped, direction);
   g_free (str_escaped);
 }
 
-static void
-theme_adium_append_event_markup (EmpathyChatView *view,
+void
+empathy_theme_adium_append_event_markup (EmpathyThemeAdium *self,
     const gchar *markup_text,
     const gchar *fallback_text)
 {
-  theme_adium_append_event_escaped (view, markup_text);
+  PangoDirection direction;
+
+  direction = pango_find_base_dir (fallback_text, -1);
+  theme_adium_append_event_escaped (self, markup_text, direction);
 }
 
-static void
-theme_adium_edit_message (EmpathyChatView *view,
+void
+empathy_theme_adium_prepend_message (EmpathyThemeAdium *self,
+    EmpathyMessage *msg,
+    gboolean should_highlight)
+{
+  const gchar *js_funcs[] = { "prependPrev",
+      "prependPrev",
+      "prepend",
+      "prepend" };
+
+  if (self->priv->pages_loading != 0)
+    {
+      queue_item (&self->priv->message_queue, QUEUED_MESSAGE, msg, NULL,
+          should_highlight, TRUE);
+      return;
+    }
+
+  theme_adium_add_message (self, msg, &self->priv->first_contact,
+      &self->priv->first_timestamp, &self->priv->first_is_backlog,
+      should_highlight, js_funcs);
+}
+
+void
+empathy_theme_adium_edit_message (EmpathyThemeAdium *self,
     EmpathyMessage *message)
 {
-  EmpathyThemeAdium *self = EMPATHY_THEME_ADIUM (view);
   WebKitDOMDocument *doc;
   WebKitDOMElement *span;
   gchar *id, *parsed_body;
@@ -1090,7 +1173,7 @@ theme_adium_edit_message (EmpathyChatView *view,
 
   if (self->priv->pages_loading != 0)
     {
-      queue_item (&self->priv->message_queue, QUEUED_EDIT, message, NULL, FALSE);
+      queue_item (&self->priv->message_queue, QUEUED_EDIT, message, NULL, FALSE, FALSE);
       return;
     }
 
@@ -1098,11 +1181,11 @@ theme_adium_edit_message (EmpathyChatView *view,
     empathy_message_get_supersedes (message));
   /* we don't pass a token here, because doing so will return another
    * <span> element, and we don't want nested <span> elements */
-  parsed_body = theme_adium_parse_body (EMPATHY_THEME_ADIUM (view),
+  parsed_body = theme_adium_parse_body (self,
     empathy_message_get_body (message), NULL);
 
   /* find the element */
-  doc = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
+  doc = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (self));
   span = webkit_dom_document_get_element_by_id (doc, id);
 
   if (span == NULL)
@@ -1129,7 +1212,7 @@ theme_adium_edit_message (EmpathyChatView *view,
     }
 
   /* set a tooltip */
-  timestamp = empathy_time_to_string_local (
+  timestamp = tpaw_time_to_string_local (
     empathy_message_get_timestamp (message),
     "%H:%M:%S");
   tooltip = g_strdup_printf (_("Message edited at %s"), timestamp);
@@ -1166,7 +1249,7 @@ theme_adium_edit_message (EmpathyChatView *view,
         }
 
       g_free (style);
-      gtk_icon_info_free (icon_info);
+      g_object_unref (icon_info);
     }
 
   goto finally;
@@ -1180,36 +1263,32 @@ finally:
   g_free (parsed_body);
 }
 
-static void
-theme_adium_scroll (EmpathyChatView *view,
+void
+empathy_theme_adium_scroll (EmpathyThemeAdium *self,
     gboolean allow_scrolling)
 {
-  EmpathyThemeAdium *self = EMPATHY_THEME_ADIUM (view);
-
   self->priv->allow_scrolling = allow_scrolling;
 
   if (allow_scrolling)
-    empathy_chat_view_scroll_down (view);
+    empathy_theme_adium_scroll_down (self);
 }
 
-static void
-theme_adium_scroll_down (EmpathyChatView *view)
+void
+empathy_theme_adium_scroll_down (EmpathyThemeAdium *self)
 {
-  webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view), "alignChat(true);");
+  webkit_web_view_execute_script (WEBKIT_WEB_VIEW (self), "alignChat(true);");
 }
 
-static gboolean
-theme_adium_get_has_selection (EmpathyChatView *view)
+gboolean
+empathy_theme_adium_get_has_selection (EmpathyThemeAdium *self)
 {
-  return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (view));
+  return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (self));
 }
 
-static void
-theme_adium_clear (EmpathyChatView *view)
+void
+empathy_theme_adium_clear (EmpathyThemeAdium *self)
 {
-  EmpathyThemeAdium *self = EMPATHY_THEME_ADIUM (view);
-
-  theme_adium_load_template (EMPATHY_THEME_ADIUM (view));
+  theme_adium_load_template (self);
 
   /* Clear last contact to avoid trying to add a 'joined'
    * message when we don't have an insertion point. */
@@ -1220,30 +1299,30 @@ theme_adium_clear (EmpathyChatView *view)
     }
 }
 
-static gboolean
-theme_adium_find_previous (EmpathyChatView *view,
+gboolean
+empathy_theme_adium_find_previous (EmpathyThemeAdium *self,
     const gchar *search_criteria,
     gboolean new_search,
     gboolean match_case)
 {
   /* FIXME: Doesn't respect new_search */
-  return webkit_web_view_search_text (WEBKIT_WEB_VIEW (view),
+  return webkit_web_view_search_text (WEBKIT_WEB_VIEW (self),
       search_criteria, match_case, FALSE, TRUE);
 }
 
-static gboolean
-theme_adium_find_next (EmpathyChatView *view,
+gboolean
+empathy_theme_adium_find_next (EmpathyThemeAdium *self,
     const gchar *search_criteria,
     gboolean new_search,
     gboolean match_case)
 {
   /* FIXME: Doesn't respect new_search */
-  return webkit_web_view_search_text (WEBKIT_WEB_VIEW (view),
+  return webkit_web_view_search_text (WEBKIT_WEB_VIEW (self),
       search_criteria, match_case, TRUE, TRUE);
 }
 
-static void
-theme_adium_find_abilities (EmpathyChatView *view,
+void
+empathy_theme_adium_find_abilities (EmpathyThemeAdium *self,
     const gchar *search_criteria,
     gboolean match_case,
     gboolean *can_do_previous,
@@ -1257,22 +1336,22 @@ theme_adium_find_abilities (EmpathyChatView *view,
     *can_do_next = TRUE;
 }
 
-static void
-theme_adium_highlight (EmpathyChatView *view,
+void
+empathy_theme_adium_highlight (EmpathyThemeAdium *self,
     const gchar *text,
     gboolean match_case)
 {
-  webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (view));
-  webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (view),
+  webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (self));
+  webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (self),
       text, match_case, 0);
-  webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (view),
+  webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (self),
       TRUE);
 }
 
-static void
-theme_adium_copy_clipboard (EmpathyChatView *view)
+void
+empathy_theme_adium_copy_clipboard (EmpathyThemeAdium *self)
 {
-  webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (view));
+  webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (self));
 }
 
 static void
@@ -1315,30 +1394,27 @@ theme_adium_remove_acked_message_unread_mark_foreach (gpointer data,
   theme_adium_remove_mark_from_message (self, id);
 }
 
-static void
-theme_adium_focus_toggled (EmpathyChatView *view,
+void
+empathy_theme_adium_focus_toggled (EmpathyThemeAdium *self,
     gboolean has_focus)
 {
-  EmpathyThemeAdium *self = EMPATHY_THEME_ADIUM (view);
-
   self->priv->has_focus = has_focus;
   if (!self->priv->has_focus)
     {
       /* We've lost focus, so let's make sure all the acked
        * messages have lost their unread marker. */
       g_queue_foreach (&self->priv->acked_messages,
-          theme_adium_remove_acked_message_unread_mark_foreach, view);
+          theme_adium_remove_acked_message_unread_mark_foreach, self);
       g_queue_clear (&self->priv->acked_messages);
 
       self->priv->has_unread_message = FALSE;
     }
 }
 
-static void
-theme_adium_message_acknowledged (EmpathyChatView *view,
+void
+empathy_theme_adium_message_acknowledged (EmpathyThemeAdium *self,
     EmpathyMessage *message)
 {
-  EmpathyThemeAdium *self = (EmpathyThemeAdium *) view;
   TpMessage *tp_msg;
   guint32 id;
   gboolean valid;
@@ -1370,70 +1446,43 @@ theme_adium_message_acknowledged (EmpathyChatView *view,
 }
 
 static gboolean
-theme_adium_button_press_event (GtkWidget *widget,
-    GdkEventButton *event)
+theme_adium_context_menu_cb (EmpathyThemeAdium *self,
+    GtkWidget *default_menu,
+    WebKitHitTestResult *hit_test_result,
+    gboolean triggered_with_keyboard,
+    gpointer user_data)
 {
-  if (event->button == 3)
-    {
-      gboolean developer_tools_enabled;
+  GtkWidget *menu;
+  EmpathyWebKitMenuFlags flags = EMPATHY_WEBKIT_MENU_CLEAR;
 
-      g_object_get (
-          G_OBJECT (webkit_web_view_get_settings (WEBKIT_WEB_VIEW (widget))),
-          "enable-developer-extras", &developer_tools_enabled, NULL);
+  if (g_settings_get_boolean (self->priv->gsettings_chat,
+        EMPATHY_PREFS_CHAT_WEBKIT_DEVELOPER_TOOLS))
+    flags |= EMPATHY_WEBKIT_MENU_INSPECT;
 
-      /* We currently have no way to add an inspector menu
-       * item ourselves, so we disable our customized menu
-       * if the developer extras are enabled. */
-      if (!developer_tools_enabled)
-        {
-          empathy_webkit_context_menu_for_event (
-            WEBKIT_WEB_VIEW (widget), event,
-            EMPATHY_WEBKIT_MENU_CLEAR);
-          return TRUE;
-        }
-    }
+  menu = empathy_webkit_create_context_menu (
+    WEBKIT_WEB_VIEW (self), hit_test_result, flags);
+
+  gtk_widget_show_all (menu);
+
+  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 3,
+      gtk_get_current_event_time ());
 
-  return GTK_WIDGET_CLASS (
-      empathy_theme_adium_parent_class)->button_press_event (widget, event);
+  return TRUE;
 }
 
-static void
-theme_adium_set_show_avatars (EmpathyChatView *view,
+void
+empathy_theme_adium_set_show_avatars (EmpathyThemeAdium *self,
     gboolean show_avatars)
 {
-  EmpathyThemeAdium *self = EMPATHY_THEME_ADIUM (view);
-
   self->priv->show_avatars = show_avatars;
 }
 
-static void
-theme_adium_iface_init (EmpathyChatViewIface *iface)
-{
-  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;
-  iface->get_has_selection = theme_adium_get_has_selection;
-  iface->clear = theme_adium_clear;
-  iface->find_previous = theme_adium_find_previous;
-  iface->find_next = theme_adium_find_next;
-  iface->find_abilities = theme_adium_find_abilities;
-  iface->highlight = theme_adium_highlight;
-  iface->copy_clipboard = theme_adium_copy_clipboard;
-  iface->focus_toggled = theme_adium_focus_toggled;
-  iface->message_acknowledged = theme_adium_message_acknowledged;
-  iface->set_show_avatars = theme_adium_set_show_avatars;
-}
-
 static void
 theme_adium_load_finished_cb (WebKitWebView *view,
     WebKitWebFrame *frame,
     gpointer user_data)
 {
   EmpathyThemeAdium *self = EMPATHY_THEME_ADIUM (view);
-  EmpathyChatView *chat_view = EMPATHY_CHAT_VIEW (view);
   GList *l;
 
   DEBUG ("Page loaded");
@@ -1450,16 +1499,16 @@ theme_adium_load_finished_cb (WebKitWebView *view,
       switch (item->type)
         {
           case QUEUED_MESSAGE:
-            theme_adium_append_message (chat_view, item->msg,
+            empathy_theme_adium_append_message (self, item->msg,
               item->should_highlight);
             break;
 
           case QUEUED_EDIT:
-            theme_adium_edit_message (chat_view, item->msg);
+            empathy_theme_adium_edit_message (self, item->msg);
             break;
 
           case QUEUED_EVENT:
-            theme_adium_append_event (chat_view, item->str);
+            empathy_theme_adium_append_event (self, item->str);
             break;
         }
 
@@ -1479,6 +1528,8 @@ theme_adium_finalize (GObject *object)
   g_object_unref (self->priv->gsettings_chat);
   g_object_unref (self->priv->gsettings_desktop);
 
+  g_free (self->priv->variant);
+
   G_OBJECT_CLASS (empathy_theme_adium_parent_class)->finalize (object);
 }
 
@@ -1493,6 +1544,8 @@ theme_adium_dispose (GObject *object)
       self->priv->smiley_manager = NULL;
     }
 
+  g_clear_object (&self->priv->first_contact);
+
   if (self->priv->last_contact)
     {
       g_object_unref (self->priv->last_contact);
@@ -1670,7 +1723,6 @@ static void
 empathy_theme_adium_class_init (EmpathyThemeAdiumClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->finalize = theme_adium_finalize;
   object_class->dispose = theme_adium_dispose;
@@ -1678,8 +1730,6 @@ empathy_theme_adium_class_init (EmpathyThemeAdiumClass *klass)
   object_class->get_property = theme_adium_get_property;
   object_class->set_property = theme_adium_set_property;
 
-  widget_class->button_press_event = theme_adium_button_press_event;
-
   g_object_class_install_property (object_class, PROP_ADIUM_DATA,
       g_param_spec_boxed ("adium-data",
         "The theme data",
@@ -1719,17 +1769,12 @@ empathy_theme_adium_init (EmpathyThemeAdium *self)
       G_CALLBACK (theme_adium_load_finished_cb), NULL);
   g_signal_connect (self, "navigation-policy-decision-requested",
         G_CALLBACK (theme_adium_navigation_policy_decision_requested_cb), NULL);
+  g_signal_connect (self, "context-menu",
+      G_CALLBACK (theme_adium_context_menu_cb), NULL);
 
   self->priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
   self->priv->gsettings_desktop = g_settings_new (
     EMPATHY_PREFS_DESKTOP_INTERFACE_SCHEMA);
-
-  g_signal_connect (self->priv->gsettings_chat,
-    "changed::" EMPATHY_PREFS_CHAT_WEBKIT_DEVELOPER_TOOLS,
-    G_CALLBACK (theme_adium_notify_enable_webkit_developer_tools_cb),
-    self);
-
-  theme_adium_update_enable_webkit_developer_tools (self);
 }
 
 EmpathyThemeAdium *
@@ -1778,13 +1823,8 @@ void
 empathy_theme_adium_show_inspector (EmpathyThemeAdium *self)
 {
   WebKitWebView *web_view = WEBKIT_WEB_VIEW (self);
-  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);
+  empathy_webkit_show_inspector (web_view);
 }
 
 gboolean
@@ -1792,10 +1832,27 @@ empathy_adium_path_is_valid (const gchar *path)
 {
   gboolean ret;
   gchar *file;
+  gchar **tmp;
+  const gchar *dir;
 
   if (path[0] != '/')
     return FALSE;
 
+  /* The directory has to be *.AdiumMessageStyle per the Adium spec */
+  tmp = g_strsplit (path, "/", 0);
+  if (tmp == NULL)
+    return FALSE;
+
+  dir = tmp[g_strv_length (tmp) - 1];
+
+  if (!g_str_has_suffix (dir, ".AdiumMessageStyle"))
+    {
+      g_strfreev (tmp);
+      return FALSE;
+    }
+
+  g_strfreev (tmp);
+
   /* The theme is not valid if there is no Info.plist */
   file = g_build_filename (path, "Contents", "Info.plist",
          NULL);