Updated Friulian translation
[empathy-mirror.git] / libempathy-gtk / empathy-ui-utils.c
blobe662c695002ad59ff34146624a063e35c66b0544
1 /*
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):
28 * Paolo Maggi
29 * Jeroen Zwartepoorte
32 #include "config.h"
33 #include "empathy-ui-utils.h"
35 #include <X11/Xatom.h>
36 #include <gdk/gdkx.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"
50 void
51 empathy_gtk_init (void)
53 static gboolean initialized = FALSE;
55 if (initialized)
56 return;
58 empathy_init ();
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)
66 gchar *path;
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);
74 g_free (path);
77 initialized = TRUE;
80 const gchar *
81 empathy_icon_name_for_presence (TpConnectionPresenceType presence)
83 switch (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:
113 default:
114 return NULL;
117 return NULL;
120 const gchar *
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);
132 const gchar *
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);
145 const gchar *
146 empathy_protocol_name_for_contact (EmpathyContact *contact)
148 TpAccount *account;
150 g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
152 account = empathy_contact_get_account (contact);
153 if (account == NULL)
154 return NULL;
156 return tp_account_get_icon_name (account);
159 struct SizeData
161 gint width;
162 gint height;
163 gboolean preserve_aspect_ratio;
166 static void
167 pixbuf_from_avatar_size_prepared_cb (GdkPixbufLoader *loader,
168 int width,
169 int height,
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 (data->width < 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;
184 width = data->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;
192 else
194 height = 0.5 + (double) height * (double) data->width / (double) width;
195 width = data->width;
198 else
200 if (data->width > 0)
201 width = data->width;
203 if (data->height > 0)
204 height = data->height;
207 gdk_pixbuf_loader_set_size (loader, width, height);
210 static void
211 empathy_avatar_pixbuf_roundify (GdkPixbuf *pixbuf)
213 gint width, height, rowstride;
214 guchar *pixels;
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)
222 return;
224 /* Top left */
225 pixels[3] = 0;
226 pixels[7] = 0x80;
227 pixels[11] = 0xC0;
228 pixels[rowstride + 3] = 0x80;
229 pixels[rowstride * 2 + 3] = 0xC0;
231 /* Top right */
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;
238 /* Bottom left */
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;
245 /* Bottom right */
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;
253 static gboolean
254 empathy_gdk_pixbuf_is_opaque (GdkPixbuf *pixbuf)
256 gint height, rowstride, i;
257 guchar *pixels;
258 guchar *row;
260 height = gdk_pixbuf_get_height (pixbuf);
261 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
262 pixels = gdk_pixbuf_get_pixels (pixbuf);
264 row = pixels;
265 for (i = 3; i < rowstride; i+=4)
266 if (row[i] < 0xfe)
267 return FALSE;
269 for (i = 1; i < height - 1; i++)
271 row = pixels + (i*rowstride);
272 if (row[3] < 0xfe || row[rowstride-1] < 0xfe)
273 return FALSE;
276 row = pixels + ((height-1) * rowstride);
277 for (i = 3; i < rowstride; i+=4)
278 if (row[i] < 0xfe)
279 return FALSE;
281 return TRUE;
284 static GdkPixbuf *
285 pixbuf_round_corners (GdkPixbuf *pixbuf)
287 GdkPixbuf *result;
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),
298 result,
299 0, 0);
301 else
303 result = g_object_ref (pixbuf);
306 if (empathy_gdk_pixbuf_is_opaque (result))
307 empathy_avatar_pixbuf_roundify (result);
309 return result;
312 static GdkPixbuf *
313 avatar_pixbuf_from_loader (GdkPixbufLoader *loader)
315 GdkPixbuf *pixbuf;
317 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
319 return pixbuf_round_corners (pixbuf);
322 static GdkPixbuf *
323 empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avatar,
324 gint width,
325 gint height)
327 GdkPixbuf *pixbuf;
328 GdkPixbufLoader *loader;
329 struct SizeData data;
330 GError *error = NULL;
332 if (!avatar)
333 return NULL;
335 data.width = width;
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");
347 return NULL;
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);
356 return NULL;
359 gdk_pixbuf_loader_close (loader, NULL);
360 pixbuf = avatar_pixbuf_from_loader (loader);
362 g_object_unref (loader);
364 return pixbuf;
367 GdkPixbuf *
368 empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact *contact,
369 gint width,
370 gint height)
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);
381 typedef struct
383 GSimpleAsyncResult *result;
384 guint width;
385 guint height;
386 GCancellable *cancellable;
387 } PixbufAvatarFromIndividualClosure;
389 static PixbufAvatarFromIndividualClosure *
390 pixbuf_avatar_from_individual_closure_new (FolksIndividual *individual,
391 GSimpleAsyncResult *result,
392 gint width,
393 gint height,
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);
409 return closure;
412 static void
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)
426 static GdkPixbuf *
427 transform_pixbuf (GdkPixbuf *pixbuf)
429 GdkPixbuf *result;
431 result = pixbuf_round_corners (pixbuf);
432 g_object_unref (pixbuf);
434 return result;
437 static void
438 avatar_icon_load_cb (GObject *object,
439 GAsyncResult *result,
440 gpointer user_data)
442 GLoadableIcon *icon = G_LOADABLE_ICON (object);
443 PixbufAvatarFromIndividualClosure *closure = user_data;
444 GInputStream *stream;
445 GError *error = NULL;
446 GdkPixbuf *pixbuf;
447 GdkPixbuf *final_pixbuf;
449 stream = g_loadable_icon_load_finish (icon, result, NULL, &error);
450 if (error != NULL)
452 DEBUG ("Failed to open avatar stream: %s", error->message);
453 g_simple_async_result_set_from_error (closure->result, error);
454 goto out;
457 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
458 closure->width, closure->height, TRUE, closure->cancellable, &error);
460 g_object_unref (stream);
462 if (pixbuf == NULL)
464 DEBUG ("Failed to read avatar: %s", error->message);
465 g_simple_async_result_set_from_error (closure->result, error);
466 goto out;
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);
475 out:
476 g_simple_async_result_complete (closure->result);
478 g_clear_error (&error);
479 pixbuf_avatar_from_individual_closure_free (closure);
482 void
483 empathy_pixbuf_avatar_from_individual_scaled_async (
484 FolksIndividual *individual,
485 gint width,
486 gint height,
487 GCancellable *cancellable,
488 GAsyncReadyCallback callback,
489 gpointer user_data)
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);
508 return;
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 */
523 GdkPixbuf *
524 empathy_pixbuf_avatar_from_individual_scaled_finish (
525 FolksIndividual *individual,
526 GAsyncResult *result,
527 GError **error)
529 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
530 gboolean result_valid;
531 GdkPixbuf *pixbuf;
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))
537 return NULL;
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;
549 GdkPixbuf *
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)
560 return 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,
568 gint width,
569 gint height);
571 GdkPixbuf *
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;
579 gint height, width;
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);
586 numerator = 3;
587 denominator = 4;
589 icon_filename = tpaw_filename_from_icon_name (icon_name,
590 GTK_ICON_SIZE_MENU);
592 if (icon_filename == NULL)
594 DEBUG ("icon name: %s could not be found\n", icon_name);
595 return NULL;
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);
604 return NULL;
607 g_free (icon_filename);
609 if (!show_protocol)
610 return pix_status;
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)
620 return pix_status;
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,
626 1, 1,
627 GDK_INTERP_BILINEAR, 255);
629 g_object_unref (pix_protocol);
631 return pix_status;
634 static GdkPixbuf *
635 empathy_pixbuf_protocol_from_contact_scaled (EmpathyContact *contact,
636 gint width,
637 gint height)
639 TpAccount *account;
640 gchar *filename;
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);
652 g_free (filename);
655 return pixbuf;
658 void
659 empathy_url_show (GtkWidget *parent,
660 const char *url)
662 gchar *real_url;
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);
673 if (error)
675 GtkWidget *dialog;
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);
692 g_free (real_url);
695 void
696 empathy_send_file (EmpathyContact *contact,
697 GFile *file)
699 EmpathyFTFactory *factory;
700 GtkRecentManager *manager;
701 gchar *uri;
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);
714 g_free (uri);
716 g_object_unref (factory);
719 void
720 empathy_send_file_from_uri_list (EmpathyContact *contact,
721 const gchar *uri_list)
723 const gchar *nl;
724 GFile *file;
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");
733 if (!nl)
734 nl = strchr (uri_list, '\n');
736 if (nl)
738 gchar *uri = g_strndup (uri_list, nl - uri_list);
739 file = g_file_new_for_uri (uri);
740 g_free (uri);
742 else
744 file = g_file_new_for_uri (uri_list);
747 empathy_send_file (contact, file);
749 g_object_unref (file);
752 static void
753 file_manager_send_file_response_cb (GtkDialog *widget,
754 gint response_id,
755 EmpathyContact *contact)
757 GFile *file;
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));
772 static gboolean
773 filter_cb (const GtkFileFilterInfo *filter_info,
774 gpointer data)
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,
788 NULL, NULL);
790 return filter;
793 void
794 empathy_send_file_with_file_chooser (EmpathyContact *contact)
796 GtkWidget *widget;
797 GtkWidget *button;
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,
806 NULL);
808 /* send button */
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,
816 GTK_RESPONSE_OK);
818 gtk_widget_set_can_default (button, TRUE);
819 gtk_dialog_set_default_response (GTK_DIALOG (widget),
820 GTK_RESPONSE_OK);
822 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), FALSE);
824 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget),
825 g_get_home_dir ());
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);
836 static void
837 file_manager_receive_file_response_cb (GtkDialog *dialog,
838 GtkResponseType response,
839 EmpathyFTHandler *handler)
841 EmpathyFTFactory *factory;
842 GFile *file;
844 if (response == GTK_RESPONSE_OK)
846 GFile *parent;
847 GFileInfo *info;
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);
858 if (error != NULL)
860 g_warning ("Error: %s", error->message);
862 g_object_unref (file);
863 return;
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,
876 GTK_BUTTONS_CLOSE,
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);
898 return;
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);
909 else
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));
920 void
921 empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler)
923 GtkWidget *widget;
924 const gchar *dir;
925 EmpathyContact *contact;
926 gchar *title;
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,
938 NULL);
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);
947 if (dir == NULL)
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);
957 g_free (title);
960 void
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;
970 static void
971 menu_deactivate_cb (GtkMenu *menu,
972 gpointer user_data)
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. */
984 GtkWidget *
985 empathy_context_menu_new (GtkWidget *attach_to)
987 GtkWidget *menu;
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);
1001 return menu;
1004 gint64
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. */
1014 gboolean
1015 empathy_individual_match_string (FolksIndividual *individual,
1016 const char *text,
1017 GPtrArray *words)
1019 const gchar *str;
1020 GeeSet *personas;
1021 GeeIterator *iter;
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))
1028 return TRUE;
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);
1037 const gchar *p;
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))
1047 retval = TRUE;
1049 else
1051 gchar *dup_str = NULL;
1052 gboolean visible;
1054 p = strstr (str, "@");
1055 if (p != NULL)
1056 str = dup_str = g_strndup (str, p - str);
1058 visible = tpaw_live_search_match_words (str, words);
1059 g_free (dup_str);
1060 if (visible)
1061 retval = TRUE;
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. */
1070 return retval;
1073 void
1074 empathy_launch_program (const gchar *dir,
1075 const gchar *name,
1076 const gchar *args)
1078 GdkDisplay *display;
1079 GError *error = NULL;
1080 gchar *path, *cmd;
1081 GAppInfo *app_info;
1082 GdkAppLaunchContext *context = NULL;
1084 /* Try to run from source directory if possible */
1085 path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src",
1086 name, NULL);
1088 if (!g_file_test (path, G_FILE_TEST_EXISTS))
1090 g_free (path);
1091 path = g_build_filename (dir, name, NULL);
1094 if (args != NULL)
1095 cmd = g_strconcat (path, " ", args, NULL);
1096 else
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);
1104 goto out;
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,
1111 &error))
1113 g_warning ("Failed to launch %s: %s", name, error->message);
1114 g_error_free (error);
1115 goto out;
1118 out:
1119 tp_clear_object (&app_info);
1120 tp_clear_object (&context);
1121 g_free (path);
1122 g_free (cmd);
1125 #ifdef GDK_WINDOWING_X11
1127 /* Most of the workspace manipulation code has been copied from libwnck
1128 * Copyright (C) 2001 Havoc Pennington
1129 * Copyright (C) 2005-2007 Vincent Untz
1131 static void
1132 _wnck_activate_workspace (Screen *screen,
1133 int new_active_space,
1134 Time timestamp)
1136 Display *display;
1137 Window root;
1138 XEvent xev;
1140 display = DisplayOfScreen (screen);
1141 root = RootWindowOfScreen (screen);
1143 xev.xclient.type = ClientMessage;
1144 xev.xclient.serial = 0;
1145 xev.xclient.send_event = True;
1146 xev.xclient.display = display;
1147 xev.xclient.window = root;
1148 xev.xclient.message_type = gdk_x11_get_xatom_by_name ("_NET_CURRENT_DESKTOP");
1149 xev.xclient.format = 32;
1150 xev.xclient.data.l[0] = new_active_space;
1151 xev.xclient.data.l[1] = timestamp;
1152 xev.xclient.data.l[2] = 0;
1153 xev.xclient.data.l[3] = 0;
1154 xev.xclient.data.l[4] = 0;
1156 gdk_error_trap_push ();
1157 XSendEvent (display, root, False,
1158 SubstructureRedirectMask | SubstructureNotifyMask,
1159 &xev);
1160 XSync (display, False);
1161 gdk_error_trap_pop_ignored ();
1164 static gboolean
1165 _wnck_get_cardinal (Screen *screen,
1166 Window xwindow,
1167 Atom atom,
1168 int *val)
1170 Display *display;
1171 Atom type;
1172 int format;
1173 gulong nitems;
1174 gulong bytes_after;
1175 gulong *num;
1176 int err, result;
1178 display = DisplayOfScreen (screen);
1180 *val = 0;
1182 gdk_error_trap_push ();
1183 type = None;
1184 result = XGetWindowProperty (display, xwindow, atom,
1185 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
1186 &bytes_after, (void *) &num);
1187 err = gdk_error_trap_pop ();
1188 if (err != Success ||
1189 result != Success)
1190 return FALSE;
1192 if (type != XA_CARDINAL)
1194 XFree (num);
1195 return FALSE;
1198 *val = *num;
1200 XFree (num);
1202 return TRUE;
1205 static int
1206 window_get_workspace (Screen *xscreen,
1207 Window win)
1209 int number;
1211 if (!_wnck_get_cardinal (xscreen, win,
1212 gdk_x11_get_xatom_by_name ("_NET_WM_DESKTOP"), &number))
1213 return -1;
1215 return number;
1218 #endif
1220 /* Ask X to move to the desktop on which @window currently is
1221 * and the present @window. */
1222 void
1223 empathy_move_to_window_desktop (GtkWindow *window,
1224 guint32 timestamp)
1226 #ifdef GDK_WINDOWING_X11
1227 GdkScreen *screen;
1228 Screen *xscreen;
1229 GdkWindow *gdk_window;
1230 int workspace;
1232 screen = gtk_window_get_screen (window);
1233 if (!GDK_IS_X11_SCREEN (screen))
1234 goto out;
1236 xscreen = gdk_x11_screen_get_xscreen (screen);
1237 gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
1239 workspace = window_get_workspace (xscreen,
1240 gdk_x11_window_get_xid (gdk_window));
1241 if (workspace == -1)
1242 goto out;
1244 _wnck_activate_workspace (xscreen, workspace, timestamp);
1246 out:
1247 gtk_window_present_with_time (window, timestamp);
1248 #endif
1251 void
1252 empathy_set_css_provider (GtkWidget *widget)
1254 GtkCssProvider *provider;
1255 gchar *filename;
1256 GError *error = NULL;
1257 GdkScreen *screen;
1259 filename = empathy_file_lookup ("empathy.css", "data");
1261 provider = gtk_css_provider_new ();
1263 if (!gtk_css_provider_load_from_path (provider, filename, &error))
1265 g_warning ("Failed to load css file '%s': %s", filename, error->message);
1266 g_error_free (error);
1267 goto out;
1270 if (widget != NULL)
1271 screen = gtk_widget_get_screen (widget);
1272 else
1273 screen = gdk_screen_get_default ();
1275 gtk_style_context_add_provider_for_screen (screen,
1276 GTK_STYLE_PROVIDER (provider),
1277 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1279 out:
1280 g_free (filename);
1281 g_object_unref (provider);
1284 static gboolean
1285 launch_app_info (GAppInfo *app_info,
1286 GError **error)
1288 GdkAppLaunchContext *context = NULL;
1289 GdkDisplay *display;
1290 GError *err = NULL;
1292 display = gdk_display_get_default ();
1293 context = gdk_display_get_app_launch_context (display);
1295 if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
1296 &err))
1298 DEBUG ("Failed to launch %s: %s",
1299 g_app_info_get_display_name (app_info), err->message);
1300 g_propagate_error (error, err);
1301 return FALSE;
1304 tp_clear_object (&context);
1305 return TRUE;
1308 gboolean
1309 empathy_launch_external_app (const gchar *desktop_file,
1310 const gchar *args,
1311 GError **error)
1313 GDesktopAppInfo *desktop_info;
1314 gboolean result;
1315 GError *err = NULL;
1317 desktop_info = g_desktop_app_info_new (desktop_file);
1318 if (desktop_info == NULL)
1320 DEBUG ("%s not found", desktop_file);
1322 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1323 "%s not found", desktop_file);
1324 return FALSE;
1327 if (args == NULL)
1329 result = launch_app_info (G_APP_INFO (desktop_info), error);
1331 else
1333 gchar *cmd;
1334 GAppInfo *app_info;
1336 /* glib doesn't have API to start a desktop file with args... (#637875) */
1337 cmd = g_strdup_printf ("%s %s", g_app_info_get_commandline (
1338 (GAppInfo *) desktop_info), args);
1340 app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &err);
1341 if (app_info == NULL)
1343 DEBUG ("Failed to launch '%s': %s", cmd, err->message);
1344 g_free (cmd);
1345 g_object_unref (desktop_info);
1346 g_propagate_error (error, err);
1347 return FALSE;
1350 result = launch_app_info (app_info, error);
1352 g_object_unref (app_info);
1353 g_free (cmd);
1356 g_object_unref (desktop_info);
1357 return result;