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
);