]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-ui-utils.c
Merge branch 'sasl'
[empathy.git] / libempathy-gtk / empathy-ui-utils.c
index c737873d130385f79cb973682b009d68ef61882b..4c128742405b72274640e1b50b70b986c9594de2 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- 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
@@ -23,6 +23,7 @@
  *          Martyn Russell <martyn@imendio.com>
  *          Xavier Claessens <xclaesse@gmail.com>
  *          Jonny Lamb <jonny.lamb@collabora.co.uk>
+ *          Travis Reitter <travis.reitter@collabora.co.uk>
  *
  *          Part of this file is copied from GtkSourceView (gtksourceiter.c):
  *          Paolo Maggi
 #include <gtk/gtk.h>
 #include <gio/gio.h>
 
+#include <telepathy-glib/util.h>
+#include <folks/folks.h>
+
 #include "empathy-ui-utils.h"
 #include "empathy-images.h"
 #include "empathy-smiley-manager.h"
-#include "empathy-conf.h"
 
 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
 #include <libempathy/empathy-debug.h>
 #include <libempathy/empathy-idle.h>
 #include <libempathy/empathy-ft-factory.h>
 
-#define SCHEMES           "([a-zA-Z\\+]+)"
-#define INVALID_CHARS     " \n\"'"
-#define INVALID_CHARS_EXT INVALID_CHARS "\\[\\]<>(){},;:?"
-#define BODY              "([^"INVALID_CHARS"]+)"
-#define BODY_END          "([^"INVALID_CHARS"]*)[^"INVALID_CHARS_EXT".]"
-#define BODY_STRICT       "([^"INVALID_CHARS_EXT"]+)"
-#define URI_REGEX         "("SCHEMES"://"BODY_END")" \
-                         "|((www|ftp)\\."BODY_END")" \
-                         "|((mailto:)?"BODY_STRICT"@"BODY"\\."BODY_END")"
-
 void
 empathy_gtk_init (void)
 {
@@ -75,19 +68,6 @@ empathy_gtk_init (void)
        initialized = TRUE;
 }
 
-GRegex *
-empathy_uri_regex_dup_singleton (void)
-{
-       static GRegex *uri_regex = NULL;
-
-       /* We intentionally leak the regex so it's not recomputed */
-       if (!uri_regex) {
-               uri_regex = g_regex_new (URI_REGEX, 0, 0, NULL);
-       }
-
-       return g_regex_ref (uri_regex);
-}
-
 static GtkBuilder *
 builder_get_file_valist (const gchar *filename,
                         const gchar *first_object,
@@ -101,6 +81,7 @@ builder_get_file_valist (const gchar *filename,
        DEBUG ("Loading file %s", filename);
 
        gui = gtk_builder_new ();
+       gtk_builder_set_translation_domain (gui, GETTEXT_PACKAGE);
        if (!gtk_builder_add_from_file (gui, filename, &error)) {
                g_critical ("GtkBuilder Error (%s): %s",
                                filename, error->message);
@@ -148,9 +129,9 @@ empathy_builder_get_file (const gchar *filename,
 }
 
 void
-empathy_builder_connect (GtkBuilder *gui,
-                        gpointer    user_data,
-                        gchar      *first_object,
+empathy_builder_connect (GtkBuilder  *gui,
+                        gpointer     user_data,
+                        const gchar *first_object,
                         ...)
 {
        va_list      args;
@@ -204,14 +185,28 @@ empathy_icon_name_for_presence (TpConnectionPresenceType presence)
        case TP_CONNECTION_PRESENCE_TYPE_AWAY:
                return EMPATHY_IMAGE_AWAY;
        case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
-               return EMPATHY_IMAGE_EXT_AWAY;
+               if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
+                                            EMPATHY_IMAGE_EXT_AWAY))
+                       return EMPATHY_IMAGE_EXT_AWAY;
+
+               /* The 'extended-away' icon is not an official one so we fallback to idle if
+                * it's not implemented */
+               return EMPATHY_IMAGE_IDLE;
        case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
-               return EMPATHY_IMAGE_HIDDEN;
+               if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
+                                            EMPATHY_IMAGE_HIDDEN))
+                       return EMPATHY_IMAGE_HIDDEN;
+
+               /* The 'hidden' icon is not an official one so we fallback to offline if
+                * it's not implemented */
+               return EMPATHY_IMAGE_OFFLINE;
        case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
        case TP_CONNECTION_PRESENCE_TYPE_ERROR:
-       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
                return EMPATHY_IMAGE_OFFLINE;
+       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
+               return EMPATHY_IMAGE_PENDING;
        case TP_CONNECTION_PRESENCE_TYPE_UNSET:
+       default:
                return NULL;
        }
 
@@ -230,6 +225,34 @@ empathy_icon_name_for_contact (EmpathyContact *contact)
        return empathy_icon_name_for_presence (presence);
 }
 
+const gchar *
+empathy_icon_name_for_individual (FolksIndividual *individual)
+{
+       FolksPresenceType folks_presence;
+       TpConnectionPresenceType presence;
+
+       folks_presence =
+           folks_presence_get_presence_type (FOLKS_PRESENCE (individual));
+       presence = empathy_folks_presence_type_to_tp (folks_presence);
+
+       return empathy_icon_name_for_presence (presence);
+}
+
+const gchar *
+empathy_protocol_name_for_contact (EmpathyContact   *contact)
+{
+       TpAccount     *account;
+
+       g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
+
+       account = empathy_contact_get_account (contact);
+       if (account == NULL) {
+               return NULL;
+       }
+
+       return tp_account_get_icon_name (account);
+}
+
 GdkPixbuf *
 empathy_pixbuf_from_data (gchar *data,
                          gsize  data_size)
@@ -416,6 +439,35 @@ empathy_gdk_pixbuf_is_opaque (GdkPixbuf *pixbuf)
        return TRUE;
 }
 
+static GdkPixbuf *
+avatar_pixbuf_from_loader (GdkPixbufLoader *loader)
+{
+       GdkPixbuf *pixbuf;
+
+       pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+       if (!gdk_pixbuf_get_has_alpha (pixbuf)) {
+               GdkPixbuf *rounded_pixbuf;
+
+               rounded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+                                                gdk_pixbuf_get_width (pixbuf),
+                                                gdk_pixbuf_get_height (pixbuf));
+               gdk_pixbuf_copy_area (pixbuf, 0, 0,
+                                     gdk_pixbuf_get_width (pixbuf),
+                                     gdk_pixbuf_get_height (pixbuf),
+                                     rounded_pixbuf,
+                                     0, 0);
+               pixbuf = rounded_pixbuf;
+       } else {
+               g_object_ref (pixbuf);
+       }
+
+       if (empathy_gdk_pixbuf_is_opaque (pixbuf)) {
+               empathy_avatar_pixbuf_roundify (pixbuf);
+       }
+
+       return pixbuf;
+}
+
 GdkPixbuf *
 empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avatar,
                                  gint          width,
@@ -449,27 +501,7 @@ empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avatar,
        }
 
        gdk_pixbuf_loader_close (loader, NULL);
-
-       pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
-       if (!gdk_pixbuf_get_has_alpha (pixbuf)) {
-               GdkPixbuf *rounded_pixbuf;
-
-               rounded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
-                                                gdk_pixbuf_get_width (pixbuf),
-                                                gdk_pixbuf_get_height (pixbuf));
-               gdk_pixbuf_copy_area (pixbuf, 0, 0,
-                                     gdk_pixbuf_get_width (pixbuf),
-                                     gdk_pixbuf_get_height (pixbuf),
-                                     rounded_pixbuf,
-                                     0, 0);
-               pixbuf = rounded_pixbuf;
-       } else {
-               g_object_ref (pixbuf);
-       }
-
-       if (empathy_gdk_pixbuf_is_opaque (pixbuf)) {
-               empathy_avatar_pixbuf_roundify (pixbuf);
-       }
+       pixbuf = avatar_pixbuf_from_loader (loader);
 
        g_object_unref (loader);
 
@@ -490,6 +522,264 @@ empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact *contact,
        return empathy_pixbuf_from_avatar_scaled (avatar, width, height);
 }
 
+typedef struct {
+       FolksIndividual *individual;
+       GSimpleAsyncResult *result;
+       guint width;
+       guint height;
+} PixbufAvatarFromIndividualClosure;
+
+static PixbufAvatarFromIndividualClosure *
+pixbuf_avatar_from_individual_closure_new (FolksIndividual    *individual,
+                                          GSimpleAsyncResult *result,
+                                          gint                width,
+                                          gint                height)
+{
+       PixbufAvatarFromIndividualClosure *closure;
+
+       g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
+       g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+       closure = g_new0 (PixbufAvatarFromIndividualClosure, 1);
+       closure->individual = g_object_ref (individual);
+       closure->result = g_object_ref (result);
+       closure->width = width;
+       closure->height = height;
+
+       return closure;
+}
+
+static void
+pixbuf_avatar_from_individual_closure_free (
+               PixbufAvatarFromIndividualClosure *closure)
+{
+       g_object_unref (closure->individual);
+       g_object_unref (closure->result);
+       g_free (closure);
+}
+
+static void
+avatar_file_load_contents_cb (GObject      *object,
+                             GAsyncResult *result,
+                             gpointer      user_data)
+{
+       GFile *file = G_FILE (object);
+       PixbufAvatarFromIndividualClosure *closure = user_data;
+       char *data = NULL;
+       gsize data_size;
+       struct SizeData size_data;
+       GError *error = NULL;
+       GdkPixbufLoader *loader = NULL;
+
+       if (!g_file_load_contents_finish (file, result, &data, &data_size,
+                               NULL, &error)) {
+               DEBUG ("failed to load avatar from file: %s",
+                               error->message);
+               g_simple_async_result_set_from_error (closure->result, error);
+               goto out;
+       }
+
+       size_data.width = closure->width;
+       size_data.height = closure->height;
+       size_data.preserve_aspect_ratio = TRUE;
+
+       loader = gdk_pixbuf_loader_new ();
+
+       g_signal_connect (loader, "size-prepared",
+                         G_CALLBACK (pixbuf_from_avatar_size_prepared_cb),
+                         &size_data);
+
+       if (!gdk_pixbuf_loader_write (loader, (guchar *) data, data_size,
+                               &error)) {
+               DEBUG ("Failed to write to pixbuf loader: %s",
+                       error ? error->message : "No error given");
+               g_simple_async_result_set_from_error (closure->result, error);
+               goto out;
+       }
+       if (!gdk_pixbuf_loader_close (loader, &error)) {
+               DEBUG ("Failed to close pixbuf loader: %s",
+                       error ? error->message : "No error given");
+               g_simple_async_result_set_from_error (closure->result, error);
+               goto out;
+       }
+
+       g_simple_async_result_set_op_res_gpointer (closure->result,
+                       avatar_pixbuf_from_loader (loader), g_object_unref);
+
+out:
+       g_simple_async_result_complete (closure->result);
+
+       g_clear_error (&error);
+       g_free (data);
+       tp_clear_object (&loader);
+       pixbuf_avatar_from_individual_closure_free (closure);
+}
+
+void
+empathy_pixbuf_avatar_from_individual_scaled_async (
+               FolksIndividual     *individual,
+               gint                 width,
+               gint                 height,
+               GCancellable        *cancellable,
+               GAsyncReadyCallback  callback,
+               gpointer             user_data)
+{
+       GFile *avatar_file;
+       GSimpleAsyncResult *result;
+       PixbufAvatarFromIndividualClosure *closure;
+
+       result = g_simple_async_result_new (G_OBJECT (individual),
+                       callback, user_data,
+                       empathy_pixbuf_avatar_from_individual_scaled_async);
+
+       avatar_file = folks_avatar_get_avatar (FOLKS_AVATAR (individual));
+       if (avatar_file == NULL)
+               goto out;
+
+       closure = pixbuf_avatar_from_individual_closure_new (individual, result,
+                                                            width, height);
+       if (closure == NULL)
+               goto out;
+
+       g_file_load_contents_async (avatar_file, cancellable,
+                       avatar_file_load_contents_cb, closure);
+
+       g_object_unref (result);
+
+       return;
+
+out:
+       g_simple_async_result_set_op_res_gpointer (result, NULL, NULL);
+       g_simple_async_result_complete (result);
+       g_object_unref (result);
+}
+
+/* Return a ref on the GdkPixbuf */
+GdkPixbuf *
+empathy_pixbuf_avatar_from_individual_scaled_finish (
+               FolksIndividual *individual,
+               GAsyncResult *result,
+               GError **error)
+{
+       GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+       gboolean result_valid;
+       GdkPixbuf *pixbuf;
+
+       g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
+       g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), NULL);
+
+       if (g_simple_async_result_propagate_error (simple, error))
+               return NULL;
+
+       result_valid = g_simple_async_result_is_valid (result,
+                       G_OBJECT (individual),
+                       empathy_pixbuf_avatar_from_individual_scaled_async);
+       g_return_val_if_fail (result_valid, NULL);
+
+       pixbuf = g_simple_async_result_get_op_res_gpointer (simple);
+       return pixbuf != NULL ? g_object_ref (pixbuf) : NULL;
+}
+
+GdkPixbuf *
+empathy_pixbuf_contact_status_icon (EmpathyContact *contact,
+                                  gboolean       show_protocol)
+{
+       const gchar *icon_name;
+
+       g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
+
+       icon_name = empathy_icon_name_for_contact (contact);
+
+       if (icon_name == NULL) {
+               return NULL;
+       }
+       return empathy_pixbuf_contact_status_icon_with_icon_name (contact,
+           icon_name,
+           show_protocol);
+}
+
+GdkPixbuf *
+empathy_pixbuf_contact_status_icon_with_icon_name (EmpathyContact *contact,
+                                         const gchar    *icon_name,
+                                         gboolean       show_protocol)
+{
+       GdkPixbuf *pix_status;
+       GdkPixbuf *pix_protocol;
+       gchar     *icon_filename;
+       gint       height, width;
+       gint       numerator, denominator;
+
+       g_return_val_if_fail (EMPATHY_IS_CONTACT (contact) ||
+                       (show_protocol == FALSE), NULL);
+       g_return_val_if_fail (icon_name != NULL, NULL);
+
+       numerator = 3;
+       denominator = 4;
+
+       icon_filename = empathy_filename_from_icon_name (icon_name,
+                                                        GTK_ICON_SIZE_MENU);
+       if (icon_filename == NULL) {
+               DEBUG ("icon name: %s could not be found\n", icon_name);
+               return NULL;
+       }
+
+       pix_status = gdk_pixbuf_new_from_file (icon_filename, NULL);
+
+       if (pix_status == NULL) {
+               DEBUG ("Could not open icon %s\n", icon_filename);
+               g_free (icon_filename);
+               return NULL;
+       }
+
+       g_free (icon_filename);
+
+       if (!show_protocol)
+               return pix_status;
+
+       height = gdk_pixbuf_get_height (pix_status);
+       width = gdk_pixbuf_get_width (pix_status);
+
+       pix_protocol = empathy_pixbuf_protocol_from_contact_scaled (contact,
+                                                                   width * numerator / denominator,
+                                                                   height * numerator / denominator);
+
+       if (pix_protocol == NULL) {
+               return pix_status;
+       }
+       gdk_pixbuf_composite (pix_protocol, pix_status,
+           0, height - height * numerator / denominator,
+           width * numerator / denominator, height * numerator / denominator,
+           0, height - height * numerator / denominator,
+           1, 1,
+           GDK_INTERP_BILINEAR, 255);
+
+       g_object_unref (pix_protocol);
+
+       return pix_status;
+}
+
+GdkPixbuf *
+empathy_pixbuf_protocol_from_contact_scaled (EmpathyContact *contact,
+                                         gint           width,
+                                         gint           height)
+{
+       TpAccount *account;
+       gchar     *filename;
+       GdkPixbuf *pixbuf = NULL;
+
+       g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
+
+       account = empathy_contact_get_account (contact);
+       filename = empathy_filename_from_icon_name (tp_account_get_icon_name (account),
+                                                   GTK_ICON_SIZE_MENU);
+       if (filename != NULL) {
+               pixbuf = gdk_pixbuf_new_from_file_at_size (filename, width, height, NULL);
+               g_free (filename);
+       }
+
+       return pixbuf;
+}
+
 GdkPixbuf *
 empathy_pixbuf_scale_down_if_necessary (GdkPixbuf *pixbuf, gint max_size)
 {
@@ -1274,7 +1564,7 @@ empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon)
 
        gtk_status_icon_get_geometry (status_icon, NULL, &icon_location, NULL);
        gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
-       dpy = gdk_x11_drawable_get_xdisplay (gdk_window);
+       dpy = GDK_WINDOW_XDISPLAY (gdk_window);
 
        data[0] = icon_location.x;
        data[1] = icon_location.y;
@@ -1283,7 +1573,8 @@ empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon)
 
        XChangeProperty (dpy,
                         GDK_WINDOW_XID (gdk_window),
-                        gdk_x11_get_xatom_by_name_for_display (gdk_drawable_get_display (gdk_window),
+                        gdk_x11_get_xatom_by_name_for_display (
+                               gdk_window_get_display (gdk_window),
                         "_NET_WM_ICON_GEOMETRY"),
                         XA_CARDINAL, 32, PropModeReplace,
                         (guchar *)&data, 4);
@@ -1294,10 +1585,9 @@ empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon)
 
 /* Takes care of moving the window to the current workspace. */
 void
-empathy_window_present (GtkWindow *window,
-                       gboolean   steal_focus)
+empathy_window_present_with_time (GtkWindow *window,
+                       guint32 timestamp)
 {
-       guint32 timestamp;
        GdkWindow *gdk_window;
 
        g_return_if_fail (GTK_IS_WINDOW (window));
@@ -1320,12 +1610,21 @@ empathy_window_present (GtkWindow *window,
                        gtk_widget_hide (GTK_WIDGET (window));
        }
 
-       timestamp = gtk_get_current_event_time ();
-       gtk_window_present_with_time (window, timestamp);
+       if (timestamp == GDK_CURRENT_TIME)
+               gtk_window_present (window);
+       else
+               gtk_window_present_with_time (window, timestamp);
+
        gtk_window_set_skip_taskbar_hint (window, FALSE);
        gtk_window_deiconify (window);
 }
 
+void
+empathy_window_present (GtkWindow *window)
+{
+  empathy_window_present_with_time (window, gtk_get_current_event_time ());
+}
+
 GtkWindow *
 empathy_get_toplevel_window (GtkWidget *widget)
 {
@@ -1419,39 +1718,56 @@ empathy_url_show (GtkWidget *parent,
        g_free (real_url);
 }
 
-static void
-link_button_hook (GtkLinkButton *button,
-                 const gchar *link,
-                 gpointer user_data)
+void
+empathy_send_file (EmpathyContact *contact, GFile *file)
 {
-       empathy_url_show (GTK_WIDGET (button), link);
-}
+       EmpathyFTFactory *factory;
+       GtkRecentManager *manager;
+       gchar *uri;
 
-GtkWidget *
-empathy_link_button_new (const gchar *url,
-                       const gchar *title)
-{
-       static gboolean hook = FALSE;
+       g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+       g_return_if_fail (G_IS_FILE (file));
 
-       if (!hook) {
-               hook = TRUE;
-               gtk_link_button_set_uri_hook (link_button_hook, NULL, NULL);
-       }
+       factory = empathy_ft_factory_dup_singleton ();
+
+       empathy_ft_factory_new_transfer_outgoing (factory, contact, file);
 
-       return gtk_link_button_new_with_label (url, title);
+       uri = g_file_get_uri (file);
+       manager = gtk_recent_manager_get_default ();
+       gtk_recent_manager_add_item (manager, uri);
+       g_free (uri);
+
+       g_object_unref (factory);
 }
 
 void
-empathy_toggle_button_set_state_quietly (GtkWidget *widget,
-                                       GCallback  callback,
-                                       gpointer   user_data,
-                                       gboolean   active)
+empathy_send_file_from_uri_list (EmpathyContact *contact, const gchar *uri_list)
 {
-       g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
+       const gchar *nl;
+       GFile *file;
 
-       g_signal_handlers_block_by_func (widget, callback, user_data);
-       g_object_set (widget, "active", active, NULL);
-       g_signal_handlers_unblock_by_func (widget, callback, user_data);
+       /* Only handle a single file for now.  It would be wicked cool to be
+          able to do multiple files, offering to zip them or whatever like
+          nautilus-sendto does.  Note that text/uri-list is defined to have
+          each line terminated by \r\n, but we can be tolerant of applications
+          that only use \n or don't terminate single-line entries.
+       */
+       nl = strstr (uri_list, "\r\n");
+       if (!nl) {
+               nl = strchr (uri_list, '\n');
+       }
+       if (nl) {
+               gchar *uri = g_strndup (uri_list, nl - uri_list);
+               file = g_file_new_for_uri (uri);
+               g_free (uri);
+       }
+       else {
+               file = g_file_new_for_uri (uri_list);
+       }
+
+       empathy_send_file (contact, file);
+
+       g_object_unref (file);
 }
 
 static void
@@ -1459,25 +1775,13 @@ file_manager_send_file_response_cb (GtkDialog      *widget,
                                    gint            response_id,
                                    EmpathyContact *contact)
 {
-       EmpathyFTFactory *factory;
        GFile *file;
-       gchar *uri;
-       GtkRecentManager *manager;
 
        if (response_id == GTK_RESPONSE_OK) {
                file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget));
-               uri = g_file_get_uri (file);
-
-               factory = empathy_ft_factory_dup_singleton ();
 
-               empathy_ft_factory_new_transfer_outgoing (factory, contact,
-                                                         file);
+               empathy_send_file (contact, file);
 
-               manager = gtk_recent_manager_get_default ();
-               gtk_recent_manager_add_item (manager, uri);
-
-               g_free (uri);
-               g_object_unref (factory);
                g_object_unref (file);
        }
 
@@ -1515,6 +1819,9 @@ empathy_send_file_with_file_chooser (EmpathyContact *contact)
 
        gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), FALSE);
 
+       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget),
+               g_get_home_dir ());
+
        g_signal_connect (widget, "response",
                          G_CALLBACK (file_manager_send_file_response_cb),
                          contact);
@@ -1553,8 +1860,17 @@ void
 empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler)
 {
        GtkWidget *widget;
+       const gchar *dir;
+       EmpathyContact *contact;
+       gchar *title;
 
-       widget = gtk_file_chooser_dialog_new (_("Select a destination"),
+       contact = empathy_ft_handler_get_contact (handler);
+       g_assert (contact != NULL);
+
+       title = g_strdup_printf (_("Incoming file from %s"),
+               empathy_contact_get_alias (contact));
+
+       widget = gtk_file_chooser_dialog_new (title,
                                              NULL,
                                              GTK_FILE_CHOOSER_ACTION_SAVE,
                                              GTK_STOCK_CANCEL,
@@ -1567,113 +1883,16 @@ empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler)
        gtk_file_chooser_set_do_overwrite_confirmation
                (GTK_FILE_CHOOSER (widget), TRUE);
 
+       dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
+       if (dir == NULL)
+               /* Fallback to $HOME if $XDG_DOWNLOAD_DIR is not set */
+               dir = g_get_home_dir ();
+
+       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), dir);
+
        g_signal_connect (widget, "response",
                G_CALLBACK (file_manager_receive_file_response_cb), handler);
 
        gtk_widget_show (widget);
+       g_free (title);
 }
-
-void
-empathy_string_parser_substr (const gchar *text,
-                             gssize len,
-                             EmpathyStringParser *parsers,
-                             gpointer user_data)
-{
-       if (parsers != NULL && parsers[0].match_func != NULL) {
-               parsers[0].match_func (text, len,
-                                      parsers[0].replace_func, parsers + 1,
-                                      user_data);
-       }
-}
-
-void
-empathy_string_match_link (const gchar *text,
-                          gssize len,
-                          EmpathyStringReplace replace_func,
-                          EmpathyStringParser *sub_parsers,
-                          gpointer user_data)
-{
-       GRegex     *uri_regex;
-       GMatchInfo *match_info;
-       gboolean    match;
-       gint        last = 0;
-
-       uri_regex = empathy_uri_regex_dup_singleton ();
-       match = g_regex_match_full (uri_regex, text, len, 0, 0, &match_info, NULL);
-       if (match) {
-               gint s = 0, e = 0;
-
-               do {
-                       g_match_info_fetch_pos (match_info, 0, &s, &e);
-
-                       if (s > last) {
-                               /* Append the text between last link (or the
-                                * start of the message) and this link */
-                               empathy_string_parser_substr (text + last,
-                                                             s - last,
-                                                             sub_parsers,
-                                                             user_data);
-                       }
-
-                       replace_func (text + s, e - s, NULL, user_data);
-
-                       last = e;
-               } while (g_match_info_next (match_info, NULL));
-       }
-
-       empathy_string_parser_substr (text + last, len - last,
-                                     sub_parsers, user_data);
-
-       g_match_info_free (match_info);
-       g_regex_unref (uri_regex);
-}
-
-void
-empathy_string_match_smiley (const gchar *text,
-                            gssize len,
-                            EmpathyStringReplace replace_func,
-                            EmpathyStringParser *sub_parsers,
-                            gpointer user_data)
-{
-       guint last = 0;
-       EmpathySmileyManager *smiley_manager;
-       GSList *hits, *l;
-
-       smiley_manager = empathy_smiley_manager_dup_singleton ();
-       hits = empathy_smiley_manager_parse_len (smiley_manager, text, len);
-
-       for (l = hits; l; l = l->next) {
-               EmpathySmileyHit *hit = l->data;
-
-               if (hit->start > last) {
-                       /* Append the text between last smiley (or the
-                        * start of the message) and this smiley */
-                       empathy_string_parser_substr (text + last,
-                                                     hit->start - last,
-                                                     sub_parsers, user_data);
-               }
-
-               replace_func (text + hit->start, hit->end - hit->start,
-                             hit, user_data);
-
-               last = hit->end;
-
-               empathy_smiley_hit_free (hit);
-       }
-       g_slist_free (hits);
-       g_object_unref (smiley_manager);
-
-       empathy_string_parser_substr (text + last, len - last,
-                                     sub_parsers, user_data);
-}
-
-void
-empathy_string_match_all (const gchar *text,
-                         gssize len,
-                         EmpathyStringReplace replace_func,
-                         EmpathyStringParser *sub_parsers,
-                         gpointer user_data)
-{
-       replace_func (text, len, NULL, user_data);
-}
-