Logo Search packages:      
Sourcecode: fast-user-switch-applet version File versions

applet.c

/* 
 * Fast User Switch Applet: applet.c
 * 
 * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
 *
 * 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 <glib/gi18n.h>

#include <string.h>

#include <gdk/gdkkeysyms.h>

#include <gtk/gtkversion.h>
#include <gtk/gtkaboutdialog.h>
#include <gtk/gtkhbox.h>
#include <gtk/gtkiconfactory.h>
#include <gtk/gtkicontheme.h>
#include <gtk/gtkimage.h>
#include <gtk/gtkimagemenuitem.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkmain.h>
#include <gtk/gtkmenubar.h>
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkradiobutton.h>
#include <gtk/gtkseparatormenuitem.h>
#include <gtk/gtkstock.h>
#include <gtk/gtkvbox.h>

#include <gconf/gconf.h>
#include <gconf/gconf-client.h>

#include <libgnomevfs/gnome-vfs-utils.h>

#include <glade/glade-xml.h>

#include <bonobo/bonobo-main.h>
#include <bonobo/bonobo-ui-util.h>

#include <libgnome/gnome-init.h>
#include <libgnomeui/gnome-help.h>

#include <panel-applet.h>
#include <panel-applet-gconf.h>

#include "fusa-manager.h"
#include "fusa-user-menu-item.h"
#include "fusa-utils.h"


/* **************** *
 *  Private Macros  *
 * **************** */

#define DISPLAY_KEY     "display_style"
#define USE_XNEST_KEY   "use_xnest"
#define LOCK_KEY  "lock_screen_after_switch"

#define GLOBAL_DIR      "/apps/fast-user-switch-applet"
#define SHOW_WINDOW_KEY "/apps/fast-user-switch-applet/show_window_item"
#define SHOW_SCREEN_KEY "/apps/fast-user-switch-applet/show_screen_item"
#define ACTIVE_ONLY_KEY "/apps/fast-user-switch-applet/show_active_users_only"


/* ******************** *
 *  Private Structures  *
 * ******************** */

typedef struct _FusaAppletData
{
  PanelApplet *applet;

  GConfClient *client;
  FusaManager *manager;

  GtkWidget *prefs;
  
  GtkWidget *menubar;
  GtkWidget *imglabel;
  GtkWidget *menu;
  GtkWidget *separator_item;
  GtkWidget *login_screen_item;
  GtkWidget *login_window_item;
  GSList *items;

  guint client_notify_applet_id;
  guint client_notify_global_id;
  guint user_notify_id;
  GQuark user_menu_item_quark;
  gint8 pixel_size;
  GtkIconSize icon_size;
}
FusaAppletData;

typedef struct _SelectorResponseData
{
  FusaAppletData *adata;
  GtkRadioButton *radio;
}
SelectorResponseData;


/* ********************* *
 *  Function Prototypes  *
 * ********************* */

/* PanelApplet Callbacks */
static gboolean applet_fill_cb              (PanelApplet               *applet,
                                   const gchar               *iid,
                                   gpointer                   data);
static void     applet_style_set_cb         (GtkWidget                 *widget,
                                   GtkStyle                  *old_style,
                                   gpointer                   data);
static void     applet_change_background_cb (PanelApplet               *applet,
                                   PanelAppletBackgroundType  type,
                                   GdkColor                  *color,
                                   GdkPixmap                 *pixmap,
                                   gpointer                   data);
static void     applet_size_allocate_cb     (GtkWidget                 *widget,
                                   GtkAllocation             *allocation,
                                   gpointer                   data);
static gboolean applet_key_press_event_cb   (GtkWidget                 *widget,
                                   GdkEventKey               *event,
                                   gpointer                   data);

/* Panel Menu Callbacks */
static void about_me_cb (BonoboUIComponent *ui_container,
                   gpointer           data,
                   const gchar       *cname);
static void admin_cb    (BonoboUIComponent *ui_container,
                   gpointer           data,
                   const gchar       *cname);
static void setup_cb    (BonoboUIComponent *ui_container,
                   gpointer           data,
                   const gchar       *cname);
static void prefs_cb    (BonoboUIComponent *ui_container,
                   gpointer           data,
                   const gchar       *cname);
static void help_cb     (BonoboUIComponent *ui_container,
                   gpointer           data,
                   const gchar       *cname);
static void about_cb    (BonoboUIComponent *ui_container,
                   gpointer           data,
                   const gchar       *cname);

/* FusaManager Callbacks */
static void manager_user_added_cb       (FusaManager  *manager,
                               FusaUser     *user,
                               gpointer      data);
static void manager_new_xnest_cb        (FusaManager  *manager,
                               FusaDisplay  *display,
                               const GError *error,
                               gpointer      data);
static void manager_new_console_cb      (FusaManager  *manager,
                               FusaDisplay  *display,
                               const GError *error,
                               gpointer      data);
static void manager_activate_display_cb (FusaManager  *manager,
                               FusaDisplay  *display,
                               const GError *error,
                               gpointer      data);
static void user_displays_changed_cb    (FusaUser *user,
                               gpointer  data);

/* GConfClient Callbacks */
static void client_notify_applet_func (GConfClient *client,
                               guint        cnxn_id,
                               GConfEntry  *entry,
                               gpointer     data);
static void client_notify_global_func (GConfClient *client,
                               guint        cnxn_id,
                               GConfEntry  *entry,
                               gpointer     data);

/* Child Widget Callbacks */
static void     prefs_radio_toggled_cb        (GtkToggleButton *button,
                                     gpointer         data);
static void     prefs_xnest_check_toggled_cb  (GtkToggleButton *button,
                                     gpointer         data);
static void     prefs_lock_check_toggled_cb   (GtkToggleButton *button,
                                     gpointer         data);
static gboolean menubar_button_press_event_cb (GtkWidget       *menubar,
                                     GdkEventButton  *event,
                                     gpointer         data);
static gboolean menubar_expose_event_cb       (GtkWidget       *menubar,
                                     GdkEventExpose  *event,
                                     gpointer         data);
static void     menu_style_set_cb             (GtkWidget       *menu,
                                     GtkStyle        *old_style,
                                     gpointer         data);
static gboolean menu_expose_cb                (GtkWidget       *menu,
                                               gpointer         data);
static void     menuitem_destroy_cb           (GtkWidget       *item,
                                     gpointer         data);
static void     menuitem_style_set_cb         (GtkWidget       *menuitem,
                                     GtkStyle        *old_style,
                                     gpointer         data);
static void     user_item_activate_cb         (GtkWidget       *menuitem,
                                     gpointer         data);
static void     login_screen_activate_cb      (GtkMenuItem     *item,
                                     gpointer         data);
static void     login_window_activate_cb      (GtkMenuItem     *item,
                                     gpointer         data);
static void     user_notify_display_name_cb   (GObject         *object,
                                     GParamSpec      *pspec,
                                     gpointer         data);

/* Utility Functions */
static void      reset_login_screen_item      (GtkWidget      *widget,
                                     gboolean        use_xnest_value,
                                     const gchar    *pref_value);
static void      reset_login_window_item      (GtkWidget      *widget,
                                     gboolean        use_xnest_value,
                                     const gchar    *pref_value);
static void      show_display_selector_dialog (FusaAppletData *adata,
                                     GSList         *displays);
static void      show_error_dialog            (FusaAppletData *adata,
                                     const GError   *error);
static gint      sort_menu_comparedatafunc    (gconstpointer   a,
                                     gconstpointer   b,
                                     gpointer        data);
static void      sort_menu                    (FusaAppletData *adata);
static GladeXML *get_glade_xml                (const gchar    *root);
static void      run_switch                   (FusaAppletData *adata,
                                     FusaDisplay    *display,
                                     gboolean        use_xnest);
static void      maybe_lock_screen            (FusaAppletData *adata,
                                     FusaDisplay    *display);
static gboolean  system_can_do_xnest          (void);
static void      make_label_bold              (GtkLabel       *label);
static void      make_label_larger            (GtkLabel       *label);
static void      make_label_small_italic      (GtkLabel       *label);
static void      fusa_applet_data_free        (FusaAppletData *adata);


/* ****************** *
 *  Global Variables  *
 * ****************** */

static GtkTooltips *tooltips = NULL;
static GnomeProgram *program = NULL;


/* *************************** *
 *  Applet main() Declaration  *
 * *************************** */

PANEL_APPLET_BONOBO_FACTORY ("OAFIID:GNOME_FastUserSwitchApplet_Factory",
                       PANEL_TYPE_APPLET,
                       "fast-user-switch-applet", "0",
                       applet_fill_cb, NULL);


/* *********************** *
 *  PanelApplet Callbacks  *
 * *********************** */

static gboolean
applet_fill_cb (PanelApplet *applet,
              const gchar *iid,
              gpointer     data)
{
  static const BonoboUIVerb menu_verbs[] = {
    BONOBO_UI_VERB ("FusaAboutMe", about_me_cb),
    BONOBO_UI_VERB ("FusaUsersGroupsAdmin", admin_cb),
    BONOBO_UI_VERB ("FusaGdmSetup", setup_cb),
    BONOBO_UI_VERB ("FusaPreferences", prefs_cb),
    BONOBO_UI_VERB ("FusaHelp", help_cb),
    BONOBO_UI_VERB ("FusaAbout", about_cb),
    BONOBO_UI_VERB_END
  };
  static gboolean first_time = FALSE;

  FusaAppletData *adata;
  GtkWidget *menuitem, *hbox;
  GSList *users;
  gchar *tmp, *key, *value;
  gboolean use_xnest, active_only;
  BonoboUIComponent *popup_component;

  if (strcmp (iid, "OAFIID:GNOME_FastUserSwitchApplet") != 0)
    return FALSE;

  /* Global FusaManager */
  if (!first_time)
    {
      gint argc = 1;
      gchar *argv[2] = { "fast-user-switch-applet", NULL};

      first_time = TRUE;

      program = gnome_program_init ("fast-user-switch-applet", VERSION,
                            LIBGNOME_MODULE, argc, argv,
                            GNOME_PROGRAM_STANDARD_PROPERTIES,
                            NULL);

      /* Do this here so it's only done once. */
      gtk_rc_parse_string ("style \"fast-user-switch-menubar-style\"\n"
                     "{\n"
                       "GtkMenuBar::shadow-type = none\n"
                       "GtkMenuBar::internal-padding = 0\n"
                     "}\n"
                     "style \"fast-user-switch-applet-style\"\n"
                     "{\n"
                       "GtkWidget::focus-line-width = 0\n"
                       "GtkWidget::focus-padding = 0\n"
                     "}\n"
                     "widget \"*.fast-user-switch-menubar\" style \"fast-user-switch-menubar-style\"\n"
                     "widget \"*.fast-user-switch-applet\" style \"fast-user-switch-applet-style\"\n");
      gtk_window_set_default_icon_name ("stock_people");
    }

  adata = g_new0 (FusaAppletData, 1);
  adata->applet = applet;

  adata->client = gconf_client_get_default ();
  adata->manager = fusa_manager_ref_default ();

  tmp = g_strdup_printf ("applet-user-menu-item-%p", adata);
  adata->user_menu_item_quark = g_quark_from_string (tmp);
  g_free (tmp);

  if (!tooltips)
    {
      tooltips = gtk_tooltips_new ();
      g_object_ref (tooltips);
      gtk_object_sink (GTK_OBJECT (tooltips));
    }
  else
    g_object_ref (tooltips);

  gtk_tooltips_set_tip (tooltips, GTK_WIDGET (applet), _("User Switcher"), NULL);
  gtk_container_set_border_width (GTK_CONTAINER (applet), 0);
  gtk_widget_set_name (GTK_WIDGET (applet), "fast-user-switch-applet");
  panel_applet_set_flags (applet, PANEL_APPLET_EXPAND_MINOR);
  panel_applet_setup_menu_from_file (applet, NULL,
                             DATADIR "/gnome-2.0/ui/GNOME_FastUserSwitchApplet.xml",
                             NULL, menu_verbs, adata);

  popup_component = panel_applet_get_popup_component (applet);
  if (panel_applet_get_locked_down (applet))
    bonobo_ui_component_set_prop (popup_component, "/commands/FusaPreferences",
                          "hidden", "0", NULL);

  /* Hide the admin context menu items if locked down or no cmd-line */
  if (gconf_client_get_bool (adata->client,
                       "/desktop/gnome/lockdown/inhibit_command_line",
                       NULL) ||
      panel_applet_get_locked_down (applet))
    {
      bonobo_ui_component_set_prop (popup_component,
                            "/popups/button3/FusaSeparator",
                            "hidden", "1", NULL);
      bonobo_ui_component_set_prop (popup_component,
                            "/commands/FusaUsersGroupsAdmin",
                            "hidden", "1", NULL);
      bonobo_ui_component_set_prop (popup_component, "/commands/FusaGdmSetup",
                            "hidden", "1", NULL);
    }
  /* And hide them anyways if they aren't compiled in. */
  else
    {
#ifndef USERS_ADMIN
#  ifdef GDM_SETUP
      bonobo_ui_component_set_prop (popup_component,
                            "/popups/button3/FusaSeparator",
                            "hidden", "1",
                            NULL);
#  endif /* !GDM_SETUP */
      bonobo_ui_component_set_prop (popup_component,
                            "/commands/FusaUsersGroupsAdmin",
                            "hidden", "1",
                            NULL);
#endif /* !USERS_ADMIN */

#ifndef GDM_SETUP
      bonobo_ui_component_set_prop (popup_component, "/commands/FusaGdmSetup",
                            "hidden", "1",
                            NULL);
#endif /* !GDM_SETUP */
    }

  /* Hide the gdmphotosetup item if it can't be found in the path. */
  tmp = g_find_program_in_path ("gnome-about-me");
  if (!tmp)
    bonobo_ui_component_set_prop (popup_component,
                          "/commands/FusaAboutMe",
                          "hidden", "1",
                          NULL);
  else
    g_free (tmp);

  panel_applet_add_preferences (applet,
                          "/schemas/apps/fast-user-switch-applet/per-applet",
                        NULL);
  g_signal_connect (adata->applet, "style-set",
                G_CALLBACK (applet_style_set_cb), adata);
  g_signal_connect (applet, "change-background",
                G_CALLBACK (applet_change_background_cb), adata);
  g_signal_connect (applet, "size-allocate",
                G_CALLBACK (applet_size_allocate_cb), adata);
  g_signal_connect (applet, "key-press-event",
                G_CALLBACK (applet_key_press_event_cb), adata);
  g_signal_connect_after (applet, "focus-in-event",
                    G_CALLBACK (gtk_widget_queue_draw), NULL);
  g_signal_connect_after (applet, "focus-out-event",
                    G_CALLBACK (gtk_widget_queue_draw), NULL);
  g_object_set_data_full (G_OBJECT (applet), "fusa-applet-data", adata,
                    (GDestroyNotify) fusa_applet_data_free);

  adata->menubar = gtk_menu_bar_new ();
  gtk_widget_set_name (adata->menubar, "fast-user-switch-menubar");
  GTK_WIDGET_SET_FLAGS (adata->menubar,
                  GTK_WIDGET_FLAGS (adata->menubar) | GTK_CAN_FOCUS);
  g_signal_connect (adata->menubar, "button-press-event",
                G_CALLBACK (menubar_button_press_event_cb), adata);
  g_signal_connect_after (adata->menubar, "expose-event",
                    G_CALLBACK (menubar_expose_event_cb), adata);
  gtk_container_add (GTK_CONTAINER (applet), adata->menubar);
  gtk_widget_show (adata->menubar);

  menuitem = gtk_menu_item_new ();
  gtk_menu_shell_append (GTK_MENU_SHELL (adata->menubar), menuitem);
  gtk_widget_show (menuitem);

  hbox = gtk_hbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (menuitem), hbox);
  gtk_widget_show (hbox);
  
  value = panel_applet_gconf_get_string (applet, DISPLAY_KEY, NULL);
  if (value)
    {
      if (strcmp (value, "username") == 0)
      {
        FusaUser *user;

        user = fusa_manager_get_user_by_uid (adata->manager, getuid ());
        adata->imglabel = gtk_label_new (fusa_user_get_display_name (user));
        adata->user_notify_id =
          g_signal_connect (user, "notify::display-name",
                        G_CALLBACK (user_notify_display_name_cb),
                        adata->imglabel);
        gtk_box_pack_start (GTK_BOX (hbox), adata->imglabel, TRUE, TRUE, 0);
        gtk_widget_show (adata->imglabel);
      }
      else if (strcmp (value, "icon") == 0)
      {
        adata->imglabel = gtk_image_new_from_icon_name ("stock_people",
                                            GTK_ICON_SIZE_MENU);
        gtk_container_add (GTK_CONTAINER (hbox), adata->imglabel);
        gtk_widget_show (adata->imglabel);
      }
      else
      {
        adata->imglabel = gtk_label_new (_("Users"));
        gtk_box_pack_start (GTK_BOX (hbox), adata->imglabel, TRUE, TRUE, 0);
        gtk_widget_show (adata->imglabel);
      }
    }
  else
    {
      adata->imglabel = gtk_label_new (_("Users"));
      gtk_box_pack_start (GTK_BOX (hbox), adata->imglabel, TRUE, TRUE, 0);
      gtk_widget_show (adata->imglabel);
    }
  g_free (value);

  adata->menu = gtk_menu_new ();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), adata->menu);
  g_signal_connect (adata->menu, "style-set",
                G_CALLBACK (menu_style_set_cb), adata);
  g_signal_connect (adata->menu, "show",
                G_CALLBACK (menu_expose_cb), adata);
  gtk_widget_show (adata->menu);

  active_only = gconf_client_get_bool (adata->client, ACTIVE_ONLY_KEY, NULL);

  /* This next part populates the list with all the users we currently know
   * about. For almost all cases, this is the empty list, because we're
   * asynchronous, and the data hasn't come back from the callback saying who
   * the users are yet. However, if someone has two FUSAs on their toolbars (why,
   * I have no freaking idea, but bear with me here), the menu of the second
   * one to be initialised needs to be filled in from the start rather than
   * depending on getting data from the callback like the first one.
   */
  users = fusa_manager_list_users (adata->manager);
  while (users)
    {
      menuitem = fusa_user_menu_item_new (users->data);
      g_object_set_qdata (users->data, adata->user_menu_item_quark, menuitem);
      g_signal_connect (menuitem, "style-set",
                  G_CALLBACK (menuitem_style_set_cb), adata);
      g_signal_connect (menuitem, "destroy",
                  G_CALLBACK (menuitem_destroy_cb), adata);
      g_signal_connect (menuitem, "activate",
                  G_CALLBACK (user_item_activate_cb), adata);
      gtk_menu_shell_append (GTK_MENU_SHELL (adata->menu), menuitem);
      adata->items = g_slist_prepend (adata->items, menuitem);

      if (active_only)
      {
        GSList *displays;

        displays = fusa_user_get_displays (users->data);
        if (displays)
          {
            g_slist_free (displays);
            gtk_widget_show (menuitem);
          }
      }
      else
      gtk_widget_show (menuitem);
      g_signal_connect (users->data, "displays-changed",
                  G_CALLBACK (user_displays_changed_cb), adata);

      users = g_slist_delete_link (users, users);
    }

  g_signal_connect (adata->manager, "user-added",
                G_CALLBACK (manager_user_added_cb), adata);

  adata->separator_item = gtk_separator_menu_item_new ();
  gtk_menu_shell_append (GTK_MENU_SHELL (adata->menu), adata->separator_item);
  adata->items = g_slist_prepend (adata->items, adata->separator_item);

  adata->login_screen_item = gtk_image_menu_item_new_with_label (_("Other"));
  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (adata->login_screen_item),
                         gtk_image_new ());
  gtk_menu_shell_append (GTK_MENU_SHELL (adata->menu),
                    adata->login_screen_item);
  g_signal_connect (adata->login_screen_item, "style-set",
                G_CALLBACK (menuitem_style_set_cb), adata);
  g_signal_connect (adata->login_screen_item, "destroy",
                G_CALLBACK (menuitem_destroy_cb), adata);
  g_signal_connect (adata->login_screen_item, "activate",
                G_CALLBACK (login_screen_activate_cb), adata);
  adata->items = g_slist_prepend (adata->items, adata->login_screen_item);

  adata->login_window_item = gtk_image_menu_item_new_with_label (_("Login Window"));
  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (adata->login_window_item),
                         gtk_image_new ());
  gtk_menu_shell_append (GTK_MENU_SHELL (adata->menu),
                    adata->login_window_item);
  g_signal_connect (adata->login_window_item, "style-set",
                G_CALLBACK (menuitem_style_set_cb), adata);
  g_signal_connect (adata->login_window_item, "destroy",
                G_CALLBACK (menuitem_destroy_cb), adata);
  g_signal_connect (adata->login_window_item, "activate",
                G_CALLBACK (login_window_activate_cb), adata);
  adata->items = g_slist_prepend (adata->items, adata->login_window_item);

  use_xnest = panel_applet_gconf_get_bool (applet, USE_XNEST_KEY, NULL);
  value = gconf_client_get_string (adata->client, SHOW_SCREEN_KEY, NULL);
  reset_login_screen_item (adata->login_screen_item, use_xnest, value);
  g_free (value);

  value = gconf_client_get_string (adata->client, SHOW_WINDOW_KEY, NULL);
  reset_login_window_item (adata->login_window_item, use_xnest, value);
  g_free (value);

  if (GTK_WIDGET_VISIBLE (adata->login_screen_item) ||
      GTK_WIDGET_VISIBLE (adata->login_window_item))
    gtk_widget_show (adata->separator_item);
  else
    gtk_widget_hide (adata->separator_item);

  key = panel_applet_gconf_get_full_key (applet, DISPLAY_KEY);
  if (key)
    {
      gchar *path;
    
      path = g_path_get_dirname (key);
      g_free (key);
      adata->client_notify_applet_id =
      gconf_client_notify_add (adata->client, path, client_notify_applet_func,
                         adata, NULL, NULL);
      g_free (path);
    }

  gconf_client_add_dir (adata->client, GLOBAL_DIR,
                  GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
  adata->client_notify_global_id =
    gconf_client_notify_add (adata->client, GLOBAL_DIR,
                       client_notify_global_func, adata, NULL, NULL);

  adata->items = g_slist_sort_with_data (adata->items,
                               sort_menu_comparedatafunc, adata);

  gtk_widget_show (GTK_WIDGET (applet));

  return TRUE;
}

static void
set_menuitem_icon (BonoboUIComponent *component,
               const gchar       *item_path,
               GtkIconTheme      *theme,
               const gchar       *icon_name,
               gint               icon_size)
{
  GdkPixbuf *pixbuf;
  gint width, height;

  pixbuf = gtk_icon_theme_load_icon (theme, icon_name, icon_size, 0, NULL);
  if (!pixbuf)
    return;

  width = gdk_pixbuf_get_width (pixbuf);
  height = gdk_pixbuf_get_height (pixbuf);
  if (width > icon_size + 4 || height > icon_size + 4)
    {
      GdkPixbuf *tmp;
      if (height > width)
        {
        width *= (gdouble) icon_size / (gdouble) height;
        height = icon_size;
      }
      else
        {
        height *= (gdouble) icon_size / (gdouble) width;
        width = icon_size;
      }
      tmp = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
      g_object_unref (pixbuf);
      pixbuf = tmp;
    }

  bonobo_ui_util_set_pixbuf (component, item_path, pixbuf, NULL);
  g_object_unref (pixbuf);
}

static void
applet_style_set_cb (GtkWidget *widget,
                 GtkStyle  *old_style,
                 gpointer   data)
{
  BonoboUIComponent *component;
  GdkScreen *screen;
  GtkIconTheme *theme;
  gint width, height, icon_size;

  if (gtk_widget_has_screen (widget))
    screen = gtk_widget_get_screen (widget);
  else
    screen = gdk_screen_get_default ();

  if (gtk_icon_size_lookup_for_settings (gtk_settings_get_for_screen (screen),
                               GTK_ICON_SIZE_MENU, &width, &height))
    icon_size = MAX (width, height);
  else
    icon_size = 16;

  theme = gtk_icon_theme_get_for_screen (screen);
  component = panel_applet_get_popup_component (PANEL_APPLET (widget));

  set_menuitem_icon (component, "/commands/FusaAboutMe",
                 theme, "user-info", icon_size);
  set_menuitem_icon (component, "/commands/FusaUsersGroupsAdmin",
                 theme, "stock_people", icon_size);
  set_menuitem_icon (component, "/commands/FusaGdmSetup",
                 theme, "gdm-setup", icon_size);
}

static void
applet_change_background_cb (PanelApplet               *applet,
                       PanelAppletBackgroundType  type,
                       GdkColor                  *color,
                       GdkPixmap                 *pixmap,
                       gpointer                   data)
{
  FusaAppletData *adata;
  GtkRcStyle *rc_style;
  GtkStyle   *style;

  adata = data;

  gtk_widget_set_style (adata->menubar, NULL);
  rc_style = gtk_rc_style_new ();
  gtk_widget_modify_style (GTK_WIDGET (adata->menubar), rc_style);
  gtk_rc_style_unref (rc_style);

  switch (type)
    {
    case PANEL_NO_BACKGROUND:
      break;
    case PANEL_COLOR_BACKGROUND:
      gtk_widget_modify_bg (adata->menubar, GTK_STATE_NORMAL, color);
      break;
    case PANEL_PIXMAP_BACKGROUND:
      style = gtk_style_copy (adata->menubar->style);
      if (style->bg_pixmap[GTK_STATE_NORMAL])
      g_object_unref (style->bg_pixmap[GTK_STATE_NORMAL]);

      style->bg_pixmap[GTK_STATE_NORMAL] = g_object_ref (pixmap);
      gtk_widget_set_style (adata->menubar, style);
      g_object_unref (style);
      break;
    }
}

/*
 * gnome-panel/applets/wncklet/window-menu.c:window_menu_key_press_event()
 * 
 * Copyright (C) 2003 Sun Microsystems, Inc.
 * Copyright (C) 2001 Free Software Foundation, Inc.
 * Copyright (C) 2000 Helix Code, Inc.
 */
static gboolean
applet_key_press_event_cb (GtkWidget   *widget,
                           GdkEventKey *event,
                           gpointer     data)
{
  FusaAppletData *adata;
  GtkMenuShell *menu_shell;

  switch (event->keyval)
    {
    case GDK_KP_Enter:
    case GDK_ISO_Enter:
    case GDK_3270_Enter:
    case GDK_Return:
    case GDK_space:
    case GDK_KP_Space:
      adata = data;
      menu_shell = GTK_MENU_SHELL (adata->menubar);
      /* 
       * We need to call _gtk_menu_shell_activate() here as is done in 
       * window_key_press_handler in gtkmenubar.c which pops up menu
       * when F10 is pressed.
       *
       * As that function is private its code is replicated here.
       */
      if (!menu_shell->active)
      {
        gtk_grab_add (GTK_WIDGET (menu_shell));
        menu_shell->have_grab = TRUE;
        menu_shell->active = TRUE;
      }

      gtk_menu_shell_select_first (menu_shell, FALSE);
      return TRUE;
    default:
      break;
    }
      
  return FALSE;
}

/*
 * gnome-panel/applets/wncklet/window-menu.c:window_menu_size_allocate()
 * 
 * Copyright (C) 2003 Sun Microsystems, Inc.
 * Copyright (C) 2001 Free Software Foundation, Inc.
 * Copyright (C) 2000 Helix Code, Inc.
 */
static void
applet_size_allocate_cb (GtkWidget     *widget,
                   GtkAllocation *allocation,
                   gpointer       data)
{
  FusaAppletData *adata;
  GList *children;
  GtkWidget *top_item;
  PanelAppletOrient orient;
  gint item_border;
  gint pixel_size;
#if GTK_CHECK_VERSION (2,7,0)
  gdouble text_angle = 0.0;
  GtkPackDirection pack_direction = GTK_PACK_DIRECTION_LTR;
#else
  PangoEllipsizeMode ellipsize_mode;
#endif

  adata = data;
  children = gtk_container_get_children (GTK_CONTAINER (adata->menubar));
  top_item = GTK_WIDGET (children->data);
  g_list_free (children);

  orient = panel_applet_get_orient (PANEL_APPLET (widget));

  item_border = (MAX (top_item->style->xthickness,
                  top_item->style->ythickness) * 2);

  switch (orient)
    {
    case PANEL_APPLET_ORIENT_UP:
    case PANEL_APPLET_ORIENT_DOWN:
      gtk_widget_set_size_request (top_item, -1, allocation->height);
      pixel_size = allocation->height - item_border;
#if GTK_CHECK_VERSION (2,7,0)
      pack_direction = GTK_PACK_DIRECTION_RTL;
      text_angle = 0.0;
#else
      ellipsize_mode = PANGO_ELLIPSIZE_NONE;
#endif /* HAVE_GTK28 */
      break;
    case PANEL_APPLET_ORIENT_LEFT:
      gtk_widget_set_size_request (top_item, allocation->width, -1);
      pixel_size = allocation->width - item_border;
#if GTK_CHECK_VERSION (2,7,0)
      pack_direction = GTK_PACK_DIRECTION_TTB;
      text_angle = 270.0;
#else
      ellipsize_mode = PANGO_ELLIPSIZE_END;
#endif /* HAVE_GTK28 */
      break;
    case PANEL_APPLET_ORIENT_RIGHT:
      gtk_widget_set_size_request (top_item, allocation->width, -1);
      pixel_size = allocation->width - item_border;
#if GTK_CHECK_VERSION (2,7,0)
      pack_direction = GTK_PACK_DIRECTION_TTB;
      text_angle = 90.0;
#else
      ellipsize_mode = PANGO_ELLIPSIZE_END;
#endif /* HAVE_GTK28 */
      break;
    default:
      g_assert_not_reached ();
      break;
    }

#if GTK_CHECK_VERSION (2,7,0)
  gtk_menu_bar_set_pack_direction (GTK_MENU_BAR (adata->menubar),
                                   pack_direction);
#endif /* HAVE_GTK28 */

  if (GTK_IS_IMAGE (adata->imglabel))
    {
      gtk_image_set_pixel_size (GTK_IMAGE (adata->imglabel),
                        pixel_size - 4);
    }
  else
    {
#if GTK_CHECK_VERSION (2,7,0)
      gtk_label_set_angle (GTK_LABEL (adata->imglabel), text_angle);
#else
      gtk_label_set_ellipsize (GTK_LABEL (adata->imglabel),
                               ellipsize_mode);
#endif

    }
}


/* ********************** *
 *  Panel Menu Callbacks  *
 * ********************** */

static void
admin_cb (BonoboUIComponent *ui_container,
        gpointer           data,
        const gchar       *cname)
{
#ifdef USERS_ADMIN
  gchar **args;
  GError *err;

  err = NULL;
  if (!g_shell_parse_argv (USERS_ADMIN, NULL, &args, &err))
    {
      g_critical ("Could not parse users and groups management command line `%s': %s",
              USERS_ADMIN, err->message);
      return;
    }

  if (!g_spawn_async (g_get_home_dir (), args, NULL,
                  (G_SPAWN_STDOUT_TO_DEV_NULL |
                   G_SPAWN_STDERR_TO_DEV_NULL |
                   G_SPAWN_SEARCH_PATH),
                  NULL, NULL, NULL, &err))
    {
      g_critical ("Could not run `%s' to manage users and groups: %s",
              USERS_ADMIN, err->message);
      g_error_free (err);
    }
  g_strfreev (args);
#endif /* USERS_ADMIN */
}

static void
setup_cb (BonoboUIComponent *ui_container,
        gpointer           data,
        const gchar       *cname)
{
#ifdef GDM_SETUP
  gchar **args;
  GError *err;

  err = NULL;
  if (!g_shell_parse_argv (GDM_SETUP, NULL, &args, &err))
    {
      g_critical ("Could not parse GDM configuration command line `%s': %s",
              GDM_SETUP, err->message);
      return;
    }

  if (!g_spawn_async (g_get_home_dir (), args, NULL,
                  (G_SPAWN_STDOUT_TO_DEV_NULL |
                   G_SPAWN_STDERR_TO_DEV_NULL |
                   G_SPAWN_SEARCH_PATH),
                  NULL, NULL, NULL, &err))
    {
      g_critical ("Could not run `%s' to configure GDM: %s",
              GDM_SETUP, err->message);
      g_error_free (err);
    }
  g_strfreev (args);
#endif /* GDM_SETUP */
}

static void
about_me_cb (BonoboUIComponent *ui_container,
           gpointer           data,
           const gchar       *cname)
{
  GError *err;

  err = NULL;
  if (!g_spawn_command_line_async ("gnome-about-me", &err))
    {
      g_critical ("Could not run `gnome-about-me' to change photo: %s",
              err->message);
      g_error_free (err);
      bonobo_ui_component_set_prop (ui_container,
                            "/commands/FusaAboutMe",
                            "hidden", "1",
                            NULL);
    }
}

static void
prefs_cb (BonoboUIComponent *ui_container,
        gpointer           data,
        const gchar       *cname)
{
  FusaAppletData *adata;
  GladeXML *xml;
  GtkWidget *warning_box, *label, *username_radio, *text_radio, *icon_radio,
    *check;
  gchar *key, *value;
  gboolean has_lockdown, xnest_available, label_setup_done;

  adata = data;

  if (adata->prefs)
    {
      gtk_window_set_screen (GTK_WINDOW (adata->prefs),
                       gtk_widget_get_screen (GTK_WIDGET (adata->applet)));
      gtk_window_present (GTK_WINDOW (adata->prefs));
      return;
    }

  xml = get_glade_xml ("prefs_dialog");

  if (!xml)
    return;

  adata->prefs = glade_xml_get_widget (xml, "prefs_dialog");
  gtk_dialog_set_default_response (GTK_DIALOG (adata->prefs),
                           GTK_RESPONSE_CLOSE);
  gtk_window_set_transient_for (GTK_WINDOW (adata->prefs),
                        GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (adata->applet))));
  gtk_window_set_screen (GTK_WINDOW (adata->prefs),
                   gtk_widget_get_screen (GTK_WIDGET (adata->applet)));
  g_signal_connect (adata->prefs, "response",
                G_CALLBACK (gtk_widget_destroy), NULL);
  g_signal_connect (adata->prefs, "delete-event",
                G_CALLBACK (gtk_true), NULL);
  g_object_add_weak_pointer (G_OBJECT (adata->prefs),
                       (gpointer *) &adata->prefs);

  warning_box = glade_xml_get_widget (xml, "warning_box");

  label = glade_xml_get_widget (xml, "appearance_label");
  make_label_bold (GTK_LABEL (label));

  label = glade_xml_get_widget (xml, "options_label");
  make_label_bold (GTK_LABEL (label));

  username_radio = glade_xml_get_widget (xml, "username_radio");
  text_radio = glade_xml_get_widget (xml, "text_radio");
  icon_radio = glade_xml_get_widget (xml, "icon_radio");

  has_lockdown = panel_applet_get_locked_down (adata->applet);

  value = panel_applet_gconf_get_string (adata->applet, DISPLAY_KEY, NULL);
  if (!value || strcmp (value, "text") == 0)
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (text_radio), TRUE);
  else if (strcmp (value, "icon") == 0)
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (icon_radio), TRUE);
  else if (strcmp (value, "username") == 0)
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (username_radio), TRUE);
  g_free (value);

  g_object_set_data (G_OBJECT (username_radio),
                 "prefs-radio-display-style", "username");
  g_signal_connect (username_radio, "toggled",
                G_CALLBACK (prefs_radio_toggled_cb), adata);
  gtk_label_set_text (GTK_LABEL (GTK_BIN (username_radio)->child),
                  fusa_user_get_display_name (fusa_manager_get_user_by_uid (adata->manager,
                                                            getuid ())));

  g_object_set_data (G_OBJECT (text_radio),
                 "prefs-radio-display-style", "text");
  g_signal_connect (text_radio, "toggled",
                G_CALLBACK (prefs_radio_toggled_cb), adata);

  g_object_set_data (G_OBJECT (icon_radio),
                 "prefs-radio-display-style", "icon");
  g_signal_connect (icon_radio, "toggled",
                G_CALLBACK (prefs_radio_toggled_cb), adata);

  label_setup_done = FALSE;
  key = panel_applet_gconf_get_full_key (adata->applet, DISPLAY_KEY);
  if (has_lockdown || !gconf_client_key_is_writable (adata->client, key, NULL))
    {
      GtkWidget *label;

      gtk_widget_set_sensitive (username_radio, FALSE);
      gtk_widget_set_sensitive (text_radio, FALSE);
      gtk_widget_set_sensitive (icon_radio, FALSE);
      
      label = glade_xml_get_widget (xml, "warning_label");
      make_label_small_italic (GTK_LABEL (label));
      label_setup_done = TRUE;

      gtk_widget_show (warning_box);
    }
  g_free (key);

  /* [_] Lock the screen after switching */
  check = glade_xml_get_widget (xml, "lock_check");
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
                        panel_applet_gconf_get_bool (adata->applet,
                                               LOCK_KEY,
                                               NULL));
  g_signal_connect (check, "toggled",
                G_CALLBACK (prefs_lock_check_toggled_cb), adata);
  key = panel_applet_gconf_get_full_key (adata->applet, LOCK_KEY);
  if (has_lockdown || !gconf_client_key_is_writable (adata->client, key, NULL))
    {
      gtk_widget_set_sensitive (check, FALSE);

      if (!label_setup_done)
      {
        GtkWidget *label;
      
        label = glade_xml_get_widget (xml, "warning_label");
        make_label_small_italic (GTK_LABEL (label));
        label_setup_done = TRUE;

        gtk_widget_show (warning_box);
      }
    }
  g_free (key);

  /* [_] Create new logins in nested windows */
  check = glade_xml_get_widget (xml, "xnest_check");
  xnest_available = system_can_do_xnest ();
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
                        (panel_applet_gconf_get_bool (adata->applet,
                                               USE_XNEST_KEY,
                                               NULL) &&
                           xnest_available));
  g_signal_connect (check, "toggled",
                G_CALLBACK (prefs_xnest_check_toggled_cb), adata);
  key = panel_applet_gconf_get_full_key (adata->applet, USE_XNEST_KEY);
  if (has_lockdown || !gconf_client_key_is_writable (adata->client, key, NULL))
    {
      gtk_widget_set_sensitive (check, FALSE);

      if (!label_setup_done)
      {
        GtkWidget *label;
      
        label = glade_xml_get_widget (xml, "warning_label");
        make_label_small_italic (GTK_LABEL (label));
        label_setup_done = TRUE;

        gtk_widget_show (warning_box);
      }
    }
  else if (!xnest_available)
    gtk_widget_set_sensitive (check, FALSE);
  g_free (key);
  
  gtk_window_present (GTK_WINDOW (adata->prefs));
}

static void
help_cb (BonoboUIComponent *ui_container,
       gpointer           data,
       const gchar       *cname)
{
  FusaAppletData *adata;
  GError *err;

  adata = data;
  err = NULL;
  gnome_help_display_on_screen ("fast-user-switch-applet", NULL,
                        gtk_widget_get_screen (GTK_WIDGET (adata->applet)),
                        &err);
  if (err)
    {
      g_critical ("Could not open help document: %s", err->message);
      g_error_free (err);
    }
}

static void
about_cb (BonoboUIComponent *ui_container,
        gpointer           data,
        const gchar       *cname)
{
  static const gchar *authors[] = {
    "James M. Cape <jcape@ignore-your.tv>",
    "Thomas Thurman <thomas@thurman.org.uk>",
    NULL
  };
  static const gchar *artists[] = {
    "Hopefully, the users themselves",
    NULL
  };
  static gchar *license[] = {
    N_("The Fast User Switch Applet 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."),
    N_("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."),
    N_("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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA "),
    NULL
  };
  gchar *license_i18n;

  license_i18n = g_strjoinv ("\n\n", license);

  gtk_show_about_dialog(NULL,
      "name", _("Fast User Switch Applet"),
      "version", VERSION,
      "copyright", "Copyright \xc2\xa9 2004-2005 James M. Cape.\n"
                   "Copyright \xc2\xa9 2006 Thomas Thurman.",
      "comments", _("A menu to quickly switch between users."),
      "authors", authors,
      "artists", artists,
      "license", license_i18n,
      "wrap-license", TRUE,
      "translator-credits", _("translator-credits"),
      "logo-icon-name", "stock_people",
      "website", "http://ignore-your.tv/fusa/",
      NULL
   );

  g_free (license_i18n);
}


/* *********************** *
 *  FusaManager Callbacks  *
 * *********************** */

static void
manager_new_console_cb (FusaManager  *manager,
                  FusaDisplay  *display,
                  const GError *error,
                  gpointer      data)
{
  if (error)
    show_error_dialog (data, error);
  else
    maybe_lock_screen (data, display);
}

static void
manager_activate_display_cb (FusaManager  *manager,
                       FusaDisplay  *display,
                       const GError *error,
                       gpointer      data)
{
  if (error)
    show_error_dialog (data, error);
  else
    maybe_lock_screen (data, display);
}

static void
manager_new_xnest_cb (FusaManager  *manager,
                  FusaDisplay  *display,
                  const GError *error,
                  gpointer      data)
{
  if (error)
    show_error_dialog (data, error);
}

static void
manager_user_added_cb (FusaManager *manager,
                   FusaUser    *user,
                   gpointer     data)
{
  FusaAppletData *adata;
  GtkWidget *menuitem;
  gboolean active_only;

  adata = data;

  menuitem = fusa_user_menu_item_new (user);
  g_object_set_qdata (G_OBJECT (user), adata->user_menu_item_quark, menuitem);
  g_signal_connect (menuitem, "style-set",
                G_CALLBACK (menuitem_style_set_cb), adata);
  g_signal_connect (menuitem, "destroy",
                G_CALLBACK (menuitem_destroy_cb), adata);
  g_signal_connect (menuitem, "activate",
                G_CALLBACK (user_item_activate_cb), adata);
  gtk_menu_shell_append (GTK_MENU_SHELL (adata->menu), menuitem);
  adata->items = g_slist_prepend (adata->items, menuitem);

  active_only = gconf_client_get_bool (adata->client, ACTIVE_ONLY_KEY, NULL);
  if (active_only)
    {
      GSList *displays;

      displays = fusa_user_get_displays (user);
      if (displays)
      {
        g_slist_free (displays);
        gtk_widget_show (menuitem);
      }
    }
  else
    gtk_widget_show (menuitem);
  g_signal_connect (user, "displays-changed",
                G_CALLBACK (user_displays_changed_cb), adata);
  sort_menu (adata);
}

static void
user_displays_changed_cb (FusaUser *user,
                    gpointer  data)
{
  FusaAppletData *adata;
  GtkWidget *menuitem;
  gboolean active_only;

  adata = data;
  menuitem = g_object_get_qdata (G_OBJECT (user), adata->user_menu_item_quark);
  if (!menuitem)
    return;

  active_only = gconf_client_get_bool (adata->client, ACTIVE_ONLY_KEY, NULL);
  if (active_only)
    {
      GSList *displays;

      displays = fusa_user_get_displays (user);
      if (displays)
      {
        g_slist_free (displays);
        gtk_widget_show (menuitem);
      }
      else
      gtk_widget_hide (menuitem);
    }
  else
    gtk_widget_show (menuitem);

  sort_menu (adata);
}


/* *********************** *
 *  GConfClient Callbacks  *
 * *********************** */

static void
client_notify_applet_func (GConfClient *client,
                     guint        cnxn_id,
                     GConfEntry  *entry,
                     gpointer     data)
{
  FusaAppletData *adata;
  GConfValue *value;
  gchar *key;

  adata = data;
  value = gconf_entry_get_value (entry);

  if (!value)
    return;

  key = g_path_get_basename (gconf_entry_get_key (entry));

  if (!key)
    return;

  if (strcmp (key, DISPLAY_KEY) == 0)
    {
      const gchar *str;
      GtkWidget *parent;
      FusaUser *user;

      str = gconf_value_get_string (value);
      parent = adata->imglabel->parent;
      user = fusa_manager_get_user_by_uid (adata->manager, getuid ());
      if (str)
      {
        if (strcmp (str, "username") == 0)
          {
            if (GTK_IS_IMAGE (adata->imglabel))
            {
              gtk_widget_destroy (adata->imglabel);
        
              adata->imglabel = gtk_label_new (fusa_user_get_display_name (user));
              adata->user_notify_id =
                g_signal_connect (user, "notify::display-name",
                              G_CALLBACK (user_notify_display_name_cb),
                              adata->imglabel);
              gtk_box_pack_start (GTK_BOX (parent), adata->imglabel,
                              TRUE, TRUE, 0);
              gtk_widget_show (adata->imglabel);
            }
            else if (!adata->user_notify_id)
            {
              gtk_label_set_text (GTK_LABEL (adata->imglabel),
                              fusa_user_get_display_name (user));
              adata->user_notify_id =
                g_signal_connect (user, "notify::display-name",
                              G_CALLBACK (user_notify_display_name_cb),
                              adata->imglabel);
            }
          }
        else if (strcmp (str, "icon") == 0)
          {
            if (!GTK_IS_IMAGE (adata->imglabel))
            {
              guint item_border;

              if (adata->user_notify_id)
                {
                  g_signal_handler_disconnect (user, adata->user_notify_id);
                  adata->user_notify_id = 0;
                }
              if (adata->imglabel->parent->style)
                item_border = (MAX (adata->imglabel->parent->style->xthickness,
                              adata->imglabel->parent->style->ythickness) * 2);
              else
                item_border = 0;

              gtk_widget_destroy (adata->imglabel);

              adata->imglabel =
                gtk_image_new_from_icon_name ("stock_people",
                                      GTK_ICON_SIZE_MENU);
              gtk_box_pack_start (GTK_BOX (parent), adata->imglabel,
                              TRUE, TRUE, 0);
              gtk_widget_show (adata->imglabel);
            }
          }
        else
          {
            if (adata->user_notify_id)
            {
              g_signal_handler_disconnect (user, adata->user_notify_id);
              adata->user_notify_id = 0;
              gtk_label_set_text (GTK_LABEL (adata->imglabel), _("Users"));
            }
            else
            {
              gtk_widget_destroy (adata->imglabel);
              adata->imglabel = gtk_label_new (_("Users"));
              gtk_box_pack_start (GTK_BOX (parent), adata->imglabel,
                              TRUE, TRUE, 0);
              gtk_widget_show (adata->imglabel);
            }
          }
      }
      else
      {
        if (adata->user_notify_id)
          {
            g_signal_handler_disconnect (user, adata->user_notify_id);
            adata->user_notify_id = 0;
            gtk_label_set_text (GTK_LABEL (adata->imglabel), _("Users"));
          }
        else
          {
            gtk_widget_destroy (adata->imglabel);
            adata->imglabel = gtk_label_new (_("Users"));
            gtk_box_pack_start (GTK_BOX (parent), adata->imglabel,
                          TRUE, TRUE, 0);
            gtk_widget_show (adata->imglabel);
          }
      }
    }
  else if (strcmp (key, USE_XNEST_KEY) == 0)
    {
      gchar *str;

      str = gconf_client_get_string (client, SHOW_SCREEN_KEY, NULL);
      reset_login_screen_item (adata->login_screen_item,
                         gconf_value_get_bool (value), str);
      g_free (str);

      str = gconf_client_get_string (client, SHOW_WINDOW_KEY, NULL);
      reset_login_window_item (adata->login_window_item,
                         gconf_value_get_bool (value), str);
      g_free (str);

      if (GTK_WIDGET_VISIBLE (adata->login_screen_item) ||
        GTK_WIDGET_VISIBLE (adata->login_window_item))
      gtk_widget_show (adata->separator_item);
      else
      gtk_widget_hide (adata->separator_item);
    }

  g_free (key);
}

static void
client_notify_global_func (GConfClient *client,
                     guint        cnxn_id,
                     GConfEntry  *entry,
                     gpointer     data)
{
  FusaAppletData *adata;
  GConfValue *value;
  const gchar *key;

  adata = data;
  value = gconf_entry_get_value (entry);
  key = gconf_entry_get_key (entry);

  if (!value || !key)
    return;

  if (strcmp (key, SHOW_SCREEN_KEY) == 0)
    reset_login_screen_item (adata->login_screen_item,
                       panel_applet_gconf_get_bool (adata->applet,
                                            USE_XNEST_KEY, NULL),
                       gconf_value_get_string (value));
  else if (strcmp (key, SHOW_WINDOW_KEY) == 0)
    reset_login_window_item (adata->login_window_item,
                       panel_applet_gconf_get_bool (adata->applet,
                                            USE_XNEST_KEY, NULL),
                       gconf_value_get_string (value));
  else if (strcmp (key, ACTIVE_ONLY_KEY) == 0)
    {
      GSList *items;
      
      items = adata->items;
      while (items)
      {
        if (FUSA_IS_USER_MENU_ITEM (items->data))
          {
            if (gconf_value_get_bool (value))
            {
              GSList *displays;

              displays = fusa_user_get_displays (fusa_user_menu_item_get_user (items->data));
              if (displays)
                {
                  g_slist_free (displays);
                  gtk_widget_show (items->data);
                }
              else
                gtk_widget_hide (items->data);
            }
            else
            gtk_widget_show (items->data);
          }

        items = items->next;
      }
    }

  if (GTK_WIDGET_VISIBLE (adata->login_screen_item) ||
      GTK_WIDGET_VISIBLE (adata->login_window_item))
    gtk_widget_show (adata->separator_item);
  else
    gtk_widget_hide (adata->separator_item);

  sort_menu (adata);
}


/* ************************ *
 *  Child Widget Callbacks  *
 * ************************ */

static void
prefs_radio_toggled_cb (GtkToggleButton *button,
                  gpointer         data)
{
  if (gtk_toggle_button_get_active (button))
    {
      FusaAppletData *adata;
      gpointer value;
    
      adata = data;
      value = g_object_get_data (G_OBJECT (button),
                         "prefs-radio-display-style");
      panel_applet_gconf_set_string (adata->applet, DISPLAY_KEY, value, NULL);
    }
}

static void
prefs_xnest_check_toggled_cb (GtkToggleButton *button,
                        gpointer         data)
{
  FusaAppletData *adata;

  adata = data;
  panel_applet_gconf_set_bool (adata->applet, USE_XNEST_KEY,
                         gtk_toggle_button_get_active (button), NULL);
}

static void
prefs_lock_check_toggled_cb (GtkToggleButton *button,
                       gpointer         data)
{
  FusaAppletData *adata;

  adata = data;
  panel_applet_gconf_set_bool (adata->applet, LOCK_KEY,
                         gtk_toggle_button_get_active (button), NULL);
}

/*
 * gnome-panel/applets/wncklet/window-menu.c:window_filter_button_press()
 * 
 * Copyright (C) 2005 James M. Cape.
 * Copyright (C) 2003 Sun Microsystems, Inc.
 * Copyright (C) 2001 Free Software Foundation, Inc.
 * Copyright (C) 2000 Helix Code, Inc.
 */
static gboolean
menubar_button_press_event_cb (GtkWidget      *menubar,
                         GdkEventButton *event,
                         gpointer        data)
{
  if (event->button != 1)
    g_signal_stop_emission_by_name (menubar, "button-press-event");
  /* Reset the login window item */
  else
    {
      FusaAppletData *adata;
      gboolean use_xnest;
      gchar *value;

      adata = data;
      use_xnest = panel_applet_gconf_get_bool (adata->applet, USE_XNEST_KEY,
                                     NULL);
      value = gconf_client_get_string (adata->client, SHOW_WINDOW_KEY, NULL);
      reset_login_window_item (GTK_WIDGET (adata->login_window_item), use_xnest,
                         value);
      g_free (value);
    }

  return FALSE;
}

/*
 * gnome-panel/applets/wncklet/window-menu.c:window_menu_on_expose()
 * 
 * Copyright (C) 2003 Sun Microsystems, Inc.
 * Copyright (C) 2001 Free Software Foundation, Inc.
 * Copyright (C) 2000 Helix Code, Inc.
 */
static gboolean
menubar_expose_event_cb (GtkWidget      *widget,
                   GdkEventExpose *event,
                   gpointer        data)
{
  FusaAppletData *adata;

  adata = data;

  if (GTK_WIDGET_HAS_FOCUS (adata->applet))
    gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
                 NULL, widget, "menu-applet", 0, 0, -1, -1);

  return FALSE;
}

/* When the user opens the menu of users, menu_expose_cb gets called.
 * That then calls GDM to ask it which users have current logins,
 * and when it has responded and we've updated the relevant data
 * structures, it calls this callback.
 *
 * All we have to do here is repaint the menu, since the menu items
 * are clever and will add or remove their check marks according to
 * whether their users have any current logins.
 */
static void
redraw_menu_cb (FusaManager  *manager,
    FusaDisplay  *display,
    const GError *error,
    gpointer      data)
{
  FusaAppletData *adata = data;
      
  gtk_widget_queue_draw (adata->menu);
}


/* Called every time the menu is displayed (and also for some reason
 * immediately it's created, which does no harm). All we have to do
 * here is kick off a request to GDM to let us know which users are
 * logged in, so we can display check marks next to their names.
 */
static gboolean
menu_expose_cb (GtkWidget *menu,
                gpointer   data)
{
  FusaAppletData *adata = data;

  fusa_manager_request_update_displays (adata->manager,
      redraw_menu_cb,
      data);

  return FALSE;
}

static void
menu_style_set_cb (GtkWidget *menu,
               GtkStyle  *old_style,
               gpointer   data)
{
  FusaAppletData *adata;
  GtkSettings *settings;
  gint width, height;

  adata = data;
  adata->icon_size = gtk_icon_size_from_name ("panel-menu");

  if (adata->icon_size == GTK_ICON_SIZE_INVALID)
    adata->icon_size = gtk_icon_size_register ("panel-menu", 24, 24);

  if (gtk_widget_has_screen (menu))
    settings = gtk_settings_get_for_screen (gtk_widget_get_screen (menu));
  else
    settings = gtk_settings_get_default ();

  if (!gtk_icon_size_lookup_for_settings (settings, adata->icon_size,
                                &width, &height))
    adata->pixel_size = -1;
  else
    adata->pixel_size = MAX (width, height);

  sort_menu (adata);
}

static void
menuitem_destroy_cb (GtkWidget *menuitem,
                 gpointer   data)
{
  FusaAppletData *adata;
  GSList *li;

  adata = data;

  if (FUSA_IS_USER_MENU_ITEM (menuitem))
    {
      FusaUser *user;
      
      user = fusa_user_menu_item_get_user (FUSA_USER_MENU_ITEM (menuitem));
      if (user)
        g_object_set_qdata (G_OBJECT (user),
                      adata->user_menu_item_quark, NULL);
    }

  li = g_slist_find (adata->items, menuitem);
  adata->items = g_slist_delete_link (adata->items, li);
  sort_menu (adata);
}

static void
menuitem_style_set_cb (GtkWidget *menuitem,
                   GtkStyle  *old_style,
                   gpointer   data)
{
  FusaAppletData *adata;

  adata = data;

  if (FUSA_IS_USER_MENU_ITEM (menuitem))
    fusa_user_menu_item_set_icon_size (FUSA_USER_MENU_ITEM (menuitem),
                               adata->pixel_size);
  else
    {
      GtkWidget *image;
      const gchar *icon_name;

      if (menuitem == adata->login_screen_item)
      icon_name = "gdm";
      else if (menuitem == adata->login_window_item)
      icon_name = "gdm-xnest";
      else
      icon_name = GTK_STOCK_MISSING_IMAGE;

      image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (menuitem));
      gtk_image_set_pixel_size (GTK_IMAGE (image), adata->pixel_size);
      gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name,
                            adata->icon_size);
    }
}

static void
user_item_activate_cb (GtkWidget *menuitem,
                   gpointer   data)
{
  FusaAppletData *adata;
  FusaUserMenuItem *item;
  FusaUser *user;
  GSList *displays;

  adata = data;
  item = FUSA_USER_MENU_ITEM (menuitem);
  user = fusa_user_menu_item_get_user (item);

  displays = fusa_user_get_displays (user);

  if (displays)
    {
      if (g_slist_length (displays) > 1)
      {
        GSList *li, *unique_consoles, *unique_displays;
        FusaDisplay *this_display;
        gint this_console;
  
        this_display = fusa_manager_get_display_by_screen (adata->manager,
                                               gtk_widget_get_screen (menuitem));
        this_console = fusa_display_get_console (this_display);

        /* Only show unique consoles for this user */
        unique_consoles = NULL;
        unique_displays = NULL;
        li = displays;
        while (li)
          {
            GSList *li2;
            gint console;

            console = fusa_display_get_console (li->data);
            if (console == this_console && li->data != this_display)
            li2 = NULL;
            else
            li2 = g_slist_find (unique_consoles, GINT_TO_POINTER (console));

            if (!li2)
            {
              unique_consoles = g_slist_prepend (unique_consoles,
                                         GINT_TO_POINTER (console));
              unique_displays = g_slist_prepend (unique_displays,
                                         li->data);
            }

            li = li->next;
          }

        g_slist_free (unique_consoles);
        if (g_slist_length (unique_displays) > 1)
          show_display_selector_dialog (adata, unique_displays);
        else
          run_switch (adata, unique_displays->data, FALSE);

        g_slist_free (unique_displays);
      }
      else
      run_switch (adata, displays->data, FALSE);

      g_slist_free (displays);
    }
  else
    {
      gboolean use_xnest;

      use_xnest = (panel_applet_gconf_get_bool (adata->applet, USE_XNEST_KEY,
                                    NULL) &&
               system_can_do_xnest ());
      displays = fusa_manager_list_displays (adata->manager);

      /* Only one display open (this one), so create a new one */
      if (g_slist_length (displays) == 1)
      {
        run_switch (adata, NULL, use_xnest);
        g_slist_free (displays);
      }
      /* Iterate through to find an open display */
      else
      {
        while (displays)
          {
            /* If the display is user-less, and either we want nested and it
             * is, or we don't, and it isn't, switch to it, free the list,
             * and leave. */
            if (!fusa_display_get_user (displays->data) &&
              (use_xnest == !!fusa_display_get_nested (displays->data)))
            {
              run_switch (adata, displays->data, FALSE);
              /* The pointer will be invalid but !NULL after this */
              g_slist_free (displays);
              break;
            }
          
            displays = g_slist_delete_link (displays, displays);
          }

        if (!displays)
          run_switch (adata, NULL, use_xnest);
      }
    }
}

static void
login_screen_activate_cb (GtkMenuItem *item,
                    gpointer     data)
{
  FusaAppletData *adata;
  GSList *displays;
  FusaDisplay *target;

  adata = data;
  target = NULL;

  displays = fusa_manager_list_displays (adata->manager);
  while (displays)
    {
      if (!fusa_display_get_user (displays->data) &&
        !fusa_display_get_nested (displays->data))
      {
        target = displays->data;
        break;
      }

      displays = g_slist_delete_link (displays, displays);
    }
  g_slist_free (displays);

  run_switch (adata, target, FALSE);
}

static void
login_window_activate_cb (GtkMenuItem *item,
                    gpointer     data)
{
  FusaAppletData *adata;
  GSList *displays;
  FusaDisplay *target;

  adata = data;
  target = NULL;

  displays = fusa_manager_list_displays (adata->manager);
  while (displays)
    {
      if (!fusa_display_get_user (displays->data) &&
        fusa_display_get_nested (displays->data))
      {
        target = displays->data;
        break;
      }

      displays = g_slist_delete_link (displays, displays);
    }
  g_slist_free (displays);

  if (!system_can_do_xnest ())
    {
      g_critical ("%s: (%s): FIXME: show an error dialog when Xnest is missing.",
              G_STRLOC, G_STRFUNC);
      gtk_widget_hide (GTK_WIDGET (item));
    }
  else
    {
      run_switch (adata, target, TRUE);
    }
}

static void
user_notify_display_name_cb (GObject    *object,
                       GParamSpec *pspec,
                       gpointer    data)
{
  gtk_label_set_text (data, fusa_user_get_display_name (FUSA_USER (object)));
}


/* ******************* *
 *  Utility Functions  *
 * ******************* */

static void
reset_login_screen_item (GtkWidget   *widget,
                   gboolean     use_xnest_value,
                   const gchar *pref_value)
{
  if (!pref_value || strcmp (pref_value, "always") == 0)
    gtk_widget_show (widget);
  else if (pref_value && strcmp (pref_value, "never") == 0)
    gtk_widget_hide (widget);
  else if (use_xnest_value)
    gtk_widget_hide (widget);
  else
    gtk_widget_show (widget);
}

static void
reset_login_window_item (GtkWidget   *widget,
                   gboolean     use_xnest_value,
                   const gchar *pref_value)
{
  if (!system_can_do_xnest ())
    gtk_widget_hide (widget);
  else if (pref_value && strcmp (pref_value, "always") == 0)
    gtk_widget_show (widget);
  else if (pref_value && strcmp (pref_value, "never") == 0)
    gtk_widget_hide (widget);
  else if (use_xnest_value)
    gtk_widget_show (widget);
  else
    gtk_widget_hide (widget);
}

static void
selector_dialog_response_cb (GtkWidget *dialog,
                       gint       response,
                       gpointer   data)
{
  SelectorResponseData *res_data;

  res_data = data;

  if (response == GTK_RESPONSE_OK)
    {
      GSList *rbuttons;
    
      rbuttons = gtk_radio_button_get_group (res_data->radio);

      while (rbuttons)
      {
        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rbuttons->data)))
          {
            run_switch (res_data->adata,
                    g_object_get_data (G_OBJECT (rbuttons->data),
                                   "fusa-applet-radio-display"),
                    FALSE);
            break;
          }

        rbuttons = rbuttons->next;
      }

    }

  gtk_widget_set_sensitive (GTK_WIDGET (res_data->adata->applet), TRUE);
  g_free (data);

  gtk_widget_destroy (dialog);
}

static void
show_display_selector_dialog (FusaAppletData *adata,
                        GSList         *displays)
{
  GtkWidget *toplevel;
  GtkWidget *dialog, *button, *image, *hbox, *vbox, *label, *radio_box, *radio;
  FusaUser *user;
  gchar *markup;
  SelectorResponseData *data;

  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (adata->applet));

  dialog =
    gtk_dialog_new_with_buttons (_("Multiple Logins Found - Fast User-Switch"),
                         (toplevel ? GTK_WINDOW (toplevel) : NULL),
                         GTK_DIALOG_NO_SEPARATOR, 
                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                         NULL);
  button = gtk_dialog_add_button (GTK_DIALOG (dialog),
                          _("Continue"), GTK_RESPONSE_OK);
  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
  image = gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON);
  gtk_button_set_image (GTK_BUTTON (button), image);
  gtk_widget_show (image);
  g_signal_connect (dialog, "style-set",
                G_CALLBACK (fusa_dialog_hig_fix), GUINT_TO_POINTER (!NULL));

  hbox = gtk_hbox_new (FALSE, 12);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);
  
  image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION,
                            GTK_ICON_SIZE_DIALOG);
  gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
  gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
  gtk_widget_show (image);

  vbox = gtk_vbox_new (FALSE, 12);
  gtk_container_add (GTK_CONTAINER (hbox), vbox);
  gtk_widget_show (vbox);

  markup = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
                      _("Multiple Logins Found"), 
                      _("The user you want to switch to is logged in multiple times on this computer. Which login do you want to switch to?"));
  label = gtk_label_new (markup);
  g_free (markup);
  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
  gtk_container_add (GTK_CONTAINER (vbox), label);
  gtk_widget_show (label);

  radio_box = gtk_vbox_new (FALSE, 6);
  gtk_container_add (GTK_CONTAINER (vbox), radio_box);
  gtk_widget_show (radio_box);

  user = fusa_display_get_user (displays->data);
  radio = NULL;
  while (displays)
    {
      if (fusa_display_get_nested (displays->data))
      markup = g_strdup_printf (_("%s in a nested window on console %d"),
                          fusa_user_get_display_name (user),
                          fusa_display_get_console (displays->data));
      else
      markup = g_strdup_printf (_("%s on console %d"),
                          fusa_user_get_display_name (user),
                          fusa_display_get_console (displays->data));
      radio = gtk_radio_button_new_with_label_from_widget (radio ? GTK_RADIO_BUTTON (radio) : NULL,
                                             markup);
      g_object_set_data (G_OBJECT (radio), "fusa-applet-radio-display",
                   displays->data); 
      g_free (markup);
      gtk_box_pack_start (GTK_BOX (radio_box), radio, FALSE, FALSE, 0);
      gtk_widget_show (radio);

      displays = displays->next;
    }

  data = g_new0 (SelectorResponseData, 1);
  data->adata = adata;
  data->radio = GTK_RADIO_BUTTON (radio);
  g_signal_connect (dialog, "response",
                G_CALLBACK (selector_dialog_response_cb), data);

  gtk_window_present (GTK_WINDOW (dialog));
  gtk_widget_set_sensitive (GTK_WIDGET (adata->applet), FALSE);
}

static void
show_error_dialog (FusaAppletData *adata,
               const GError   *error)
{
  GladeXML *xml;
  GtkWidget *dialog;
  GtkWidget *label, *image;

  if (error->domain == FUSA_MANAGER_DM_ERROR)
    {
      const gchar *primary, *secondary, *stock_id;
      gboolean is_fatal;

      switch (error->code)
      {
      case FUSA_MANAGER_DM_ERROR_NOT_RUNNING:
      case FUSA_MANAGER_DM_ERROR_UNKNOWN:
        is_fatal = TRUE;
        primary = _("Display Manager Unavailable");
        secondary = _("In order for the User Selector to function properly, the GNOME Display Manager must be running, but it is not.");
        break;
      case FUSA_MANAGER_DM_ERROR_CONSOLES_UNSUPPORTED:
        is_fatal = TRUE;
        primary = _("Multiple Consoles Not Supported");
        secondary = _("This system does not support multiple graphical login screens.");
        break;
      case FUSA_MANAGER_DM_ERROR_FLEXI_LIMIT_REACHED:
      case FUSA_MANAGER_DM_ERROR_X_LIMIT_REACHED:
        is_fatal = FALSE;
        primary = _("Too Many Sessions");
        secondary = _("There are currently too many sessions running to create a new one. Someone must log out, or the system configuration must be changed to allow for more.");
        break;
      case FUSA_MANAGER_DM_ERROR_X_ERROR:
      case FUSA_MANAGER_DM_ERROR_X_FAILED:
      case FUSA_MANAGER_DM_ERROR_X_NOT_FOUND:
        is_fatal = FALSE;
        primary = _("Graphical System Error");
        secondary = _("The graphical interface did not properly start, which is needed for graphical logins. It is likely that the X Window System or GDM is not properly configured.");
        break;
      case FUSA_MANAGER_DM_ERROR_XNEST_AUTH_ERROR:
      case FUSA_MANAGER_DM_ERROR_PERMISSIONS:
        is_fatal = FALSE;
        primary = _("Permissions Error");
        secondary = _("Your user does not have permissions to create additional logins.  Check your <i>~/.Xauthority</i> setup.");
        break;
      /* Ignore anything else. */
      default:
        return;
        is_fatal = FALSE;
        primary = NULL;
        secondary = NULL;
        break;
      }

      xml = get_glade_xml ("error_dialog");
      g_assert (xml);

      if (is_fatal)
      stock_id = GTK_STOCK_DIALOG_ERROR;
      else
      stock_id = GTK_STOCK_DIALOG_WARNING;

      image = glade_xml_get_widget (xml, "dialog_image");
      gtk_image_set_from_stock (GTK_IMAGE (image), stock_id,
                        GTK_ICON_SIZE_DIALOG);

      label = glade_xml_get_widget (xml, "primary_label");
      gtk_label_set_text (GTK_LABEL (label), primary);
      make_label_larger (GTK_LABEL (label));
      make_label_bold (GTK_LABEL (label));

      label = glade_xml_get_widget (xml, "secondary_label");
      gtk_label_set_markup (GTK_LABEL (label), secondary);

      label = glade_xml_get_widget (xml, "details_label");
      gtk_label_set_text (GTK_LABEL (label), error->message);

      dialog = glade_xml_get_widget (xml, "error_dialog");
      gtk_dialog_run (GTK_DIALOG (dialog));

      if (is_fatal)
      bonobo_main_quit ();

      g_object_unref (xml);
    }
}

static gint
sort_menu_comparedatafunc (gconstpointer a,
                     gconstpointer b,
                     gpointer      data)
{
  FusaAppletData *adata;
  gboolean a_is_user, b_is_user;

  a_is_user = FUSA_IS_USER_MENU_ITEM (a);
  b_is_user = FUSA_IS_USER_MENU_ITEM (b);

  if (a_is_user && !b_is_user)
    return -1;

  if (b_is_user && !a_is_user)
    return 1;

  if (a_is_user && b_is_user)
    return fusa_user_collate (fusa_user_menu_item_get_user ((FusaUserMenuItem *) a),
                        fusa_user_menu_item_get_user ((FusaUserMenuItem *) b));

  adata = data;
  if (a == adata->separator_item)
    return -1;

  if (b == adata->separator_item)
    return 1;

  if (a == adata->login_screen_item)
    return -1;

  if (b == adata->login_screen_item)
    return 1;

  return 0;
}

static void
sort_menu (FusaAppletData *adata)
{
  GSList *items;
  guint n_items, n_rows, n_cols, row, column, count;
  gint screen_height;

  if (!gtk_widget_has_screen (adata->menu))
    return;

  adata->items = g_slist_sort_with_data (adata->items,
                               sort_menu_comparedatafunc, adata);

  screen_height = gdk_screen_get_height (gtk_widget_get_screen (adata->menu));

  n_items = 0;
  items = adata->items;
  while (items)
    {
      if (GTK_WIDGET_VISIBLE (items->data))
      n_items++;

      items = items->next;
    }

  /* FIXME: Do a better job of figuring out exactly how big the menuitems are */
  n_rows = (gdouble) screen_height / (gdouble) (adata->pixel_size + 16);
  n_cols = (gdouble) n_items / (gdouble) n_rows;
  n_rows = (gdouble) n_items / (gdouble) (n_cols + 1);

  row = 0;
  column = 0;
  count = 0;
  items = adata->items;
  while (items)
    {
      if (GTK_WIDGET_VISIBLE (items->data))
      {
        gtk_menu_attach (GTK_MENU (adata->menu), items->data,
                     column, column + 1, row, row + 1);
        row++;
        if (row > n_rows)
          {
            row = 0;
            column++;
          }

          /* Just re-attaching them doesn't alter the order you get them
           * in when you move through with the arrow keys, though; we
           * have to set that explicitly.
           */
          gtk_menu_reorder_child (GTK_MENU (adata->menu),
              items->data, count++);
      }

      items = items->next;
    }
}

static GladeXML *
get_glade_xml (const gchar *root)
{
  GladeXML *xml;

  xml = glade_xml_new (DATADIR "/fast-user-switch-applet/ui.glade", root, NULL);

  if (!xml)
    {
      GtkWidget *dialog;

      dialog =
      gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
                        _("Missing Required File"));
      gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                    _("The User Selector's interfaces file, `%s', could not be opened. It is likely that this application was not properly installed or configured."),
                                    DATADIR "/fast-user-switch-applet/ui.glade");
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      bonobo_main_quit ();
      return NULL;
    }
  
  return xml;
}

static void
run_switch (FusaAppletData *adata,
          FusaDisplay    *display,
          gboolean        use_xnest)
{
  GdkScreen *screen;

  if (gtk_widget_has_screen (adata->menubar))
    screen = gtk_widget_get_screen (adata->menubar);
  else
    screen = gdk_screen_get_default ();

  if (display)
    {
      fusa_manager_activate_display (adata->manager, display, screen,
                             manager_activate_display_cb, adata, NULL);
      return;
    }

  if (use_xnest)
    fusa_manager_new_xnest (adata->manager, screen,
                      manager_new_xnest_cb, adata, NULL);
  else
    fusa_manager_new_console (adata->manager, screen,
                        manager_new_console_cb, adata, NULL);
}

static void
maybe_lock_screen (FusaAppletData *adata,
               FusaDisplay    *display)
{
  if (panel_applet_gconf_get_bool (adata->applet, LOCK_KEY, NULL) &&
      !fusa_display_get_nested (display))
    {
      gchar *args[3];
      GError *err;
      GdkScreen *screen;
      gboolean use_gscreensaver = TRUE;

      args[0] = g_find_program_in_path ("gnome-screensaver-command");
      if (!args[0])
        {
          args[0] = g_find_program_in_path ("xscreensaver-command");
          use_gscreensaver = FALSE;
        }
      if (!args[0])
      return;

      if (use_gscreensaver)
      args[1] = "--lock";
      else
      args[1] = "-lock";
      args[2] = NULL;

      if (gtk_widget_has_screen (GTK_WIDGET (adata->applet)))
      screen = gtk_widget_get_screen (GTK_WIDGET (adata->applet));
      else
      screen = gdk_screen_get_default ();

      err = NULL;
      if (!gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL,
                        (G_SPAWN_STDERR_TO_DEV_NULL |
                         G_SPAWN_STDOUT_TO_DEV_NULL),
                        NULL, NULL, NULL, &err))
      {
        g_warning (_("Can't lock screen: %s"), err->message);
        g_error_free (err);
      }

      if (use_gscreensaver)
      args[1] = "--throttle";
      else
      args[1] = "-throttle";

      if (!gdk_spawn_on_screen (screen, g_get_home_dir (), args, NULL,
                        (G_SPAWN_STDERR_TO_DEV_NULL |
                         G_SPAWN_STDOUT_TO_DEV_NULL),
                        NULL, NULL, NULL, &err))
      {
        g_warning (_("Can't temporarily set screensaver to blank screen: %s"),
                 err->message);
        g_error_free (err);
      }

      g_free (args[0]);
    }
}

static gboolean
system_can_do_xnest (void)
{
  gchar *tmp;
  gboolean retval;

  tmp = g_find_program_in_path ("Xnest");
  retval = (tmp != NULL ? TRUE : FALSE);
  g_free (tmp);

  return retval;
}

static void
make_label_bold (GtkLabel *label)
{
  PangoAttrList *list;
  PangoAttribute *attr;
  gboolean existing_list;

  list = gtk_label_get_attributes (label);
  existing_list = (list != NULL);
  if (!existing_list)
    list = pango_attr_list_new ();
  else
    pango_attr_list_ref (list);

  attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
  attr->start_index = 0;
  attr->end_index = (guint) -1;
  pango_attr_list_insert (list, attr);

  gtk_label_set_attributes (label, list);
  pango_attr_list_unref (list);
}

static void
make_label_larger (GtkLabel *label)
{
  PangoAttrList *list;
  PangoAttribute *attr;
  gboolean existing_list;

  list = gtk_label_get_attributes (label);
  existing_list = (list != NULL);
  if (!existing_list)
    list = pango_attr_list_new ();
  else
    pango_attr_list_ref (list);

  attr = pango_attr_scale_new (PANGO_SCALE_LARGE);
  attr->start_index = 0;
  attr->end_index = (guint) -1;
  pango_attr_list_insert (list, attr);

  gtk_label_set_attributes (label, list);
  pango_attr_list_unref (list);
}

static void
make_label_small_italic (GtkLabel *label)
{
  PangoAttrList *list;
  PangoAttribute *attr;
  gboolean existing_list;

  list = gtk_label_get_attributes (label);
  existing_list = (list != NULL);
  if (!existing_list)
    list = pango_attr_list_new ();
  else
    pango_attr_list_ref (list);

  attr = pango_attr_style_new (PANGO_STYLE_ITALIC);
  attr->start_index = 0;
  attr->end_index = (guint) -1;
  pango_attr_list_insert (list, attr);

  attr = pango_attr_scale_new (PANGO_SCALE_SMALL);
  attr->start_index = 0;
  attr->end_index = (guint) -1;
  pango_attr_list_insert (list, attr);

  gtk_label_set_attributes (label, list);
  pango_attr_list_unref (list);
}

static void
fusa_applet_data_free (FusaAppletData *adata)
{
  if (adata->prefs)
    gtk_widget_destroy (adata->prefs);

  gconf_client_notify_remove (adata->client, adata->client_notify_applet_id);
  gconf_client_notify_remove (adata->client, adata->client_notify_global_id);
  gconf_client_remove_dir (adata->client, GLOBAL_DIR, NULL);
  g_object_unref (adata->client);
  g_object_unref (adata->manager);
  g_object_unref (tooltips);

  g_free (adata);
}

Generated by  Doxygen 1.6.0   Back to index