X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy-gtk%2Fempathy-ui-utils.c;h=4503a39cc22f67f12cb613e3d1be4f55df183d46;hp=e7b8fce74194ddd70cb57d7523bf14d8ab8b02b9;hb=4d2d5a1622f93b69af195d7e3c4d4cb424dbec6e;hpb=435a811e440c3a5a79f504fa6f2850e63673f49a diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c index e7b8fce7..4503a39c 100644 --- a/libempathy-gtk/empathy-ui-utils.c +++ b/libempathy-gtk/empathy-ui-utils.c @@ -40,6 +40,7 @@ #include "empathy-ui-utils.h" #include "empathy-images.h" +#include "empathy-smiley-manager.h" #include "empathy-conf.h" #define DEBUG_FLAG EMPATHY_DEBUG_OTHER @@ -49,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) { @@ -72,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, @@ -99,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; } @@ -142,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); @@ -157,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); @@ -217,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) @@ -240,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; @@ -477,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) { @@ -1270,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); @@ -1281,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. - */ - - if (!empathy_window_get_is_visible (window)) { - /* Hide it so present brings it to the current workspace. */ - gtk_widget_hide (GTK_WIDGET (window)); + /* 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; + + /* 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 (); + 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); gtk_window_set_skip_taskbar_hint (window, FALSE); - /* FIXME: This shouldn't be required as gtk_window_present's doc says - * it deiconify automatically. */ gtk_window_deiconify (window); } @@ -1314,7 +1429,7 @@ 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); } @@ -1421,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) { - g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget)); + 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 (); - 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_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) +{ + 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); + } + + empathy_send_file (contact, file); + + g_object_unref (file); } static void @@ -1438,25 +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); } @@ -1488,12 +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); @@ -1532,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, @@ -1546,36 +1692,16 @@ empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler) gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (widget), TRUE); + 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); + g_signal_connect (widget, "response", G_CALLBACK (file_manager_receive_file_response_cb), handler); gtk_widget_show (widget); } -/** empathy_show_yes_no_question_dialog: - * @parent: The parent of the message dialog - * @message: The question message - * @response_callback: The callback connected to the "response" signal of - * the message dialog. - * @user_data: User data to pass to the @response_callback. - * - * A simple utility function to create a modal yes/no question message dialog - * and hooking to its "response" signal. - */ -void empathy_show_yes_no_question_dialog (GtkWindow *parent, - gchar *message, - GCallback response_callback, - gpointer user_data) -{ - GtkWidget *message_dialog; - - message_dialog = gtk_message_dialog_new (parent, - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, - message); - - g_signal_connect (message_dialog, "response", response_callback, user_data); - - gtk_widget_show (message_dialog); -}