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))
178 width = width * (double) data->height / (gdouble) height;
179 height = data->height;
181 else if (data->height < 0)
183 height = height * (double) data->width / (double) width;
186 else if ((double) height * (double) data->width >
187 (double) width * (double) data->height)
189 width = 0.5 + (double) width * (double) data->height / (double) height;
190 height = data->height;
194 height = 0.5 + (double) height * (double) data->width / (double) width;
203 if (data->height > 0)
204 height = data->height;
207 gdk_pixbuf_loader_set_size (loader, width, height);
211 empathy_avatar_pixbuf_roundify (GdkPixbuf *pixbuf)
213 gint width, height, rowstride;
216 width = gdk_pixbuf_get_width (pixbuf);
217 height = gdk_pixbuf_get_height (pixbuf);
218 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
219 pixels = gdk_pixbuf_get_pixels (pixbuf);
221 if (width < 6 || height < 6)
228 pixels[rowstride + 3] = 0x80;
229 pixels[rowstride * 2 + 3] = 0xC0;
232 pixels[width * 4 - 1] = 0;
233 pixels[width * 4 - 5] = 0x80;
234 pixels[width * 4 - 9] = 0xC0;
235 pixels[rowstride + (width * 4) - 1] = 0x80;
236 pixels[(2 * rowstride) + (width * 4) - 1] = 0xC0;
239 pixels[(height - 1) * rowstride + 3] = 0;
240 pixels[(height - 1) * rowstride + 7] = 0x80;
241 pixels[(height - 1) * rowstride + 11] = 0xC0;
242 pixels[(height - 2) * rowstride + 3] = 0x80;
243 pixels[(height - 3) * rowstride + 3] = 0xC0;
246 pixels[height * rowstride - 1] = 0;
247 pixels[(height - 1) * rowstride - 1] = 0x80;
248 pixels[(height - 2) * rowstride - 1] = 0xC0;
249 pixels[height * rowstride - 5] = 0x80;
250 pixels[height * rowstride - 9] = 0xC0;
254 empathy_gdk_pixbuf_is_opaque (GdkPixbuf *pixbuf)
256 gint height, rowstride, i;
260 height = gdk_pixbuf_get_height (pixbuf);
261 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
262 pixels = gdk_pixbuf_get_pixels (pixbuf);
265 for (i = 3; i < rowstride; i+=4)
269 for (i = 1; i < height - 1; i++)
271 row = pixels + (i*rowstride);
272 if (row[3] < 0xfe || row[rowstride-1] < 0xfe)
276 row = pixels + ((height-1) * rowstride);
277 for (i = 3; i < rowstride; i+=4)
285 pixbuf_round_corners (GdkPixbuf *pixbuf)
289 if (!gdk_pixbuf_get_has_alpha (pixbuf))
291 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
292 gdk_pixbuf_get_width (pixbuf),
293 gdk_pixbuf_get_height (pixbuf));
295 gdk_pixbuf_copy_area (pixbuf, 0, 0,
296 gdk_pixbuf_get_width (pixbuf),
297 gdk_pixbuf_get_height (pixbuf),
303 result = g_object_ref (pixbuf);
306 if (empathy_gdk_pixbuf_is_opaque (result))
307 empathy_avatar_pixbuf_roundify (result);
313 avatar_pixbuf_from_loader (GdkPixbufLoader *loader)
317 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
319 return pixbuf_round_corners (pixbuf);
323 empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avatar,
328 GdkPixbufLoader *loader;
329 struct SizeData data;
330 GError *error = NULL;
336 data.height = height;
337 data.preserve_aspect_ratio = TRUE;
339 loader = gdk_pixbuf_loader_new ();
341 g_signal_connect (loader, "size-prepared",
342 G_CALLBACK (pixbuf_from_avatar_size_prepared_cb), &data);
344 if (avatar->len == 0)
346 g_warning ("Avatar has 0 length");
349 else if (!gdk_pixbuf_loader_write (loader, avatar->data, avatar->len, &error))
351 g_warning ("Couldn't write avatar image:%p with "
352 "length:%" G_GSIZE_FORMAT " to pixbuf loader: %s",
353 avatar->data, avatar->len, error->message);
355 g_error_free (error);
359 gdk_pixbuf_loader_close (loader, NULL);
360 pixbuf = avatar_pixbuf_from_loader (loader);
362 g_object_unref (loader);
368 empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact *contact,
372 EmpathyAvatar *avatar;
374 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
376 avatar = empathy_contact_get_avatar (contact);
378 return empathy_pixbuf_from_avatar_scaled (avatar, width, height);
383 GSimpleAsyncResult *result;
386 GCancellable *cancellable;
387 } PixbufAvatarFromIndividualClosure;
389 static PixbufAvatarFromIndividualClosure *
390 pixbuf_avatar_from_individual_closure_new (FolksIndividual *individual,
391 GSimpleAsyncResult *result,
394 GCancellable *cancellable)
396 PixbufAvatarFromIndividualClosure *closure;
398 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
399 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
401 closure = g_slice_new0 (PixbufAvatarFromIndividualClosure);
402 closure->result = g_object_ref (result);
403 closure->width = width;
404 closure->height = height;
406 if (cancellable != NULL)
407 closure->cancellable = g_object_ref (cancellable);
413 pixbuf_avatar_from_individual_closure_free (
414 PixbufAvatarFromIndividualClosure *closure)
416 g_clear_object (&closure->cancellable);
417 g_object_unref (closure->result);
418 g_slice_free (PixbufAvatarFromIndividualClosure, closure);
422 * @pixbuf: (transfer all)
424 * Return: (transfer all)
427 transform_pixbuf (GdkPixbuf *pixbuf)
431 result = pixbuf_round_corners (pixbuf);
432 g_object_unref (pixbuf);
438 avatar_icon_load_cb (GObject *object,
439 GAsyncResult *result,
442 GLoadableIcon *icon = G_LOADABLE_ICON (object);
443 PixbufAvatarFromIndividualClosure *closure = user_data;
444 GInputStream *stream;
445 GError *error = NULL;
447 GdkPixbuf *final_pixbuf;
449 stream = g_loadable_icon_load_finish (icon, result, NULL, &error);
452 DEBUG ("Failed to open avatar stream: %s", error->message);
453 g_simple_async_result_set_from_error (closure->result, error);
457 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
458 closure->width, closure->height, TRUE, closure->cancellable, &error);
460 g_object_unref (stream);
464 DEBUG ("Failed to read avatar: %s", error->message);
465 g_simple_async_result_set_from_error (closure->result, error);
469 final_pixbuf = transform_pixbuf (pixbuf);
471 /* Pass ownership of final_pixbuf to the result */
472 g_simple_async_result_set_op_res_gpointer (closure->result,
473 final_pixbuf, g_object_unref);
476 g_simple_async_result_complete (closure->result);
478 g_clear_error (&error);
479 pixbuf_avatar_from_individual_closure_free (closure);
483 empathy_pixbuf_avatar_from_individual_scaled_async (
484 FolksIndividual *individual,
487 GCancellable *cancellable,
488 GAsyncReadyCallback callback,
491 GLoadableIcon *avatar_icon;
492 GSimpleAsyncResult *result;
493 PixbufAvatarFromIndividualClosure *closure;
495 result = g_simple_async_result_new (G_OBJECT (individual),
496 callback, user_data, empathy_pixbuf_avatar_from_individual_scaled_async);
498 avatar_icon = folks_avatar_details_get_avatar (
499 FOLKS_AVATAR_DETAILS (individual));
501 if (avatar_icon == NULL)
503 g_simple_async_result_set_error (result, G_IO_ERROR,
504 G_IO_ERROR_NOT_FOUND, "no avatar found");
506 g_simple_async_result_complete (result);
507 g_object_unref (result);
511 closure = pixbuf_avatar_from_individual_closure_new (individual, result,
512 width, height, cancellable);
514 g_return_if_fail (closure != NULL);
516 g_loadable_icon_load_async (avatar_icon, width, cancellable,
517 avatar_icon_load_cb, closure);
519 g_object_unref (result);
522 /* Return a ref on the GdkPixbuf */
524 empathy_pixbuf_avatar_from_individual_scaled_finish (
525 FolksIndividual *individual,
526 GAsyncResult *result,
529 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
530 gboolean result_valid;
533 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
534 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), NULL);
536 if (g_simple_async_result_propagate_error (simple, error))
539 result_valid = g_simple_async_result_is_valid (result,
540 G_OBJECT (individual),
541 empathy_pixbuf_avatar_from_individual_scaled_async);
543 g_return_val_if_fail (result_valid, NULL);
545 pixbuf = g_simple_async_result_get_op_res_gpointer (simple);
546 return pixbuf != NULL ? g_object_ref (pixbuf) : NULL;
550 empathy_pixbuf_contact_status_icon (EmpathyContact *contact,
551 gboolean show_protocol)
553 const gchar *icon_name;
555 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
557 icon_name = empathy_icon_name_for_contact (contact);
559 if (icon_name == NULL)
562 return empathy_pixbuf_contact_status_icon_with_icon_name (contact,
563 icon_name, show_protocol);
566 static GdkPixbuf * empathy_pixbuf_protocol_from_contact_scaled (
567 EmpathyContact *contact,
572 empathy_pixbuf_contact_status_icon_with_icon_name (EmpathyContact *contact,
573 const gchar *icon_name,
574 gboolean show_protocol)
576 GdkPixbuf *pix_status;
577 GdkPixbuf *pix_protocol;
578 gchar *icon_filename;
580 gint numerator, denominator;
582 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact) ||
583 (show_protocol == FALSE), NULL);
584 g_return_val_if_fail (icon_name != NULL, NULL);
589 icon_filename = tpaw_filename_from_icon_name (icon_name,
592 if (icon_filename == NULL)
594 DEBUG ("icon name: %s could not be found\n", icon_name);
598 pix_status = gdk_pixbuf_new_from_file (icon_filename, NULL);
600 if (pix_status == NULL)
602 DEBUG ("Could not open icon %s\n", icon_filename);
603 g_free (icon_filename);
607 g_free (icon_filename);
612 height = gdk_pixbuf_get_height (pix_status);
613 width = gdk_pixbuf_get_width (pix_status);
615 pix_protocol = empathy_pixbuf_protocol_from_contact_scaled (contact,
616 width * numerator / denominator,
617 height * numerator / denominator);
619 if (pix_protocol == NULL)
622 gdk_pixbuf_composite (pix_protocol, pix_status,
623 0, height - height * numerator / denominator,
624 width * numerator / denominator, height * numerator / denominator,
625 0, height - height * numerator / denominator,
627 GDK_INTERP_BILINEAR, 255);
629 g_object_unref (pix_protocol);
635 empathy_pixbuf_protocol_from_contact_scaled (EmpathyContact *contact,
641 GdkPixbuf *pixbuf = NULL;
643 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
645 account = empathy_contact_get_account (contact);
646 filename = tpaw_filename_from_icon_name (
647 tp_account_get_icon_name (account), GTK_ICON_SIZE_MENU);
649 if (filename != NULL)
651 pixbuf = gdk_pixbuf_new_from_file_at_size (filename, width, height, NULL);
659 empathy_url_show (GtkWidget *parent,
663 GError *error = NULL;
665 g_return_if_fail (parent == NULL || GTK_IS_WIDGET (parent));
666 g_return_if_fail (url != NULL);
668 real_url = tpaw_make_absolute_url (url);
670 gtk_show_uri (parent ? gtk_widget_get_screen (parent) : NULL, real_url,
671 gtk_get_current_event_time (), &error);
677 dialog = gtk_message_dialog_new (NULL, 0,
678 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
679 _("Unable to open URI"));
681 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
682 "%s", error->message);
684 g_signal_connect (dialog, "response",
685 G_CALLBACK (gtk_widget_destroy), NULL);
687 gtk_window_present (GTK_WINDOW (dialog));
689 g_clear_error (&error);
696 empathy_send_file (EmpathyContact *contact,
699 EmpathyFTFactory *factory;
700 GtkRecentManager *manager;
703 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
704 g_return_if_fail (G_IS_FILE (file));
706 factory = empathy_ft_factory_dup_singleton ();
708 empathy_ft_factory_new_transfer_outgoing (factory, contact, file,
709 empathy_get_current_action_time ());
711 uri = g_file_get_uri (file);
712 manager = gtk_recent_manager_get_default ();
713 gtk_recent_manager_add_item (manager, uri);
716 g_object_unref (factory);
720 empathy_send_file_from_uri_list (EmpathyContact *contact,
721 const gchar *uri_list)
726 /* Only handle a single file for now. It would be wicked cool to be
727 able to do multiple files, offering to zip them or whatever like
728 nautilus-sendto does. Note that text/uri-list is defined to have
729 each line terminated by \r\n, but we can be tolerant of applications
730 that only use \n or don't terminate single-line entries.
732 nl = strstr (uri_list, "\r\n");
734 nl = strchr (uri_list, '\n');
738 gchar *uri = g_strndup (uri_list, nl - uri_list);
739 file = g_file_new_for_uri (uri);
744 file = g_file_new_for_uri (uri_list);
747 empathy_send_file (contact, file);
749 g_object_unref (file);
753 file_manager_send_file_response_cb (GtkDialog *widget,
755 EmpathyContact *contact)
759 if (response_id == GTK_RESPONSE_OK)
761 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget));
763 empathy_send_file (contact, file);
765 g_object_unref (file);
768 g_object_unref (contact);
769 gtk_widget_destroy (GTK_WIDGET (widget));
773 filter_cb (const GtkFileFilterInfo *filter_info,
776 /* filter out socket files */
777 return tp_strdiff (filter_info->mime_type, "inode/socket");
780 static GtkFileFilter *
781 create_file_filter (void)
783 GtkFileFilter *filter;
785 filter = gtk_file_filter_new ();
787 gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_MIME_TYPE, filter_cb,
794 empathy_send_file_with_file_chooser (EmpathyContact *contact)
799 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
801 DEBUG ("Creating selection file chooser");
803 widget = gtk_file_chooser_dialog_new (_("Select a file"), NULL,
804 GTK_FILE_CHOOSER_ACTION_OPEN,
805 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
809 button = gtk_button_new_with_mnemonic (_("_Send"));
810 gtk_button_set_image (GTK_BUTTON (button),
811 gtk_image_new_from_icon_name (EMPATHY_IMAGE_DOCUMENT_SEND,
812 GTK_ICON_SIZE_BUTTON));
813 gtk_widget_show (button);
815 gtk_dialog_add_action_widget (GTK_DIALOG (widget), button,
818 gtk_widget_set_can_default (button, TRUE);
819 gtk_dialog_set_default_response (GTK_DIALOG (widget),
822 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), FALSE);
824 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget),
827 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (widget),
828 create_file_filter ());
830 g_signal_connect (widget, "response",
831 G_CALLBACK (file_manager_send_file_response_cb), g_object_ref (contact));
833 gtk_widget_show (widget);
837 file_manager_receive_file_response_cb (GtkDialog *dialog,
838 GtkResponseType response,
839 EmpathyFTHandler *handler)
841 EmpathyFTFactory *factory;
844 if (response == GTK_RESPONSE_OK)
848 guint64 free_space, file_size;
849 GError *error = NULL;
851 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
852 parent = g_file_get_parent (file);
853 info = g_file_query_filesystem_info (parent,
854 G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, &error);
856 g_object_unref (parent);
860 g_warning ("Error: %s", error->message);
862 g_object_unref (file);
866 free_space = g_file_info_get_attribute_uint64 (info,
867 G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
868 file_size = empathy_ft_handler_get_total_bytes (handler);
870 g_object_unref (info);
872 if (file_size > free_space)
874 GtkWidget *message = gtk_message_dialog_new (GTK_WINDOW (dialog),
875 GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
877 _("Insufficient free space to save file"));
878 char *file_size_str, *free_space_str;
880 file_size_str = g_format_size (file_size);
881 free_space_str = g_format_size (free_space);
883 gtk_message_dialog_format_secondary_text (
884 GTK_MESSAGE_DIALOG (message),
885 _("%s of free space are required to save this "
886 "file, but only %s is available. Please "
887 "choose another location."),
888 file_size_str, free_space_str);
890 gtk_dialog_run (GTK_DIALOG (message));
892 g_free (file_size_str);
893 g_free (free_space_str);
894 gtk_widget_destroy (message);
896 g_object_unref (file);
901 factory = empathy_ft_factory_dup_singleton ();
903 empathy_ft_factory_set_destination_for_incoming_handler (
904 factory, handler, file);
906 g_object_unref (factory);
907 g_object_unref (file);
911 /* unref the handler, as we dismissed the file chooser,
912 * and refused the transfer.
914 g_object_unref (handler);
917 gtk_widget_destroy (GTK_WIDGET (dialog));
921 empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler)
925 EmpathyContact *contact;
928 contact = empathy_ft_handler_get_contact (handler);
929 g_assert (contact != NULL);
931 title = g_strdup_printf (_("Incoming file from %s"),
932 empathy_contact_get_alias (contact));
934 widget = gtk_file_chooser_dialog_new (title,
935 NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
936 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
937 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
940 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (widget),
941 empathy_ft_handler_get_filename (handler));
943 gtk_file_chooser_set_do_overwrite_confirmation
944 (GTK_FILE_CHOOSER (widget), TRUE);
946 dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
948 /* Fallback to $HOME if $XDG_DOWNLOAD_DIR is not set */
949 dir = g_get_home_dir ();
951 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), dir);
953 g_signal_connect (widget, "response",
954 G_CALLBACK (file_manager_receive_file_response_cb), handler);
956 gtk_widget_show (widget);
961 empathy_make_color_whiter (GdkRGBA *color)
963 const GdkRGBA white = { 1.0, 1.0, 1.0, 1.0 };
965 color->red = (color->red + white.red) / 2;
966 color->green = (color->green + white.green) / 2;
967 color->blue = (color->blue + white.blue) / 2;
971 menu_deactivate_cb (GtkMenu *menu,
974 /* FIXME: we shouldn't have to disconnect the signal (bgo #641327) */
975 g_signal_handlers_disconnect_by_func (menu,
976 menu_deactivate_cb, user_data);
978 gtk_menu_detach (menu);
981 /* Convenient function to create a GtkMenu attached to @attach_to and detach
982 * it when the menu is not displayed any more. This is useful when creating a
983 * context menu that we want to get rid as soon as it as been displayed. */
985 empathy_context_menu_new (GtkWidget *attach_to)
989 menu = gtk_menu_new ();
991 gtk_menu_attach_to_widget (GTK_MENU (menu), attach_to, NULL);
993 /* menu is initially unowned but gtk_menu_attach_to_widget () taked its
994 * floating ref. We can either wait that @attach_to releases its ref when
995 * it will be destroyed (when leaving Empathy most of the time) or explicitely
996 * detach the menu when it's not displayed any more.
997 * We go for the latter as we don't want to keep useless menus in memory
998 * during the whole lifetime of Empathy. */
999 g_signal_connect (menu, "deactivate", G_CALLBACK (menu_deactivate_cb), NULL);
1005 empathy_get_current_action_time (void)
1007 return (tp_user_action_time_from_x11 (gtk_get_current_event_time ()));
1010 /* @words = tpaw_live_search_strip_utf8_string (@text);
1012 * User has to pass both so we don't have to compute @words ourself each time
1013 * this function is called. */
1015 empathy_individual_match_string (FolksIndividual *individual,
1022 gboolean retval = FALSE;
1024 /* check alias name */
1025 str = folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual));
1027 if (tpaw_live_search_match_words (str, words))
1030 personas = folks_individual_get_personas (individual);
1032 /* check contact id, remove the @server.com part */
1033 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
1034 while (retval == FALSE && gee_iterator_next (iter))
1036 FolksPersona *persona = gee_iterator_get (iter);
1039 if (empathy_folks_persona_is_interesting (persona))
1041 str = folks_persona_get_display_id (persona);
1043 /* Accept the persona if @text is a full prefix of his ID; that allows
1044 * user to find, say, a jabber contact by typing his JID. */
1045 if (g_str_has_prefix (str, text))
1051 gchar *dup_str = NULL;
1054 p = strstr (str, "@");
1056 str = dup_str = g_strndup (str, p - str);
1058 visible = tpaw_live_search_match_words (str, words);
1064 g_clear_object (&persona);
1066 g_clear_object (&iter);
1068 /* FIXME: Add more rules here, we could check phone numbers in
1069 * contact's vCard for example. */
1074 empathy_launch_program (const gchar *dir,
1078 GdkDisplay *display;
1079 GError *error = NULL;
1082 GdkAppLaunchContext *context = NULL;
1084 /* Try to run from source directory if possible */
1085 path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src",
1088 if (!g_file_test (path, G_FILE_TEST_EXISTS))
1091 path = g_build_filename (dir, name, NULL);
1095 cmd = g_strconcat (path, " ", args, NULL);
1097 cmd = g_strdup (path);
1099 app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &error);
1100 if (app_info == NULL)
1102 DEBUG ("Failed to create app info: %s", error->message);
1103 g_error_free (error);
1107 display = gdk_display_get_default ();
1108 context = gdk_display_get_app_launch_context (display);
1110 if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
1113 g_warning ("Failed to launch %s: %s", name, error->message);
1114 g_error_free (error);
1119 tp_clear_object (&app_info);
1120 tp_clear_object (&context);
1125 /* Most of the workspace manipulation code has been copied from libwnck
1126 * Copyright (C) 2001 Havoc Pennington
1127 * Copyright (C) 2005-2007 Vincent Untz
1130 _wnck_activate_workspace (Screen *screen,
1131 int new_active_space,
1138 display = DisplayOfScreen (screen);
1139 root = RootWindowOfScreen (screen);
1141 xev.xclient.type = ClientMessage;
1142 xev.xclient.serial = 0;
1143 xev.xclient.send_event = True;
1144 xev.xclient.display = display;
1145 xev.xclient.window = root;
1146 xev.xclient.message_type = gdk_x11_get_xatom_by_name ("_NET_CURRENT_DESKTOP");
1147 xev.xclient.format = 32;
1148 xev.xclient.data.l[0] = new_active_space;
1149 xev.xclient.data.l[1] = timestamp;
1150 xev.xclient.data.l[2] = 0;
1151 xev.xclient.data.l[3] = 0;
1152 xev.xclient.data.l[4] = 0;
1154 gdk_error_trap_push ();
1155 XSendEvent (display, root, False,
1156 SubstructureRedirectMask | SubstructureNotifyMask,
1158 XSync (display, False);
1159 gdk_error_trap_pop_ignored ();
1163 _wnck_get_cardinal (Screen *screen,
1176 display = DisplayOfScreen (screen);
1180 gdk_error_trap_push ();
1182 result = XGetWindowProperty (display, xwindow, atom,
1183 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
1184 &bytes_after, (void *) &num);
1185 err = gdk_error_trap_pop ();
1186 if (err != Success ||
1190 if (type != XA_CARDINAL)
1204 window_get_workspace (Screen *xscreen,
1209 if (!_wnck_get_cardinal (xscreen, win,
1210 gdk_x11_get_xatom_by_name ("_NET_WM_DESKTOP"), &number))
1216 /* Ask X to move to the desktop on which @window currently is
1217 * and the present @window. */
1219 empathy_move_to_window_desktop (GtkWindow *window,
1224 GdkWindow *gdk_window;
1227 screen = gtk_window_get_screen (window);
1228 xscreen = gdk_x11_screen_get_xscreen (screen);
1229 gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
1231 workspace = window_get_workspace (xscreen,
1232 gdk_x11_window_get_xid (gdk_window));
1233 if (workspace == -1)
1236 _wnck_activate_workspace (xscreen, workspace, timestamp);
1239 gtk_window_present_with_time (window, timestamp);
1243 empathy_set_css_provider (GtkWidget *widget)
1245 GtkCssProvider *provider;
1247 GError *error = NULL;
1250 filename = empathy_file_lookup ("empathy.css", "data");
1252 provider = gtk_css_provider_new ();
1254 if (!gtk_css_provider_load_from_path (provider, filename, &error))
1256 g_warning ("Failed to load css file '%s': %s", filename, error->message);
1257 g_error_free (error);
1262 screen = gtk_widget_get_screen (widget);
1264 screen = gdk_screen_get_default ();
1266 gtk_style_context_add_provider_for_screen (screen,
1267 GTK_STYLE_PROVIDER (provider),
1268 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1272 g_object_unref (provider);
1276 launch_app_info (GAppInfo *app_info,
1279 GdkAppLaunchContext *context = NULL;
1280 GdkDisplay *display;
1283 display = gdk_display_get_default ();
1284 context = gdk_display_get_app_launch_context (display);
1286 if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
1289 DEBUG ("Failed to launch %s: %s",
1290 g_app_info_get_display_name (app_info), err->message);
1291 g_propagate_error (error, err);
1295 tp_clear_object (&context);
1300 empathy_launch_external_app (const gchar *desktop_file,
1304 GDesktopAppInfo *desktop_info;
1308 desktop_info = g_desktop_app_info_new (desktop_file);
1309 if (desktop_info == NULL)
1311 DEBUG ("%s not found", desktop_file);
1313 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1314 "%s not found", desktop_file);
1320 result = launch_app_info (G_APP_INFO (desktop_info), error);
1327 /* glib doesn't have API to start a desktop file with args... (#637875) */
1328 cmd = g_strdup_printf ("%s %s", g_app_info_get_commandline (
1329 (GAppInfo *) desktop_info), args);
1331 app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &err);
1332 if (app_info == NULL)
1334 DEBUG ("Failed to launch '%s': %s", cmd, err->message);
1336 g_object_unref (desktop_info);
1337 g_propagate_error (error, err);
1341 result = launch_app_info (app_info, error);
1343 g_object_unref (app_info);
1347 g_object_unref (desktop_info);