X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy-gtk%2Fempathy-ui-utils.c;h=4503a39cc22f67f12cb613e3d1be4f55df183d46;hp=c33abb63d2ec4187203ca22ea3852ad0d7708c07;hb=4d2d5a1622f93b69af195d7e3c4d4cb424dbec6e;hpb=58d08cef143811d990218551a922451609978610 diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c index c33abb63..4503a39c 100644 --- a/libempathy-gtk/empathy-ui-utils.c +++ b/libempathy-gtk/empathy-ui-utils.c @@ -37,12 +37,10 @@ #include #include #include -#include - -#include #include "empathy-ui-utils.h" #include "empathy-images.h" +#include "empathy-smiley-manager.h" #include "empathy-conf.h" #define DEBUG_FLAG EMPATHY_DEBUG_OTHER @@ -52,14 +50,6 @@ #include #include -#define SCHEMES "(https?|s?ftps?|nntp|news|javascript|about|ghelp|apt|telnet|"\ - "file|webcal|mailto)" -#define BODY "([^\\ \\n]+)" -#define END_BODY "([^\\ \\n]*[^,;\?><()\\ \"\\.\\n])" -#define URI_REGEX "("SCHEMES"://"END_BODY")" \ - "|((mailto:)?"BODY"@"BODY"\\."END_BODY")"\ - "|((www|ftp)\\."END_BODY")" - void empathy_gtk_init (void) { @@ -75,19 +65,6 @@ empathy_gtk_init (void) initialized = TRUE; } -GRegex * -empathy_uri_regex_dup_singleton (void) -{ - static GRegex *uri_regex = NULL; - - /* We intentionally leak the regex so it's not recomputed */ - if (!uri_regex) { - uri_regex = g_regex_new (URI_REGEX, 0, 0, NULL); - } - - return g_regex_ref (uri_regex); -} - static GtkBuilder * builder_get_file_valist (const gchar *filename, const gchar *first_object, @@ -102,9 +79,19 @@ builder_get_file_valist (const gchar *filename, gui = gtk_builder_new (); if (!gtk_builder_add_from_file (gui, filename, &error)) { - g_critical ("GtkBuilder Error: %s", error->message); + g_critical ("GtkBuilder Error (%s): %s", + filename, error->message); g_clear_error (&error); g_object_unref (gui); + + /* we need to iterate and set all of the pointers to NULL */ + for (name = first_object; name; + name = va_arg (args, const gchar *)) { + object_ptr = va_arg (args, GObject**); + + *object_ptr = NULL; + } + return NULL; } @@ -145,13 +132,13 @@ empathy_builder_connect (GtkBuilder *gui, { va_list args; const gchar *name; - const gchar *signal; + const gchar *sig; GObject *object; GCallback callback; va_start (args, first_object); for (name = first_object; name; name = va_arg (args, const gchar *)) { - signal = va_arg (args, const gchar *); + sig = va_arg (args, const gchar *); callback = va_arg (args, GCallback); object = gtk_builder_get_object (gui, name); @@ -160,7 +147,7 @@ empathy_builder_connect (GtkBuilder *gui, continue; } - g_signal_connect (object, signal, callback, user_data); + g_signal_connect (object, sig, callback, user_data); } va_end (args); @@ -183,16 +170,6 @@ empathy_builder_unref_and_keep_widget (GtkBuilder *gui, return widget; } -const gchar * -empathy_icon_name_from_account (McAccount *account) -{ - McProfile *profile; - - profile = mc_account_get_profile (account); - - return mc_profile_get_icon_name (profile); -} - const gchar * empathy_icon_name_for_presence (TpConnectionPresenceType presence) { @@ -230,6 +207,21 @@ empathy_icon_name_for_contact (EmpathyContact *contact) return empathy_icon_name_for_presence (presence); } +const gchar * +empathy_protocol_name_for_contact (EmpathyContact *contact) +{ + TpAccount *account; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + + account = empathy_contact_get_account (contact); + if (account == NULL) { + return NULL; + } + + return tp_account_get_icon_name (account); +} + GdkPixbuf * empathy_pixbuf_from_data (gchar *data, gsize data_size) @@ -253,7 +245,7 @@ empathy_pixbuf_from_data_and_mime (gchar *data, } loader = gdk_pixbuf_loader_new (); - if (!gdk_pixbuf_loader_write (loader, data, data_size, &error)) { + if (!gdk_pixbuf_loader_write (loader, (guchar *) data, data_size, &error)) { DEBUG ("Failed to write to pixbuf loader: %s", error ? error->message : "No error given"); goto out; @@ -490,6 +482,104 @@ empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact *contact, return empathy_pixbuf_from_avatar_scaled (avatar, width, height); } +GdkPixbuf * +empathy_pixbuf_contact_status_icon (EmpathyContact *contact, + gboolean show_protocol) +{ + const gchar *icon_name; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + + icon_name = empathy_icon_name_for_contact (contact); + + if (icon_name == NULL) { + return NULL; + } + return empathy_pixbuf_contact_status_icon_with_icon_name (contact, + icon_name, + show_protocol); +} + +GdkPixbuf * +empathy_pixbuf_contact_status_icon_with_icon_name (EmpathyContact *contact, + const gchar *icon_name, + gboolean show_protocol) +{ + GdkPixbuf *pix_status; + GdkPixbuf *pix_protocol; + gchar *icon_filename; + gint height, width; + gint numerator, denominator; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + g_return_val_if_fail (icon_name != NULL, NULL); + + numerator = 3; + denominator = 4; + + icon_filename = empathy_filename_from_icon_name (icon_name, + GTK_ICON_SIZE_MENU); + if (icon_filename == NULL) { + DEBUG ("icon name: %s could not be found\n", icon_name); + return NULL; + } + + pix_status = gdk_pixbuf_new_from_file (icon_filename, NULL); + + g_free (icon_filename); + + if (pix_status == NULL) { + DEBUG ("Could not open icon %s\n", icon_filename); + return NULL; + } + + if (!show_protocol) + return pix_status; + + height = gdk_pixbuf_get_height (pix_status); + width = gdk_pixbuf_get_width (pix_status); + + pix_protocol = empathy_pixbuf_protocol_from_contact_scaled (contact, + width * numerator / denominator, + height * numerator / denominator); + + if (pix_protocol == NULL) { + return pix_status; + } + gdk_pixbuf_composite (pix_protocol, pix_status, + 0, height - height * numerator / denominator, + width * numerator / denominator, height * numerator / denominator, + 0, height - height * numerator / denominator, + 1, 1, + GDK_INTERP_BILINEAR, 255); + + g_object_unref (pix_protocol); + + return pix_status; +} + +GdkPixbuf * +empathy_pixbuf_protocol_from_contact_scaled (EmpathyContact *contact, + gint width, + gint height) +{ + TpAccount *account; + gchar *filename; + GdkPixbuf *pixbuf = NULL; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + + account = empathy_contact_get_account (contact); + filename = empathy_filename_from_icon_name (tp_account_get_icon_name (account), + GTK_ICON_SIZE_MENU); + if (filename != NULL) { + pixbuf = gdk_pixbuf_new_from_file_at_size (filename, width, height, NULL); + g_free (filename); + } + + return pixbuf; +} + GdkPixbuf * empathy_pixbuf_scale_down_if_necessary (GdkPixbuf *pixbuf, gint max_size) { @@ -558,6 +648,29 @@ empathy_pixbuf_from_icon_name (const gchar *icon_name, return empathy_pixbuf_from_icon_name_sized (icon_name, size); } +gchar * +empathy_filename_from_icon_name (const gchar *icon_name, + GtkIconSize icon_size) +{ + GtkIconTheme *icon_theme; + GtkIconInfo *icon_info; + gint w, h; + gint size = 48; + gchar *ret; + + icon_theme = gtk_icon_theme_get_default (); + + if (gtk_icon_size_lookup (icon_size, &w, &h)) { + size = (w + h) / 2; + } + + icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, 0); + ret = g_strdup (gtk_icon_info_get_filename (icon_info)); + gtk_icon_info_free (icon_info); + + return ret; +} + /* Stolen from GtkSourceView, hence the weird intendation. Please keep it like * that to make it easier to apply changes from the original code. */ @@ -1228,7 +1341,7 @@ empathy_window_get_is_visible (GtkWindow *window) g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); - gdk_window = GTK_WIDGET (window)->window; + gdk_window = gtk_widget_get_window (GTK_WIDGET (window)); if (!gdk_window) { return FALSE; } @@ -1250,7 +1363,7 @@ empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon) GdkWindow *gdk_window; gtk_status_icon_get_geometry (status_icon, NULL, &icon_location, NULL); - gdk_window = GTK_WIDGET (window)->window; + gdk_window = gtk_widget_get_window (GTK_WIDGET (window)); dpy = gdk_x11_drawable_get_xdisplay (gdk_window); data[0] = icon_location.x; @@ -1260,7 +1373,8 @@ empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon) XChangeProperty (dpy, GDK_WINDOW_XID (gdk_window), - gdk_x11_get_xatom_by_name_for_display (gdk_drawable_get_display (gdk_window), + gdk_x11_get_xatom_by_name_for_display ( + gdk_drawable_get_display (gdk_window), "_NET_WM_ICON_GEOMETRY"), XA_CARDINAL, 32, PropModeReplace, (guchar *)&data, 4); @@ -1271,27 +1385,38 @@ empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon) /* Takes care of moving the window to the current workspace. */ void -empathy_window_present (GtkWindow *window, - gboolean steal_focus) +empathy_window_present (GtkWindow *window) { + GdkWindow *gdk_window; guint32 timestamp; g_return_if_fail (GTK_IS_WINDOW (window)); - /* There are three cases: hidden, visible, visible on another - * workspace. - */ + /* Move the window to the current workspace before trying to show it. + * This is the behaviour people expect when clicking on the statusbar icon. */ + gdk_window = gtk_widget_get_window (GTK_WIDGET (window)); + if (gdk_window) { + gint x, y; + gint w, h; - if (!empathy_window_get_is_visible (window)) { - /* Hide it so present brings it to the current workspace. */ - gtk_widget_hide (GTK_WIDGET (window)); + /* Has no effect if the WM has viewports, like compiz */ + gdk_x11_window_move_to_current_desktop (gdk_window); + + /* If window is still off-screen, hide it to force it to + * reposition on the current workspace. */ + gtk_window_get_position (window, &x, &y); + gtk_window_get_size (window, &w, &h); + if (!EMPATHY_RECT_IS_ON_SCREEN (x, y, w, h)) + gtk_widget_hide (GTK_WIDGET (window)); } timestamp = gtk_get_current_event_time (); - gtk_window_set_skip_taskbar_hint (window, FALSE); + if (timestamp == 0) + /* No event, fallback to _NET_WM_USER_TIME */ + timestamp = gdk_x11_display_get_user_time (gdk_display_get_default ()); + gtk_window_present_with_time (window, timestamp); - /* FIXME: This shouldn't be required as gtk_window_present's doc says - * it deiconify automatically. */ + gtk_window_set_skip_taskbar_hint (window, FALSE); gtk_window_deiconify (window); } @@ -1304,32 +1429,53 @@ empathy_get_toplevel_window (GtkWidget *widget) toplevel = gtk_widget_get_toplevel (widget); if (GTK_IS_WINDOW (toplevel) && - GTK_WIDGET_TOPLEVEL (toplevel)) { + gtk_widget_is_toplevel (toplevel)) { return GTK_WINDOW (toplevel); } return NULL; } -/* The URL opening code can't handle schemeless strings, so we try to be - * smart and add http if there is no scheme or doesn't look like a mail - * address. This should work in most cases, and let us click on strings - * like "www.gnome.org". +/** empathy_make_absolute_url_len: + * @url: an url + * @len: a length + * + * Same as #empathy_make_absolute_url but for a limited string length */ -static gchar * -fixup_url (const gchar *url) +gchar * +empathy_make_absolute_url_len (const gchar *url, + guint len) { + g_return_val_if_fail (url != NULL, NULL); + if (g_str_has_prefix (url, "ghelp:") || g_str_has_prefix (url, "mailto:") || strstr (url, ":/")) { - return NULL; + return g_strndup (url, len); } if (strstr (url, "@")) { - return g_strdup_printf ("mailto:%s", url); + return g_strdup_printf ("mailto:%.*s", len, url); } - return g_strdup_printf ("http://%s", url); + return g_strdup_printf ("http://%.*s", len, url); +} + +/** empathy_make_absolute_url: + * @url: an url + * + * The URL opening code can't handle schemeless strings, so we try to be + * smart and add http if there is no scheme or doesn't look like a mail + * address. This should work in most cases, and let us click on strings + * like "www.gnome.org". + * + * Returns: a newly allocated url with proper mailto: or http:// prefix, use + * g_free when your are done with it + */ +gchar * +empathy_make_absolute_url (const gchar *url) +{ + return empathy_make_absolute_url_len (url, strlen (url)); } void @@ -1339,12 +1485,12 @@ empathy_url_show (GtkWidget *parent, gchar *real_url; GError *error = NULL; - real_url = fixup_url (url); - if (real_url) { - url = real_url; - } + g_return_if_fail (parent == NULL || GTK_IS_WIDGET (parent)); + g_return_if_fail (url != NULL); + + real_url = empathy_make_absolute_url (url); - gtk_show_uri (gtk_widget_get_screen (parent), url, + gtk_show_uri (parent ? gtk_widget_get_screen (parent) : NULL, real_url, gtk_get_current_event_time (), &error); if (error) { @@ -1390,16 +1536,55 @@ empathy_link_button_new (const gchar *url, } void -empathy_toggle_button_set_state_quietly (GtkWidget *widget, - GCallback callback, - gpointer user_data, - gboolean active) +empathy_send_file (EmpathyContact *contact, GFile *file) +{ + EmpathyFTFactory *factory; + GtkRecentManager *manager; + gchar *uri; + + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + g_return_if_fail (G_IS_FILE (file)); + + factory = empathy_ft_factory_dup_singleton (); + + empathy_ft_factory_new_transfer_outgoing (factory, contact, file); + + uri = g_file_get_uri (file); + manager = gtk_recent_manager_get_default (); + gtk_recent_manager_add_item (manager, uri); + g_free (uri); + + g_object_unref (factory); +} + +void +empathy_send_file_from_uri_list (EmpathyContact *contact, const gchar *uri_list) { - g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget)); + const gchar *nl; + GFile *file; + + /* Only handle a single file for now. It would be wicked cool to be + able to do multiple files, offering to zip them or whatever like + nautilus-sendto does. Note that text/uri-list is defined to have + each line terminated by \r\n, but we can be tolerant of applications + that only use \n or don't terminate single-line entries. + */ + nl = strstr (uri_list, "\r\n"); + if (!nl) { + nl = strchr (uri_list, '\n'); + } + if (nl) { + gchar *uri = g_strndup (uri_list, nl - uri_list); + file = g_file_new_for_uri (uri); + g_free (uri); + } + else { + file = g_file_new_for_uri (uri_list); + } - g_signal_handlers_block_by_func (widget, callback, user_data); - g_object_set (widget, "active", active, NULL); - g_signal_handlers_unblock_by_func (widget, callback, user_data); + empathy_send_file (contact, file); + + g_object_unref (file); } static void @@ -1407,24 +1592,13 @@ file_manager_send_file_response_cb (GtkDialog *widget, gint response_id, EmpathyContact *contact) { - EmpathyFTFactory *factory; GFile *file; - gchar *uri; - GtkRecentManager *manager; if (response_id == GTK_RESPONSE_OK) { file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget)); - uri = g_file_get_uri (file); - - factory = empathy_ft_factory_dup_singleton (); - empathy_ft_factory_new_transfer_outgoing (factory, contact, file); + empathy_send_file (contact, file); - manager = gtk_recent_manager_get_default (); - gtk_recent_manager_add_item (manager, uri); - - g_free (uri); - g_object_unref (factory); g_object_unref (file); } @@ -1456,10 +1630,15 @@ empathy_send_file_with_file_chooser (EmpathyContact *contact) gtk_widget_show (button); gtk_dialog_add_action_widget (GTK_DIALOG (widget), button, GTK_RESPONSE_OK); - GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + gtk_widget_set_can_default (button, TRUE); gtk_dialog_set_default_response (GTK_DIALOG (widget), GTK_RESPONSE_OK); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), FALSE); + + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), + g_get_home_dir ()); + g_signal_connect (widget, "response", G_CALLBACK (file_manager_send_file_response_cb), contact); @@ -1484,6 +1663,11 @@ file_manager_receive_file_response_cb (GtkDialog *dialog, g_object_unref (factory); g_object_unref (file); + } else { + /* unref the handler, as we dismissed the file chooser, + * and refused the transfer. + */ + g_object_unref (handler); } gtk_widget_destroy (GTK_WIDGET (dialog)); @@ -1493,6 +1677,7 @@ void empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler) { GtkWidget *widget; + const gchar *dir; widget = gtk_file_chooser_dialog_new (_("Select a destination"), NULL, @@ -1504,144 +1689,19 @@ empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler) NULL); gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (widget), empathy_ft_handler_get_filename (handler)); - g_signal_connect (widget, "response", - G_CALLBACK (file_manager_receive_file_response_cb), handler); + gtk_file_chooser_set_do_overwrite_confirmation + (GTK_FILE_CHOOSER (widget), TRUE); - gtk_widget_show (widget); -} - -typedef struct { - EmpathySound sound_id; - const char * event_ca_id; - const char * event_ca_description; - const char * gconf_key; -} EmpathySoundEntry; - -/* NOTE: these entries MUST be in the same order than EmpathySound enum */ -static EmpathySoundEntry sound_entries[LAST_EMPATHY_SOUND] = { - { EMPATHY_SOUND_MESSAGE_INCOMING, "message-new-instant", - N_("Received an instant message"), EMPATHY_PREFS_SOUNDS_INCOMING_MESSAGE } , - { EMPATHY_SOUND_MESSAGE_OUTGOING, "message-sent-instant", - N_("Sent an instant message"), EMPATHY_PREFS_SOUNDS_OUTGOING_MESSAGE } , - { EMPATHY_SOUND_CONVERSATION_NEW, "message-new-instant", - N_("Incoming chat request"), EMPATHY_PREFS_SOUNDS_NEW_CONVERSATION }, - { EMPATHY_SOUND_CONTACT_CONNECTED, "service-login", - N_("Contact connected"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGIN }, - { EMPATHY_SOUND_CONTACT_DISCONNECTED, "service-logout", - N_("Contact disconnected"), EMPATHY_PREFS_SOUNDS_CONTACT_LOGOUT }, - { EMPATHY_SOUND_ACCOUNT_CONNECTED, "service-login", - N_("Connected to server"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGIN }, - { EMPATHY_SOUND_ACCOUNT_DISCONNECTED, "service-logout", - N_("Disconnected from server"), EMPATHY_PREFS_SOUNDS_SERVICE_LOGOUT }, - { EMPATHY_SOUND_PHONE_INCOMING, "phone-incoming-call", - N_("Incoming voice call"), NULL }, - { EMPATHY_SOUND_PHONE_OUTGOING, "phone-outgoing-calling", - N_("Outgoing voice call"), NULL }, - { EMPATHY_SOUND_PHONE_HANGUP, "phone-hangup", - N_("Voice call ended"), NULL }, -}; + dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD); + if (dir == NULL) + /* Fallback to $HOME if $XDG_DOWNLOAD_DIR is not set */ + dir = g_get_home_dir (); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), dir); -static gboolean -empathy_sound_pref_is_enabled (const char *key) -{ - EmpathyConf *conf; - gboolean res; - - conf = empathy_conf_get (); - res = FALSE; - - empathy_conf_get_bool (conf, EMPATHY_PREFS_SOUNDS_ENABLED, &res); - - if (!res) { - return FALSE; - } - - if (!empathy_check_available_state ()) { - empathy_conf_get_bool (conf, EMPATHY_PREFS_SOUNDS_DISABLED_AWAY, - &res); - if (res) { - return FALSE; - } - } - - empathy_conf_get_bool (conf, key, &res); - - return res; -} - -void -empathy_sound_stop (EmpathySound sound_id) -{ - EmpathySoundEntry *entry; - - g_return_if_fail (sound_id < LAST_EMPATHY_SOUND); - - entry = &(sound_entries[sound_id]); - g_return_if_fail (entry->sound_id == sound_id); - - ca_context_cancel (ca_gtk_context_get (), entry->sound_id); -} - - -gboolean -empathy_sound_play_full (GtkWidget *widget, EmpathySound sound_id, - ca_finish_callback_t callback, gpointer user_data) -{ - EmpathySoundEntry *entry; - gboolean should_play = TRUE; - ca_proplist *p = NULL; - ca_context *c; - - g_return_val_if_fail (sound_id < LAST_EMPATHY_SOUND, FALSE); - - entry = &(sound_entries[sound_id]); - g_return_val_if_fail (entry->sound_id == sound_id, FALSE); - - if (entry->gconf_key != NULL) { - should_play = empathy_sound_pref_is_enabled (entry->gconf_key); - } - - if (!should_play) - return FALSE; - - c = ca_gtk_context_get (); - ca_context_cancel (c, entry->sound_id); - - DEBUG ("Play sound \"%s\" (%s)", - entry->event_ca_id, - entry->event_ca_description); - - if (ca_proplist_create (&p) < 0) - goto failed; - - if (ca_proplist_sets (p, CA_PROP_EVENT_ID, entry->event_ca_id) < 0) - goto failed; - - if (ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, - gettext (entry->event_ca_id)) < 0) - goto failed; - - if (ca_gtk_proplist_set_for_widget (p, widget) < 0) - goto failed; - - ca_context_play_full (ca_gtk_context_get (), entry->sound_id, - p, callback, user_data); - - ca_proplist_destroy (p); - - return TRUE; - -failed: - if (p != NULL) - ca_proplist_destroy (p); - - return FALSE; -} + g_signal_connect (widget, "response", + G_CALLBACK (file_manager_receive_file_response_cb), handler); -void -empathy_sound_play (GtkWidget *widget, EmpathySound sound_id) -{ - empathy_sound_play_full (widget, sound_id, NULL, NULL); + gtk_widget_show (widget); }