/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * Copyright (C) 2007 Ray Strode * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "gdm-user-manager.h" #include "gdm-user-chooser-widget.h" #define KEY_DISABLE_USER_LIST "/apps/gdm/simple-greeter/disable_user_list" enum { USER_NO_DISPLAY = 1 << 0, USER_ACCOUNT_DISABLED = 1 << 1, }; #define DEFAULT_USER_ICON "stock_person" #define GDM_USER_CHOOSER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_CHOOSER_WIDGET, GdmUserChooserWidgetPrivate)) #define MAX_ICON_SIZE 128 struct GdmUserChooserWidgetPrivate { GdmUserManager *manager; GtkIconTheme *icon_theme; GdkPixbuf *logged_in_pixbuf; GdkPixbuf *stock_person_pixbuf; guint loaded : 1; guint show_user_other : 1; guint show_user_guest : 1; guint show_user_auto : 1; guint show_normal_users : 1; guint has_user_other : 1; guint load_idle_id; }; enum { PROP_0, PROP_SHOW_USER_GUEST, PROP_SHOW_USER_AUTO, PROP_SHOW_USER_OTHER, }; static void gdm_user_chooser_widget_class_init (GdmUserChooserWidgetClass *klass); static void gdm_user_chooser_widget_init (GdmUserChooserWidget *user_chooser_widget); static void gdm_user_chooser_widget_finalize (GObject *object); G_DEFINE_TYPE (GdmUserChooserWidget, gdm_user_chooser_widget, GDM_TYPE_CHOOSER_WIDGET) static void add_user_other (GdmUserChooserWidget *widget); static void remove_user_other (GdmUserChooserWidget *widget); static int get_font_height_for_widget (GtkWidget *widget) { PangoFontMetrics *metrics; PangoContext *context; int ascent; int descent; int height; gtk_widget_ensure_style (widget); context = gtk_widget_get_pango_context (widget); metrics = pango_context_get_metrics (context, widget->style->font_desc, pango_context_get_language (context)); ascent = pango_font_metrics_get_ascent (metrics); descent = pango_font_metrics_get_descent (metrics); height = PANGO_PIXELS (ascent + descent); pango_font_metrics_unref (metrics); return height; } static int get_icon_height_for_widget (GtkWidget *widget) { int font_height; int height; font_height = get_font_height_for_widget (widget); height = 3 * font_height; if (height > MAX_ICON_SIZE) { height = MAX_ICON_SIZE; } g_debug ("GdmUserChooserWidget: font height %d; using icon size %d", font_height, height); return height; } static void update_other_user_visibility (GdmUserChooserWidget *widget) { int number_of_users; if (!widget->priv->show_user_other) { if (widget->priv->has_user_other) { remove_user_other (widget); } return; } number_of_users = gdm_chooser_widget_get_number_of_items (GDM_CHOOSER_WIDGET (widget)); /* we hide the Other user if it's the last one, and we show it * if there's another user */ if (number_of_users == 1 && widget->priv->has_user_other) { remove_user_other (widget); } if (number_of_users >= 1 && !widget->priv->has_user_other) { add_user_other (widget); } } static void add_user_other (GdmUserChooserWidget *widget) { widget->priv->has_user_other = TRUE; gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget), GDM_USER_CHOOSER_USER_OTHER, NULL, /* translators: This option prompts * the user to type in a username * manually instead of choosing from * a list. */ C_("user", "Other..."), _("Choose a different account"), 0, FALSE, TRUE); } static void add_user_guest (GdmUserChooserWidget *widget) { gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget), GDM_USER_CHOOSER_USER_GUEST, widget->priv->stock_person_pixbuf, _("Guest"), _("Login as a temporary guest"), 0, FALSE, TRUE); update_other_user_visibility (widget); } static void add_user_auto (GdmUserChooserWidget *widget) { gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget), GDM_USER_CHOOSER_USER_AUTO, NULL, _("Automatic Login"), _("Automatically login to the system after selecting options"), 0, FALSE, TRUE); update_other_user_visibility (widget); } static void remove_user_other (GdmUserChooserWidget *widget) { widget->priv->has_user_other = FALSE; gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget), GDM_USER_CHOOSER_USER_OTHER); } static void remove_user_guest (GdmUserChooserWidget *widget) { gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget), GDM_USER_CHOOSER_USER_GUEST); update_other_user_visibility (widget); } static void remove_user_auto (GdmUserChooserWidget *widget) { gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget), GDM_USER_CHOOSER_USER_AUTO); update_other_user_visibility (widget); } void gdm_user_chooser_widget_set_show_user_other (GdmUserChooserWidget *widget, gboolean show_user) { g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget)); if (widget->priv->show_user_other != show_user) { widget->priv->show_user_other = show_user; update_other_user_visibility (widget); } } void gdm_user_chooser_widget_set_show_user_guest (GdmUserChooserWidget *widget, gboolean show_user) { g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget)); if (widget->priv->show_user_guest != show_user) { widget->priv->show_user_guest = show_user; if (show_user) { add_user_guest (widget); } else { remove_user_guest (widget); } } } void gdm_user_chooser_widget_set_show_user_auto (GdmUserChooserWidget *widget, gboolean show_user) { g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget)); if (widget->priv->show_user_auto != show_user) { widget->priv->show_user_auto = show_user; if (show_user) { add_user_auto (widget); } else { remove_user_auto (widget); } } } char * gdm_user_chooser_widget_get_chosen_user_name (GdmUserChooserWidget *widget) { g_return_val_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget), NULL); return gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (widget)); } void gdm_user_chooser_widget_set_chosen_user_name (GdmUserChooserWidget *widget, const char *name) { g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget)); gdm_chooser_widget_set_active_item (GDM_CHOOSER_WIDGET (widget), name); } void gdm_user_chooser_widget_set_show_only_chosen (GdmUserChooserWidget *widget, gboolean show_only) { g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget)); gdm_chooser_widget_set_hide_inactive_items (GDM_CHOOSER_WIDGET (widget), show_only); } static void gdm_user_chooser_widget_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GdmUserChooserWidget *self; self = GDM_USER_CHOOSER_WIDGET (object); switch (prop_id) { case PROP_SHOW_USER_AUTO: gdm_user_chooser_widget_set_show_user_auto (self, g_value_get_boolean (value)); break; case PROP_SHOW_USER_GUEST: gdm_user_chooser_widget_set_show_user_guest (self, g_value_get_boolean (value)); break; case PROP_SHOW_USER_OTHER: gdm_user_chooser_widget_set_show_user_other (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gdm_user_chooser_widget_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GdmUserChooserWidget *self; self = GDM_USER_CHOOSER_WIDGET (object); switch (prop_id) { case PROP_SHOW_USER_AUTO: g_value_set_boolean (value, self->priv->show_user_auto); break; case PROP_SHOW_USER_GUEST: g_value_set_boolean (value, self->priv->show_user_guest); break; case PROP_SHOW_USER_OTHER: g_value_set_boolean (value, self->priv->show_user_other); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean is_user_list_disabled (GdmUserChooserWidget *widget) { GConfClient *client; GError *error; gboolean result; client = gconf_client_get_default (); error = NULL; result = gconf_client_get_bool (client, KEY_DISABLE_USER_LIST, &error); if (error != NULL) { g_debug ("GdmUserChooserWidget: unable to get disable-user-list configuration: %s", error->message); g_error_free (error); } g_object_unref (client); return result; } static void add_user (GdmUserChooserWidget *widget, GdmUser *user) { GdkPixbuf *pixbuf; char *tooltip; gboolean is_logged_in; int size; if (!widget->priv->show_normal_users) { return; } size = get_icon_height_for_widget (GTK_WIDGET (widget)); pixbuf = gdm_user_render_icon (user, size); if (pixbuf == NULL && widget->priv->stock_person_pixbuf != NULL) { pixbuf = g_object_ref (widget->priv->stock_person_pixbuf); } tooltip = g_strdup_printf (_("Log in as %s"), gdm_user_get_user_name (user)); is_logged_in = gdm_user_get_num_sessions (user) > 0; g_debug ("GdmUserChooserWidget: User added name:%s logged-in:%d pixbuf:%p", gdm_user_get_user_name (user), is_logged_in, pixbuf); gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (widget), gdm_user_get_user_name (user), pixbuf, gdm_user_get_display_name (user), tooltip, gdm_user_get_login_frequency (user), is_logged_in, FALSE); g_free (tooltip); if (pixbuf != NULL) { g_object_unref (pixbuf); } update_other_user_visibility (widget); } static void on_user_added (GdmUserManager *manager, GdmUser *user, GdmUserChooserWidget *widget) { /* wait for all users to be loaded */ if (! widget->priv->loaded) { return; } add_user (widget, user); } static void on_user_removed (GdmUserManager *manager, GdmUser *user, GdmUserChooserWidget *widget) { const char *user_name; g_debug ("GdmUserChooserWidget: User removed: %s", gdm_user_get_user_name (user)); /* wait for all users to be loaded */ if (! widget->priv->loaded) { return; } user_name = gdm_user_get_user_name (user); gdm_chooser_widget_remove_item (GDM_CHOOSER_WIDGET (widget), user_name); update_other_user_visibility (widget); } static void on_user_is_logged_in_changed (GdmUserManager *manager, GdmUser *user, GdmUserChooserWidget *widget) { const char *user_name; gboolean is_logged_in; g_debug ("GdmUserChooserWidget: User logged in changed: %s", gdm_user_get_user_name (user)); user_name = gdm_user_get_user_name (user); is_logged_in = gdm_user_get_num_sessions (user) > 0; gdm_chooser_widget_set_item_in_use (GDM_CHOOSER_WIDGET (widget), user_name, is_logged_in); } static void on_user_login_frequency_changed (GdmUserManager *manager, GdmUser *user, GdmUserChooserWidget *widget) { const char *user_name; gulong freq; g_debug ("GdmUserChooserWidget: User login frequency changed: %s", gdm_user_get_user_name (user)); user_name = gdm_user_get_user_name (user); freq = gdm_user_get_login_frequency (user); gdm_chooser_widget_set_item_priority (GDM_CHOOSER_WIDGET (widget), user_name, freq); } static void on_users_loaded (GdmUserManager *manager, GdmUserChooserWidget *widget) { GSList *users; gboolean list_visible; g_debug ("GdmUserChooserWidget: Users loaded"); users = gdm_user_manager_list_users (manager); while (users != NULL) { add_user (widget, users->data); users = g_slist_delete_link (users, users); } g_object_get (G_OBJECT (widget), "list-visible", &list_visible, NULL); if (list_visible) { gtk_widget_grab_focus (GTK_WIDGET (widget)); } widget->priv->loaded = TRUE; gdm_chooser_widget_loaded (GDM_CHOOSER_WIDGET (widget)); } static gboolean load_users (GdmUserChooserWidget *widget) { if (widget->priv->show_normal_users) { widget->priv->manager = gdm_user_manager_ref_default (); g_signal_connect (widget->priv->manager, "user-added", G_CALLBACK (on_user_added), widget); g_signal_connect (widget->priv->manager, "user-removed", G_CALLBACK (on_user_removed), widget); g_signal_connect (widget->priv->manager, "users-loaded", G_CALLBACK (on_users_loaded), widget); g_signal_connect (widget->priv->manager, "user-is-logged-in-changed", G_CALLBACK (on_user_is_logged_in_changed), widget); g_signal_connect (widget->priv->manager, "user-login-frequency-changed", G_CALLBACK (on_user_login_frequency_changed), widget); } else { gdm_chooser_widget_loaded (GDM_CHOOSER_WIDGET (widget)); } widget->priv->load_idle_id = 0; return FALSE; } static GObject * gdm_user_chooser_widget_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GdmUserChooserWidget *widget; widget = GDM_USER_CHOOSER_WIDGET (G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->constructor (type, n_construct_properties, construct_properties)); widget->priv->has_user_other = FALSE; widget->priv->show_normal_users = !is_user_list_disabled (widget); widget->priv->load_idle_id = g_idle_add ((GSourceFunc)load_users, widget); return G_OBJECT (widget); } static void gdm_user_chooser_widget_dispose (GObject *object) { GdmUserChooserWidget *widget; widget = GDM_USER_CHOOSER_WIDGET (object); G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->dispose (object); if (widget->priv->load_idle_id > 0) { g_source_remove (widget->priv->load_idle_id); widget->priv->load_idle_id = 0; } if (widget->priv->logged_in_pixbuf != NULL) { g_object_unref (widget->priv->logged_in_pixbuf); widget->priv->logged_in_pixbuf = NULL; } if (widget->priv->stock_person_pixbuf != NULL) { g_object_unref (widget->priv->stock_person_pixbuf); widget->priv->stock_person_pixbuf = NULL; } } static void gdm_user_chooser_widget_class_init (GdmUserChooserWidgetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = gdm_user_chooser_widget_get_property; object_class->set_property = gdm_user_chooser_widget_set_property; object_class->constructor = gdm_user_chooser_widget_constructor; object_class->dispose = gdm_user_chooser_widget_dispose; object_class->finalize = gdm_user_chooser_widget_finalize; g_object_class_install_property (object_class, PROP_SHOW_USER_AUTO, g_param_spec_boolean ("show-user-auto", "show user auto", "show user auto", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_SHOW_USER_GUEST, g_param_spec_boolean ("show-user-guest", "show user guest", "show user guest", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_SHOW_USER_OTHER, g_param_spec_boolean ("show-user-other", "show user other", "show user other", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_type_class_add_private (klass, sizeof (GdmUserChooserWidgetPrivate)); } static GdkPixbuf * get_stock_person_pixbuf (GdmUserChooserWidget *widget) { GdkPixbuf *pixbuf; int size; size = get_icon_height_for_widget (GTK_WIDGET (widget)); pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme, DEFAULT_USER_ICON, size, 0, NULL); return pixbuf; } static GdkPixbuf * get_logged_in_pixbuf (GdmUserChooserWidget *widget) { GdkPixbuf *pixbuf; int size; size = get_icon_height_for_widget (GTK_WIDGET (widget)); pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme, "emblem-default", size / 3, 0, NULL); return pixbuf; } typedef struct { GdkPixbuf *old_icon; GdkPixbuf *new_icon; } IconUpdateData; static gboolean update_icons (GdmChooserWidget *widget, const char *id, GdkPixbuf **image, char **name, char **comment, gulong *priority, gboolean *is_in_use, gboolean *is_separate, IconUpdateData *data) { if (data->old_icon == *image) { *image = data->new_icon; return TRUE; } return FALSE; } static void load_icons (GdmUserChooserWidget *widget) { GdkPixbuf *old_pixbuf; IconUpdateData data; if (widget->priv->logged_in_pixbuf != NULL) { g_object_unref (widget->priv->logged_in_pixbuf); } widget->priv->logged_in_pixbuf = get_logged_in_pixbuf (widget); old_pixbuf = widget->priv->stock_person_pixbuf; widget->priv->stock_person_pixbuf = get_stock_person_pixbuf (widget); /* update the icons in the model */ data.old_icon = old_pixbuf; data.new_icon = widget->priv->stock_person_pixbuf; gdm_chooser_widget_update_foreach_item (GDM_CHOOSER_WIDGET (widget), (GdmChooserUpdateForeachFunc)update_icons, &data); if (old_pixbuf != NULL) { g_object_unref (old_pixbuf); } } static void on_icon_theme_changed (GtkIconTheme *icon_theme, GdmUserChooserWidget *widget) { g_debug ("GdmUserChooserWidget: icon theme changed"); load_icons (widget); } static void setup_icons (GdmUserChooserWidget *widget) { if (gtk_widget_has_screen (GTK_WIDGET (widget))) { widget->priv->icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (widget))); } else { widget->priv->icon_theme = gtk_icon_theme_get_default (); } if (widget->priv->icon_theme != NULL) { g_signal_connect (widget->priv->icon_theme, "changed", G_CALLBACK (on_icon_theme_changed), widget); } load_icons (widget); } static void gdm_user_chooser_widget_init (GdmUserChooserWidget *widget) { widget->priv = GDM_USER_CHOOSER_WIDGET_GET_PRIVATE (widget); gdm_chooser_widget_set_separator_position (GDM_CHOOSER_WIDGET (widget), GDM_CHOOSER_WIDGET_POSITION_BOTTOM); gdm_chooser_widget_set_in_use_message (GDM_CHOOSER_WIDGET (widget), _("Currently logged in")); setup_icons (widget); } static void gdm_user_chooser_widget_finalize (GObject *object) { GdmUserChooserWidget *widget; g_return_if_fail (object != NULL); g_return_if_fail (GDM_IS_USER_CHOOSER_WIDGET (object)); widget = GDM_USER_CHOOSER_WIDGET (object); g_return_if_fail (widget->priv != NULL); G_OBJECT_CLASS (gdm_user_chooser_widget_parent_class)->finalize (object); } GtkWidget * gdm_user_chooser_widget_new (void) { GObject *object; object = g_object_new (GDM_TYPE_USER_CHOOSER_WIDGET, NULL); return GTK_WIDGET (object); }