2 * Copyright (C) 2002-2007 Imendio AB
3 * Copyright (C) 2007-2010 Collabora Ltd.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301 USA
20 * Authors: Mikael Hallendal <micke@imendio.com>
21 * Richard Hult <richard@imendio.com>
22 * Martyn Russell <martyn@imendio.com>
23 * Xavier Claessens <xclaesse@gmail.com>
24 * Jonny Lamb <jonny.lamb@collabora.co.uk>
25 * Travis Reitter <travis.reitter@collabora.co.uk>
27 * Part of this file is copied from GtkSourceView (gtksourceiter.c):
33 #include "empathy-ui-utils.h"
35 #include <X11/Xatom.h>
37 #include <glib/gi18n-lib.h>
38 #include <gio/gdesktopappinfo.h>
39 #include <tp-account-widgets/tpaw-live-search.h>
40 #include <tp-account-widgets/tpaw-pixbuf-utils.h>
41 #include <tp-account-widgets/tpaw-utils.h>
43 #include "empathy-ft-factory.h"
44 #include "empathy-images.h"
45 #include "empathy-utils.h"
47 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
48 #include "empathy-debug.h"
51 empathy_gtk_init (void)
53 static gboolean initialized = FALSE;
60 gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
61 PKGDATADIR G_DIR_SEPARATOR_S "icons");
63 /* Add icons from source dir if available */
64 if (g_getenv ("EMPATHY_SRCDIR") != NULL)
68 path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "data",
69 "icons", "local-copy", NULL);
71 if (g_file_test (path, G_FILE_TEST_EXISTS))
72 gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), path);
81 empathy_icon_name_for_presence (TpConnectionPresenceType presence)
85 case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
86 return EMPATHY_IMAGE_AVAILABLE;
87 case TP_CONNECTION_PRESENCE_TYPE_BUSY:
88 return EMPATHY_IMAGE_BUSY;
89 case TP_CONNECTION_PRESENCE_TYPE_AWAY:
90 return EMPATHY_IMAGE_AWAY;
91 case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
92 if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
93 EMPATHY_IMAGE_EXT_AWAY))
94 return EMPATHY_IMAGE_EXT_AWAY;
96 /* The 'extended-away' icon is not an official one so we fallback to
97 * idle if it's not implemented */
98 return EMPATHY_IMAGE_IDLE;
99 case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
100 if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
101 EMPATHY_IMAGE_HIDDEN))
102 return EMPATHY_IMAGE_HIDDEN;
104 /* The 'hidden' icon is not an official one so we fallback to offline if
105 * it's not implemented */
106 return EMPATHY_IMAGE_OFFLINE;
107 case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
108 case TP_CONNECTION_PRESENCE_TYPE_ERROR:
109 return EMPATHY_IMAGE_OFFLINE;
110 case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
111 return EMPATHY_IMAGE_PENDING;
112 case TP_CONNECTION_PRESENCE_TYPE_UNSET:
121 empathy_icon_name_for_contact (EmpathyContact *contact)
123 TpConnectionPresenceType presence;
125 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact),
126 EMPATHY_IMAGE_OFFLINE);
128 presence = empathy_contact_get_presence (contact);
129 return empathy_icon_name_for_presence (presence);
133 empathy_icon_name_for_individual (FolksIndividual *individual)
135 FolksPresenceType folks_presence;
136 TpConnectionPresenceType presence;
138 folks_presence = folks_presence_details_get_presence_type (
139 FOLKS_PRESENCE_DETAILS (individual));
140 presence = empathy_folks_presence_type_to_tp (folks_presence);
142 return empathy_icon_name_for_presence (presence);
146 empathy_protocol_name_for_contact (EmpathyContact *contact)
150 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
152 account = empathy_contact_get_account (contact);
156 return tp_account_get_icon_name (account);
163 gboolean preserve_aspect_ratio;
167 pixbuf_from_avatar_size_prepared_cb (GdkPixbufLoader *loader,
170 struct SizeData *data)
172 g_return_if_fail (width > 0 && height > 0);
174 if (data->preserve_aspect_ratio && (data->width > 0 || data->height > 0))
176 if (width < data->width && height < data->height)
184 width = width * (double) data->height / (gdouble) height;
185 height = data->height;
187 else if (data->height < 0)
189 height = height * (double) data->width / (double) width;
192 else if ((double) height * (double) data->width >
193 (double) width * (double) data->height)
195 width = 0.5 + (double) width * (double) data->height / (double) height;
196 height = data->height;
200 height = 0.5 + (double) height * (double) data->width / (double) width;
209 if (data->height > 0)
210 height = data->height;
213 gdk_pixbuf_loader_set_size (loader, width, height);
217 empathy_avatar_pixbuf_roundify (GdkPixbuf *pixbuf)
219 gint width, height, rowstride;
222 width = gdk_pixbuf_get_width (pixbuf);
223 height = gdk_pixbuf_get_height (pixbuf);
224 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
225 pixels = gdk_pixbuf_get_pixels (pixbuf);
227 if (width < 6 || height < 6)
234 pixels[rowstride + 3] = 0x80;
235 pixels[rowstride * 2 + 3] = 0xC0;
238 pixels[width * 4 - 1] = 0;
239 pixels[width * 4 - 5] = 0x80;
240 pixels[width * 4 - 9] = 0xC0;
241 pixels[rowstride + (width * 4) - 1] = 0x80;
242 pixels[(2 * rowstride) + (width * 4) - 1] = 0xC0;
245 pixels[(height - 1) * rowstride + 3] = 0;
246 pixels[(height - 1) * rowstride + 7] = 0x80;
247 pixels[(height - 1) * rowstride + 11] = 0xC0;
248 pixels[(height - 2) * rowstride + 3] = 0x80;
249 pixels[(height - 3) * rowstride + 3] = 0xC0;
252 pixels[height * rowstride - 1] = 0;
253 pixels[(height - 1) * rowstride - 1] = 0x80;
254 pixels[(height - 2) * rowstride - 1] = 0xC0;
255 pixels[height * rowstride - 5] = 0x80;
256 pixels[height * rowstride - 9] = 0xC0;
260 empathy_gdk_pixbuf_is_opaque (GdkPixbuf *pixbuf)
262 gint height, rowstride, i;
266 height = gdk_pixbuf_get_height (pixbuf);
267 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
268 pixels = gdk_pixbuf_get_pixels (pixbuf);
271 for (i = 3; i < rowstride; i+=4)
275 for (i = 1; i < height - 1; i++)
277 row = pixels + (i*rowstride);
278 if (row[3] < 0xfe || row[rowstride-1] < 0xfe)
282 row = pixels + ((height-1) * rowstride);
283 for (i = 3; i < rowstride; i+=4)
291 pixbuf_round_corners (GdkPixbuf *pixbuf)
295 if (!gdk_pixbuf_get_has_alpha (pixbuf))
297 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
298 gdk_pixbuf_get_width (pixbuf),
299 gdk_pixbuf_get_height (pixbuf));
301 gdk_pixbuf_copy_area (pixbuf, 0, 0,
302 gdk_pixbuf_get_width (pixbuf),
303 gdk_pixbuf_get_height (pixbuf),
309 result = g_object_ref (pixbuf);
312 if (empathy_gdk_pixbuf_is_opaque (result))
313 empathy_avatar_pixbuf_roundify (result);
319 avatar_pixbuf_from_loader (GdkPixbufLoader *loader)
323 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
325 return pixbuf_round_corners (pixbuf);
329 empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avatar,
334 GdkPixbufLoader *loader;
335 struct SizeData data;
336 GError *error = NULL;
342 data.height = height;
343 data.preserve_aspect_ratio = TRUE;
345 loader = gdk_pixbuf_loader_new ();
347 g_signal_connect (loader, "size-prepared",
348 G_CALLBACK (pixbuf_from_avatar_size_prepared_cb), &data);
350 if (avatar->len == 0)
352 g_warning ("Avatar has 0 length");
355 else if (!gdk_pixbuf_loader_write (loader, avatar->data, avatar->len, &error))
357 g_warning ("Couldn't write avatar image:%p with "
358 "length:%" G_GSIZE_FORMAT " to pixbuf loader: %s",
359 avatar->data, avatar->len, error->message);
361 g_error_free (error);
365 gdk_pixbuf_loader_close (loader, NULL);
366 pixbuf = avatar_pixbuf_from_loader (loader);
368 g_object_unref (loader);
374 empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact *contact,
378 EmpathyAvatar *avatar;
380 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
382 avatar = empathy_contact_get_avatar (contact);
384 return empathy_pixbuf_from_avatar_scaled (avatar, width, height);
389 GSimpleAsyncResult *result;
392 GCancellable *cancellable;
393 } PixbufAvatarFromIndividualClosure;
395 static PixbufAvatarFromIndividualClosure *
396 pixbuf_avatar_from_individual_closure_new (FolksIndividual *individual,
397 GSimpleAsyncResult *result,
400 GCancellable *cancellable)
402 PixbufAvatarFromIndividualClosure *closure;
404 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
405 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
407 closure = g_slice_new0 (PixbufAvatarFromIndividualClosure);
408 closure->result = g_object_ref (result);
409 closure->width = width;
410 closure->height = height;
412 if (cancellable != NULL)
413 closure->cancellable = g_object_ref (cancellable);
419 pixbuf_avatar_from_individual_closure_free (
420 PixbufAvatarFromIndividualClosure *closure)
422 g_clear_object (&closure->cancellable);
423 g_object_unref (closure->result);
424 g_slice_free (PixbufAvatarFromIndividualClosure, closure);
428 * @pixbuf: (transfer all)
430 * Return: (transfer all)
433 transform_pixbuf (GdkPixbuf *pixbuf)
437 result = pixbuf_round_corners (pixbuf);
438 g_object_unref (pixbuf);
444 avatar_icon_load_cb (GObject *object,
445 GAsyncResult *result,
448 GLoadableIcon *icon = G_LOADABLE_ICON (object);
449 PixbufAvatarFromIndividualClosure *closure = user_data;
450 GInputStream *stream;
451 GError *error = NULL;
453 GdkPixbuf *final_pixbuf;
455 stream = g_loadable_icon_load_finish (icon, result, NULL, &error);
458 DEBUG ("Failed to open avatar stream: %s", error->message);
459 g_simple_async_result_set_from_error (closure->result, error);
463 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
464 closure->width, closure->height, TRUE, closure->cancellable, &error);
466 g_object_unref (stream);
470 DEBUG ("Failed to read avatar: %s", error->message);
471 g_simple_async_result_set_from_error (closure->result, error);
475 final_pixbuf = transform_pixbuf (pixbuf);
477 /* Pass ownership of final_pixbuf to the result */
478 g_simple_async_result_set_op_res_gpointer (closure->result,
479 final_pixbuf, g_object_unref);
482 g_simple_async_result_complete (closure->result);
484 g_clear_error (&error);
485 pixbuf_avatar_from_individual_closure_free (closure);
489 empathy_pixbuf_avatar_from_individual_scaled_async (
490 FolksIndividual *individual,
493 GCancellable *cancellable,
494 GAsyncReadyCallback callback,
497 GLoadableIcon *avatar_icon;
498 GSimpleAsyncResult *result;
499 PixbufAvatarFromIndividualClosure *closure;
501 result = g_simple_async_result_new (G_OBJECT (individual),
502 callback, user_data, empathy_pixbuf_avatar_from_individual_scaled_async);
504 avatar_icon = folks_avatar_details_get_avatar (
505 FOLKS_AVATAR_DETAILS (individual));
507 if (avatar_icon == NULL)
509 g_simple_async_result_set_error (result, G_IO_ERROR,
510 G_IO_ERROR_NOT_FOUND, "no avatar found");
512 g_simple_async_result_complete (result);
513 g_object_unref (result);
517 closure = pixbuf_avatar_from_individual_closure_new (individual, result,
518 width, height, cancellable);
520 g_return_if_fail (closure != NULL);
522 g_loadable_icon_load_async (avatar_icon, width, cancellable,
523 avatar_icon_load_cb, closure);
525 g_object_unref (result);
528 /* Return a ref on the GdkPixbuf */
530 empathy_pixbuf_avatar_from_individual_scaled_finish (
531 FolksIndividual *individual,
532 GAsyncResult *result,
535 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
536 gboolean result_valid;
539 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
540 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), NULL);
542 if (g_simple_async_result_propagate_error (simple, error))
545 result_valid = g_simple_async_result_is_valid (result,
546 G_OBJECT (individual),
547 empathy_pixbuf_avatar_from_individual_scaled_async);
549 g_return_val_if_fail (result_valid, NULL);
551 pixbuf = g_simple_async_result_get_op_res_gpointer (simple);
552 return pixbuf != NULL ? g_object_ref (pixbuf) : NULL;
556 empathy_pixbuf_contact_status_icon (EmpathyContact *contact,
557 gboolean show_protocol)
559 const gchar *icon_name;
561 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
563 icon_name = empathy_icon_name_for_contact (contact);
565 if (icon_name == NULL)
568 return empathy_pixbuf_contact_status_icon_with_icon_name (contact,
569 icon_name, show_protocol);
572 static GdkPixbuf * empathy_pixbuf_protocol_from_contact_scaled (
573 EmpathyContact *contact,
578 empathy_pixbuf_contact_status_icon_with_icon_name (EmpathyContact *contact,
579 const gchar *icon_name,
580 gboolean show_protocol)
582 GdkPixbuf *pix_status;
583 GdkPixbuf *pix_protocol;
584 gchar *icon_filename;
586 gint numerator, denominator;
588 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact) ||
589 (show_protocol == FALSE), NULL);
590 g_return_val_if_fail (icon_name != NULL, NULL);
595 icon_filename = tpaw_filename_from_icon_name (icon_name,
598 if (icon_filename == NULL)
600 DEBUG ("icon name: %s could not be found\n", icon_name);
604 pix_status = gdk_pixbuf_new_from_file (icon_filename, NULL);
606 if (pix_status == NULL)
608 DEBUG ("Could not open icon %s\n", icon_filename);
609 g_free (icon_filename);
613 g_free (icon_filename);
618 height = gdk_pixbuf_get_height (pix_status);
619 width = gdk_pixbuf_get_width (pix_status);
621 pix_protocol = empathy_pixbuf_protocol_from_contact_scaled (contact,
622 width * numerator / denominator,
623 height * numerator / denominator);
625 if (pix_protocol == NULL)
628 gdk_pixbuf_composite (pix_protocol, pix_status,
629 0, height - height * numerator / denominator,
630 width * numerator / denominator, height * numerator / denominator,
631 0, height - height * numerator / denominator,
633 GDK_INTERP_BILINEAR, 255);
635 g_object_unref (pix_protocol);
641 empathy_pixbuf_protocol_from_contact_scaled (EmpathyContact *contact,
647 GdkPixbuf *pixbuf = NULL;
649 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
651 account = empathy_contact_get_account (contact);
652 filename = tpaw_filename_from_icon_name (
653 tp_account_get_icon_name (account), GTK_ICON_SIZE_MENU);
655 if (filename != NULL)
657 pixbuf = gdk_pixbuf_new_from_file_at_size (filename, width, height, NULL);
665 empathy_url_show (GtkWidget *parent,
669 GError *error = NULL;
671 g_return_if_fail (parent == NULL || GTK_IS_WIDGET (parent));
672 g_return_if_fail (url != NULL);
674 real_url = tpaw_make_absolute_url (url);
676 gtk_show_uri (parent ? gtk_widget_get_screen (parent) : NULL, real_url,
677 gtk_get_current_event_time (), &error);
683 dialog = gtk_message_dialog_new (NULL, 0,
684 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
685 _("Unable to open URI"));
687 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
688 "%s", error->message);
690 g_signal_connect (dialog, "response",
691 G_CALLBACK (gtk_widget_destroy), NULL);
693 gtk_window_present (GTK_WINDOW (dialog));
695 g_clear_error (&error);
702 empathy_send_file (EmpathyContact *contact,
705 EmpathyFTFactory *factory;
706 GtkRecentManager *manager;
709 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
710 g_return_if_fail (G_IS_FILE (file));
712 factory = empathy_ft_factory_dup_singleton ();
714 empathy_ft_factory_new_transfer_outgoing (factory, contact, file,
715 empathy_get_current_action_time ());
717 uri = g_file_get_uri (file);
718 manager = gtk_recent_manager_get_default ();
719 gtk_recent_manager_add_item (manager, uri);
722 g_object_unref (factory);
726 empathy_send_file_from_uri_list (EmpathyContact *contact,
727 const gchar *uri_list)
732 /* Only handle a single file for now. It would be wicked cool to be
733 able to do multiple files, offering to zip them or whatever like
734 nautilus-sendto does. Note that text/uri-list is defined to have
735 each line terminated by \r\n, but we can be tolerant of applications
736 that only use \n or don't terminate single-line entries.
738 nl = strstr (uri_list, "\r\n");
740 nl = strchr (uri_list, '\n');
744 gchar *uri = g_strndup (uri_list, nl - uri_list);
745 file = g_file_new_for_uri (uri);
750 file = g_file_new_for_uri (uri_list);
753 empathy_send_file (contact, file);
755 g_object_unref (file);
759 file_manager_send_file_response_cb (GtkDialog *widget,
761 EmpathyContact *contact)
765 if (response_id == GTK_RESPONSE_OK)
767 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget));
769 empathy_send_file (contact, file);
771 g_object_unref (file);
774 g_object_unref (contact);
775 gtk_widget_destroy (GTK_WIDGET (widget));
779 filter_cb (const GtkFileFilterInfo *filter_info,
782 /* filter out socket files */
783 return tp_strdiff (filter_info->mime_type, "inode/socket");
786 static GtkFileFilter *
787 create_file_filter (void)
789 GtkFileFilter *filter;
791 filter = gtk_file_filter_new ();
793 gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_MIME_TYPE, filter_cb,
800 empathy_send_file_with_file_chooser (EmpathyContact *contact)
805 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
807 DEBUG ("Creating selection file chooser");
809 widget = gtk_file_chooser_dialog_new (_("Select a file"), NULL,
810 GTK_FILE_CHOOSER_ACTION_OPEN,
811 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
815 button = gtk_button_new_with_mnemonic (_("_Send"));
816 gtk_button_set_image (GTK_BUTTON (button),
817 gtk_image_new_from_icon_name (EMPATHY_IMAGE_DOCUMENT_SEND,
818 GTK_ICON_SIZE_BUTTON));
819 gtk_widget_show (button);
821 gtk_dialog_add_action_widget (GTK_DIALOG (widget), button,
824 gtk_widget_set_can_default (button, TRUE);
825 gtk_dialog_set_default_response (GTK_DIALOG (widget),
828 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), FALSE);
830 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget),
833 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget),
834 create_file_filter ());
836 g_signal_connect (widget, "response",
837 G_CALLBACK (file_manager_send_file_response_cb), g_object_ref (contact));
839 gtk_widget_show (widget);
843 file_manager_receive_file_response_cb (GtkDialog *dialog,
844 GtkResponseType response,
845 EmpathyFTHandler *handler)
847 EmpathyFTFactory *factory;
850 if (response == GTK_RESPONSE_OK)
854 guint64 free_space, file_size;
855 GError *error = NULL;
857 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
858 parent = g_file_get_parent (file);
859 info = g_file_query_filesystem_info (parent,
860 G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, &error);
862 g_object_unref (parent);
866 g_warning ("Error: %s", error->message);
868 g_object_unref (file);
872 free_space = g_file_info_get_attribute_uint64 (info,
873 G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
874 file_size = empathy_ft_handler_get_total_bytes (handler);
876 g_object_unref (info);
878 if (file_size > free_space)
880 GtkWidget *message = gtk_message_dialog_new (GTK_WINDOW (dialog),
881 GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
883 _("Insufficient free space to save file"));
884 char *file_size_str, *free_space_str;
886 file_size_str = g_format_size (file_size);
887 free_space_str = g_format_size (free_space);
889 gtk_message_dialog_format_secondary_text (
890 GTK_MESSAGE_DIALOG (message),
891 _("%s of free space are required to save this "
892 "file, but only %s is available. Please "
893 "choose another location."),
894 file_size_str, free_space_str);
896 gtk_dialog_run (GTK_DIALOG (message));
898 g_free (file_size_str);
899 g_free (free_space_str);
900 gtk_widget_destroy (message);
902 g_object_unref (file);
907 factory = empathy_ft_factory_dup_singleton ();
909 empathy_ft_factory_set_destination_for_incoming_handler (
910 factory, handler, file);
912 g_object_unref (factory);
913 g_object_unref (file);
917 /* unref the handler, as we dismissed the file chooser,
918 * and refused the transfer.
920 g_object_unref (handler);
923 gtk_widget_destroy (GTK_WIDGET (dialog));
927 empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler)
931 EmpathyContact *contact;
934 contact = empathy_ft_handler_get_contact (handler);
935 g_assert (contact != NULL);
937 title = g_strdup_printf (_("Incoming file from %s"),
938 empathy_contact_get_alias (contact));
940 widget = gtk_file_chooser_dialog_new (title,
941 NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
942 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
943 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
946 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (widget),
947 empathy_ft_handler_get_filename (handler));
949 gtk_file_chooser_set_do_overwrite_confirmation
950 (GTK_FILE_CHOOSER (widget), TRUE);
952 dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
954 /* Fallback to $HOME if $XDG_DOWNLOAD_DIR is not set */
955 dir = g_get_home_dir ();
957 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), dir);
959 g_signal_connect (widget, "response",
960 G_CALLBACK (file_manager_receive_file_response_cb), handler);
962 gtk_widget_show (widget);
967 empathy_make_color_whiter (GdkRGBA *color)
969 const GdkRGBA white = { 1.0, 1.0, 1.0, 1.0 };
971 color->red = (color->red + white.red) / 2;
972 color->green = (color->green + white.green) / 2;
973 color->blue = (color->blue + white.blue) / 2;
977 menu_deactivate_cb (GtkMenu *menu,
980 /* FIXME: we shouldn't have to disconnect the signal (bgo #641327) */
981 g_signal_handlers_disconnect_by_func (menu,
982 menu_deactivate_cb, user_data);
984 gtk_menu_detach (menu);
987 /* Convenient function to create a GtkMenu attached to @attach_to and detach
988 * it when the menu is not displayed any more. This is useful when creating a
989 * context menu that we want to get rid as soon as it as been displayed. */
991 empathy_context_menu_new (GtkWidget *attach_to)
995 menu = gtk_menu_new ();
997 gtk_menu_attach_to_widget (GTK_MENU (menu), attach_to, NULL);
999 /* menu is initially unowned but gtk_menu_attach_to_widget () taked its
1000 * floating ref. We can either wait that @attach_to releases its ref when
1001 * it will be destroyed (when leaving Empathy most of the time) or explicitely
1002 * detach the menu when it's not displayed any more.
1003 * We go for the latter as we don't want to keep useless menus in memory
1004 * during the whole lifetime of Empathy. */
1005 g_signal_connect (menu, "deactivate", G_CALLBACK (menu_deactivate_cb), NULL);
1011 empathy_get_current_action_time (void)
1013 return (tp_user_action_time_from_x11 (gtk_get_current_event_time ()));
1016 /* @words = tpaw_live_search_strip_utf8_string (@text);
1018 * User has to pass both so we don't have to compute @words ourself each time
1019 * this function is called. */
1021 empathy_individual_match_string (FolksIndividual *individual,
1028 gboolean retval = FALSE;
1030 /* check alias name */
1031 str = folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual));
1033 if (tpaw_live_search_match_words (str, words))
1036 personas = folks_individual_get_personas (individual);
1038 /* check contact id, remove the @server.com part */
1039 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
1040 while (retval == FALSE && gee_iterator_next (iter))
1042 FolksPersona *persona = gee_iterator_get (iter);
1045 if (empathy_folks_persona_is_interesting (persona))
1047 str = folks_persona_get_display_id (persona);
1049 /* Accept the persona if @text is a full prefix of his ID; that allows
1050 * user to find, say, a jabber contact by typing his JID. */
1051 if (g_str_has_prefix (str, text))
1057 gchar *dup_str = NULL;
1060 p = strstr (str, "@");
1062 str = dup_str = g_strndup (str, p - str);
1064 visible = tpaw_live_search_match_words (str, words);
1070 g_clear_object (&persona);
1072 g_clear_object (&iter);
1074 /* FIXME: Add more rules here, we could check phone numbers in
1075 * contact's vCard for example. */
1080 empathy_launch_program (const gchar *dir,
1084 GdkDisplay *display;
1085 GError *error = NULL;
1088 GdkAppLaunchContext *context = NULL;
1090 /* Try to run from source directory if possible */
1091 path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src",
1094 if (!g_file_test (path, G_FILE_TEST_EXISTS))
1097 path = g_build_filename (dir, name, NULL);
1101 cmd = g_strconcat (path, " ", args, NULL);
1103 cmd = g_strdup (path);
1105 app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &error);
1106 if (app_info == NULL)
1108 DEBUG ("Failed to create app info: %s", error->message);
1109 g_error_free (error);
1113 display = gdk_display_get_default ();
1114 context = gdk_display_get_app_launch_context (display);
1116 if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
1119 g_warning ("Failed to launch %s: %s", name, error->message);
1120 g_error_free (error);
1125 tp_clear_object (&app_info);
1126 tp_clear_object (&context);
1131 /* Most of the workspace manipulation code has been copied from libwnck
1132 * Copyright (C) 2001 Havoc Pennington
1133 * Copyright (C) 2005-2007 Vincent Untz
1136 _wnck_activate_workspace (Screen *screen,
1137 int new_active_space,
1144 display = DisplayOfScreen (screen);
1145 root = RootWindowOfScreen (screen);
1147 xev.xclient.type = ClientMessage;
1148 xev.xclient.serial = 0;
1149 xev.xclient.send_event = True;
1150 xev.xclient.display = display;
1151 xev.xclient.window = root;
1152 xev.xclient.message_type = gdk_x11_get_xatom_by_name ("_NET_CURRENT_DESKTOP");
1153 xev.xclient.format = 32;
1154 xev.xclient.data.l[0] = new_active_space;
1155 xev.xclient.data.l[1] = timestamp;
1156 xev.xclient.data.l[2] = 0;
1157 xev.xclient.data.l[3] = 0;
1158 xev.xclient.data.l[4] = 0;
1160 gdk_error_trap_push ();
1161 XSendEvent (display, root, False,
1162 SubstructureRedirectMask | SubstructureNotifyMask,
1164 XSync (display, False);
1165 gdk_error_trap_pop_ignored ();
1169 _wnck_get_cardinal (Screen *screen,
1182 display = DisplayOfScreen (screen);
1186 gdk_error_trap_push ();
1188 result = XGetWindowProperty (display, xwindow, atom,
1189 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
1190 &bytes_after, (void *) &num);
1191 err = gdk_error_trap_pop ();
1192 if (err != Success ||
1196 if (type != XA_CARDINAL)
1210 window_get_workspace (Screen *xscreen,
1215 if (!_wnck_get_cardinal (xscreen, win,
1216 gdk_x11_get_xatom_by_name ("_NET_WM_DESKTOP"), &number))
1222 /* Ask X to move to the desktop on which @window currently is
1223 * and the present @window. */
1225 empathy_move_to_window_desktop (GtkWindow *window,
1230 GdkWindow *gdk_window;
1233 screen = gtk_window_get_screen (window);
1234 xscreen = gdk_x11_screen_get_xscreen (screen);
1235 gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
1237 workspace = window_get_workspace (xscreen,
1238 gdk_x11_window_get_xid (gdk_window));
1239 if (workspace == -1)
1242 _wnck_activate_workspace (xscreen, workspace, timestamp);
1245 gtk_window_present_with_time (window, timestamp);
1249 empathy_set_css_provider (GtkWidget *widget)
1251 GtkCssProvider *provider;
1253 GError *error = NULL;
1256 filename = empathy_file_lookup ("empathy.css", "data");
1258 provider = gtk_css_provider_new ();
1260 if (!gtk_css_provider_load_from_path (provider, filename, &error))
1262 g_warning ("Failed to load css file '%s': %s", filename, error->message);
1263 g_error_free (error);
1268 screen = gtk_widget_get_screen (widget);
1270 screen = gdk_screen_get_default ();
1272 gtk_style_context_add_provider_for_screen (screen,
1273 GTK_STYLE_PROVIDER (provider),
1274 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1278 g_object_unref (provider);
1282 launch_app_info (GAppInfo *app_info,
1285 GdkAppLaunchContext *context = NULL;
1286 GdkDisplay *display;
1289 display = gdk_display_get_default ();
1290 context = gdk_display_get_app_launch_context (display);
1292 if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
1295 DEBUG ("Failed to launch %s: %s",
1296 g_app_info_get_display_name (app_info), err->message);
1297 g_propagate_error (error, err);
1301 tp_clear_object (&context);
1306 empathy_launch_external_app (const gchar *desktop_file,
1310 GDesktopAppInfo *desktop_info;
1314 desktop_info = g_desktop_app_info_new (desktop_file);
1315 if (desktop_info == NULL)
1317 DEBUG ("%s not found", desktop_file);
1319 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1320 "%s not found", desktop_file);
1326 result = launch_app_info (G_APP_INFO (desktop_info), error);
1333 /* glib doesn't have API to start a desktop file with args... (#637875) */
1334 cmd = g_strdup_printf ("%s %s", g_app_info_get_commandline (
1335 (GAppInfo *) desktop_info), args);
1337 app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &err);
1338 if (app_info == NULL)
1340 DEBUG ("Failed to launch '%s': %s", cmd, err->message);
1342 g_object_unref (desktop_info);
1343 g_propagate_error (error, err);
1347 result = launch_app_info (app_info, error);
1349 g_object_unref (app_info);
1353 g_object_unref (desktop_info);