]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-ui-utils.c
Merge remote-tracking branch 'pochu/misc-fixes'
[empathy.git] / libempathy-gtk / empathy-ui-utils.c
index 810bbfc265e1af207a708bb2516ca18bf7d3d03c..32ad451a12115918492780ae78e8ec7854580036 100644 (file)
@@ -526,13 +526,18 @@ typedef struct {
        GSimpleAsyncResult *result;
        guint width;
        guint height;
+       struct SizeData size_data;
+       GdkPixbufLoader *loader;
+       GCancellable *cancellable;
+       guint8 data[512];
 } PixbufAvatarFromIndividualClosure;
 
 static PixbufAvatarFromIndividualClosure *
 pixbuf_avatar_from_individual_closure_new (FolksIndividual    *individual,
                                           GSimpleAsyncResult *result,
                                           gint                width,
-                                          gint                height)
+                                          gint                height,
+                                          GCancellable       *cancellable)
 {
        PixbufAvatarFromIndividualClosure *closure;
 
@@ -544,6 +549,7 @@ pixbuf_avatar_from_individual_closure_new (FolksIndividual    *individual,
        closure->result = g_object_ref (result);
        closure->width = width;
        closure->height = height;
+       closure->cancellable = g_object_ref (cancellable);
 
        return closure;
 }
@@ -552,65 +558,140 @@ static void
 pixbuf_avatar_from_individual_closure_free (
                PixbufAvatarFromIndividualClosure *closure)
 {
+       g_object_unref (closure->cancellable);
+       tp_clear_object (&closure->loader);
        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)
+avatar_icon_load_close_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;
-       }
+       g_input_stream_close_finish (G_INPUT_STREAM (object), result, &error);
 
-       size_data.width = closure->width;
-       size_data.height = closure->height;
-       size_data.preserve_aspect_ratio = TRUE;
+       if (error != NULL) {
+               DEBUG ("Failed to close pixbuf stream: %s", error->message);
+               g_error_free (error);
+       }
+}
 
-       loader = gdk_pixbuf_loader_new ();
+static void
+avatar_icon_load_read_cb (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+       GInputStream *stream = G_INPUT_STREAM (object);
+       PixbufAvatarFromIndividualClosure *closure = user_data;
+       gssize n_read;
+       GError *error = NULL;
 
-       g_signal_connect (loader, "size-prepared",
-                         G_CALLBACK (pixbuf_from_avatar_size_prepared_cb),
-                         &size_data);
+       /* Finish reading this chunk from the stream */
+       n_read = g_input_stream_read_finish (stream, result, &error);
+       if (error != NULL) {
+               DEBUG ("Failed to finish read from pixbuf stream: %s",
+                       error->message);
+               g_simple_async_result_set_from_error (closure->result, error);
+               goto out_close;
+       }
 
-       if (!gdk_pixbuf_loader_write (loader, (guchar *) data, data_size,
-                               &error)) {
+       /* Write the chunk to the pixbuf loader */
+       if (!gdk_pixbuf_loader_write (closure->loader, (guchar *) closure->data,
+                       n_read, &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_close;
+       }
+
+       if (n_read == 0) {
+               /* EOF? */
+               if (!gdk_pixbuf_loader_close (closure->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;
+               }
+
+               /* We're done. */
+               g_simple_async_result_set_op_res_gpointer (closure->result,
+                       avatar_pixbuf_from_loader (closure->loader),
+                       g_object_unref);
+
                goto out;
+       } else {
+               /* Loop round and read another chunk. */
+               g_input_stream_read_async (stream, closure->data,
+                       G_N_ELEMENTS (closure->data),
+                       G_PRIORITY_DEFAULT, closure->cancellable,
+                       avatar_icon_load_read_cb, closure);
+
+               return;
        }
-       if (!gdk_pixbuf_loader_close (loader, &error)) {
-               DEBUG ("Failed to close pixbuf loader: %s",
-                       error ? error->message : "No error given");
+
+out_close:
+       /* We must close the pixbuf loader before unreffing it. */
+       gdk_pixbuf_loader_close (closure->loader, NULL);
+
+out:
+       /* Close the file for safety (even though it should be
+        * automatically closed when the stream is finalised). */
+       g_input_stream_close_async (stream, G_PRIORITY_DEFAULT, NULL,
+               (GAsyncReadyCallback) avatar_icon_load_close_cb, NULL);
+
+       g_simple_async_result_complete (closure->result);
+
+       g_clear_error (&error);
+       pixbuf_avatar_from_individual_closure_free (closure);
+}
+
+static void
+avatar_icon_load_cb (GObject      *object,
+                     GAsyncResult *result,
+                     gpointer      user_data)
+{
+       GLoadableIcon *icon = G_LOADABLE_ICON (object);
+       PixbufAvatarFromIndividualClosure *closure = user_data;
+       GInputStream *stream;
+       GError *error = NULL;
+
+       stream = g_loadable_icon_load_finish (icon, result, NULL, &error);
+       if (error != NULL) {
+               DEBUG ("Failed to open avatar stream: %s", error->message);
                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);
+       closure->size_data.width = closure->width;
+       closure->size_data.height = closure->height;
+       closure->size_data.preserve_aspect_ratio = TRUE;
+
+       /* Load the data into a pixbuf loader in chunks. */
+       closure->loader = gdk_pixbuf_loader_new ();
+
+       g_signal_connect (closure->loader, "size-prepared",
+                         G_CALLBACK (pixbuf_from_avatar_size_prepared_cb),
+                         &(closure->size_data));
+
+       /* Begin to read the first chunk. */
+       g_input_stream_read_async (stream, closure->data,
+                       G_N_ELEMENTS (closure->data),
+                       G_PRIORITY_DEFAULT, closure->cancellable,
+                       avatar_icon_load_read_cb, closure);
+
+       g_object_unref (stream);
+
+       return;
 
 out:
        g_simple_async_result_complete (closure->result);
 
        g_clear_error (&error);
-       g_free (data);
-       tp_clear_object (&loader);
+       tp_clear_object (&stream);
        pixbuf_avatar_from_individual_closure_free (closure);
 }
 
@@ -623,7 +704,7 @@ empathy_pixbuf_avatar_from_individual_scaled_async (
                GAsyncReadyCallback  callback,
                gpointer             user_data)
 {
-       GFile *avatar_file;
+       GLoadableIcon *avatar_icon;
        GSimpleAsyncResult *result;
        PixbufAvatarFromIndividualClosure *closure;
 
@@ -631,18 +712,19 @@ empathy_pixbuf_avatar_from_individual_scaled_async (
                        callback, user_data,
                        empathy_pixbuf_avatar_from_individual_scaled_async);
 
-       avatar_file =
+       avatar_icon =
                folks_avatar_details_get_avatar (FOLKS_AVATAR_DETAILS (individual));
-       if (avatar_file == NULL)
+       if (avatar_icon == NULL)
                goto out;
 
        closure = pixbuf_avatar_from_individual_closure_new (individual, result,
-                                                            width, height);
+                                                            width, height,
+                                                            cancellable);
        if (closure == NULL)
                goto out;
 
-       g_file_load_contents_async (avatar_file, cancellable,
-                       avatar_file_load_contents_cb, closure);
+       g_loadable_icon_load_async (avatar_icon, width, cancellable,
+                       avatar_icon_load_cb, closure);
 
        g_object_unref (result);
 
@@ -865,6 +947,9 @@ empathy_filename_from_icon_name (const gchar *icon_name,
        }
 
        icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, 0);
+       if (icon_info == NULL)
+               return NULL;
+
        ret = g_strdup (gtk_icon_info_get_filename (icon_info));
        gtk_icon_info_free (icon_info);
 
@@ -1677,7 +1762,8 @@ empathy_send_file (EmpathyContact *contact, GFile *file)
 
        factory = empathy_ft_factory_dup_singleton ();
 
-       empathy_ft_factory_new_transfer_outgoing (factory, contact, file);
+       empathy_ft_factory_new_transfer_outgoing (factory, contact, file,
+               empathy_get_current_action_time ());
 
        uri = g_file_get_uri (file);
        manager = gtk_recent_manager_get_default ();
@@ -2013,12 +2099,25 @@ empathy_individual_match_string (FolksIndividual *individual,
   return retval;
 }
 
+static gboolean
+dtmf_dialpad_button_pressed_cb (GObject *button,
+    GtkEntry *entry)
+{
+  GtkEntryBuffer *buffer = gtk_entry_get_buffer (entry);
+  const gchar *label;
+
+  label = g_object_get_data (button, "label");
+  gtk_entry_buffer_insert_text (buffer, -1, label, -1);
+
+  return FALSE;
+}
+
 GtkWidget *
 empathy_create_dtmf_dialpad (GObject *self,
     GCallback dtmf_button_pressed_cb,
     GCallback dtmf_button_released_cb)
 {
-  GtkWidget *table;
+  GtkWidget *box, *entry, *table;
   int i;
   GQuark button_quark;
   struct {
@@ -2039,6 +2138,13 @@ empathy_create_dtmf_dialpad (GObject *self,
                       { "*", "",     TP_DTMF_EVENT_ASTERISK },
                       { NULL, } };
 
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
+
+  entry = gtk_entry_new ();
+  gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE);
+
+  gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 3);
+
   button_quark = g_quark_from_static_string (EMPATHY_DTMF_BUTTON_ID);
 
   table = gtk_table_new (4, 3, TRUE);
@@ -2059,6 +2165,9 @@ empathy_create_dtmf_dialpad (GObject *self,
       gtk_label_set_markup (GTK_LABEL (label), str);
       g_free (str);
 
+      g_object_set_data (G_OBJECT (button), "label",
+          (gpointer) dtmfbuttons[i].label);
+
       gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 3);
 
       /* sub label */
@@ -2077,11 +2186,69 @@ empathy_create_dtmf_dialpad (GObject *self,
       g_object_set_qdata (G_OBJECT (button), button_quark,
         GUINT_TO_POINTER (dtmfbuttons[i].event));
 
+      /* To update the GtkEntry */
+      g_signal_connect (G_OBJECT (button), "pressed",
+        G_CALLBACK (dtmf_dialpad_button_pressed_cb), entry);
+
       g_signal_connect (G_OBJECT (button), "pressed",
         dtmf_button_pressed_cb, self);
       g_signal_connect (G_OBJECT (button), "released",
         dtmf_button_released_cb, self);
     }
 
-  return table;
+  gtk_box_pack_start (GTK_BOX (box), table, FALSE, FALSE, 3);
+
+  return box;
+}
+
+void
+empathy_launch_program (const gchar *dir,
+    const gchar *name,
+    const gchar *args)
+{
+  GdkDisplay *display;
+  GError *error = NULL;
+  gchar *path, *cmd;
+  GAppInfo *app_info;
+  GdkAppLaunchContext *context = NULL;
+
+  /* Try to run from source directory if possible */
+  path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src",
+      name, NULL);
+
+  if (!g_file_test (path, G_FILE_TEST_EXISTS))
+    {
+      g_free (path);
+      path = g_build_filename (dir, name, NULL);
+    }
+
+  if (args != NULL)
+    cmd = g_strconcat (path, " ", args, NULL);
+  else
+    cmd = g_strdup (path);
+
+  app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &error);
+  if (app_info == NULL)
+    {
+      DEBUG ("Failed to create app info: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  display = gdk_display_get_default ();
+  context = gdk_display_get_app_launch_context (display);
+
+  if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
+      &error))
+    {
+      g_warning ("Failed to launch %s: %s", name, error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+out:
+  tp_clear_object (&app_info);
+  tp_clear_object (&context);
+  g_free (path);
+  g_free (cmd);
 }