/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 *
 * Copyright (C) 2010 Red Hat, Inc
 * Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
 * Copyright (C) 2010 Richard Hughes <richard@hughsie.com>
 *
 * 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 <libupower-glib/upower.h>
#include <glib/gi18n.h>
#include <gnome-settings-daemon/gsd-enums.h>
#include "egg-list-box/egg-list-box.h"

#ifdef HAVE_BLUETOOTH
/* Handling is broken:
 * https://bugzilla.gnome.org/show_bug.cgi?id=691730
 * https://bugzilla.gnome.org/show_bug.cgi?id=691151
 * #include <bluetooth-client.h> */
#undef HAVE_BLUETOOTH
#endif



#ifdef HAVE_NETWORK_MANAGER
#include <nm-client.h>
#endif

#include "cc-power-panel.h"
#include "cc-power-resources.h"

/* Uncomment this to test the behaviour of the panel in
 * battery-less situations:
 *
 * #define TEST_NO_BATTERIES
 */

/* Uncomment this to test the behaviour of the panel with
 * multiple appearing devices
 *
 * #define TEST_FAKE_DEVICES
 */

#define WID(b, w) (GtkWidget *) gtk_builder_get_object (b, w)

CC_PANEL_REGISTER (CcPowerPanel, cc_power_panel)

#define POWER_PANEL_PRIVATE(o) \
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_POWER_PANEL, CcPowerPanelPrivate))

struct _CcPowerPanelPrivate
{
  GSettings     *gsd_settings;
  GSettings     *session_settings;
  GCancellable  *cancellable;
  GtkBuilder    *builder;
  UpClient      *up_client;
  GDBusProxy    *screen_proxy;
  gboolean       has_batteries;

  GList         *boxes;
  GList         *boxes_reverse;

  GtkSizeGroup  *row_sizegroup;
  GtkSizeGroup  *battery_sizegroup;
  GtkSizeGroup  *charge_sizegroup;
  GtkSizeGroup  *level_sizegroup;

  GtkWidget     *battery_heading;
  GtkWidget     *battery_section;
  GtkWidget     *battery_list;
  GtkWidget     *battery_capacity;

  GtkWidget     *device_heading;
  GtkWidget     *device_section;
  GtkWidget     *device_list;

  GtkWidget     *dim_screen_row;
  GtkWidget     *brightness_row;
  GtkWidget     *brightness_scale;
  gboolean       setting_brightness;

  GtkWidget     *automatic_suspend_row;
  GtkWidget     *automatic_suspend_label;
  GtkWidget     *critical_battery_row;
  GtkWidget     *critical_battery_combo;

#ifdef HAVE_BLUETOOTH
  BluetoothClient *bt_client;
  GtkWidget       *bt_switch;
#endif

#ifdef HAVE_NETWORK_MANAGER
  NMClient      *nm_client;
  GtkWidget     *wifi_switch;
  GtkWidget     *wifi_row;
  GtkWidget     *mobile_switch;
  GtkWidget     *mobile_row;
#endif

  GtkAdjustment *focus_adjustment;
};

enum
{
  ACTION_MODEL_TEXT,
  ACTION_MODEL_VALUE
};

static void
cc_power_panel_dispose (GObject *object)
{
  CcPowerPanelPrivate *priv = CC_POWER_PANEL (object)->priv;

  g_clear_object (&priv->gsd_settings);
  g_clear_object (&priv->session_settings);
  if (priv->cancellable != NULL)
    {
      g_cancellable_cancel (priv->cancellable);
      g_object_unref (priv->cancellable);
      priv->cancellable = NULL;
    }
  g_clear_object (&priv->builder);
  g_clear_object (&priv->screen_proxy);
  g_clear_object (&priv->up_client);
#ifdef HAVE_BLUETOOTH
  g_clear_object (&priv->bt_client);
#endif
#ifdef HAVE_NETWORK_MANAGER
  g_clear_object (&priv->nm_client);
#endif
  g_clear_pointer (&priv->boxes, g_list_free);
  g_clear_pointer (&priv->boxes_reverse, g_list_free);

  G_OBJECT_CLASS (cc_power_panel_parent_class)->dispose (object);
}

static const char *
cc_power_panel_get_help_uri (CcPanel *panel)
{
  return "help:gnome-help/power";
}

static void
cc_power_panel_class_init (CcPowerPanelClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  CcPanelClass *panel_class = CC_PANEL_CLASS (klass);

  g_type_class_add_private (klass, sizeof (CcPowerPanelPrivate));

  object_class->dispose = cc_power_panel_dispose;

  panel_class->get_help_uri = cc_power_panel_get_help_uri;
}

static gchar *
get_timestring (guint64 time_secs)
{
  gchar* timestring = NULL;
  gint  hours;
  gint  minutes;

  /* Add 0.5 to do rounding */
  minutes = (int) ( ( time_secs / 60.0 ) + 0.5 );

  if (minutes == 0)
    {
      timestring = g_strdup (_("Unknown time"));
      return timestring;
    }

  if (minutes < 60)
    {
      timestring = g_strdup_printf (ngettext ("%i minute",
                                    "%i minutes",
                                    minutes), minutes);
      return timestring;
    }

  hours = minutes / 60;
  minutes = minutes % 60;

  if (minutes == 0)
    {
      timestring = g_strdup_printf (ngettext (
                                    "%i hour",
                                    "%i hours",
                                    hours), hours);
      return timestring;
    }

  /* TRANSLATOR: "%i %s %i %s" are "%i hours %i minutes"
   * Swap order with "%2$s %2$i %1$s %1$i if needed */
  timestring = g_strdup_printf (_("%i %s %i %s"),
                                hours, ngettext ("hour", "hours", hours),
                                minutes, ngettext ("minute", "minutes", minutes));
  return timestring;
}

static gchar *
get_details_string (gdouble percentage, UpDeviceState state, guint64 time)
{
  gchar *details;

  if (time > 0)
    {
      gchar *time_string;

      time_string = get_timestring (time);
      switch (state)
        {
          case UP_DEVICE_STATE_CHARGING:
          case UP_DEVICE_STATE_PENDING_CHARGE:
            /* TRANSLATORS: %1 is a time string, e.g. "1 hour 5 minutes" */
            details = g_strdup_printf (_("%s until fully charged"), time_string);
            break;
          case UP_DEVICE_STATE_DISCHARGING:
          case UP_DEVICE_STATE_PENDING_DISCHARGE:
            if (percentage < 20)
              {
                /* TRANSLATORS: %1 is a time string, e.g. "1 hour 5 minutes" */
                details = g_strdup_printf (_("Caution: %s remaining"), time_string);
              }
            else
              {
                /* TRANSLATORS: %1 is a time string, e.g. "1 hour 5 minutes" */
                details = g_strdup_printf (_("%s remaining"), time_string);
              }
            break;
          case UP_DEVICE_STATE_FULLY_CHARGED:
            /* TRANSLATORS: primary battery */
            details = g_strdup (_("Fully charged"));
            break;
          case UP_DEVICE_STATE_EMPTY:
            /* TRANSLATORS: primary battery */
            details = g_strdup (_("Empty"));
            break;
          default:
            details = g_strdup_printf ("error: %s", up_device_state_to_string (state));
            break;
        }
      g_free (time_string);
    }
  else
    {
      switch (state)
        {
          case UP_DEVICE_STATE_CHARGING:
          case UP_DEVICE_STATE_PENDING_CHARGE:
            /* TRANSLATORS: primary battery */
            details = g_strdup (_("Charging"));
            break;
          case UP_DEVICE_STATE_DISCHARGING:
          case UP_DEVICE_STATE_PENDING_DISCHARGE:
            /* TRANSLATORS: primary battery */
            details = g_strdup (_("Discharging"));
            break;
          case UP_DEVICE_STATE_FULLY_CHARGED:
            /* TRANSLATORS: primary battery */
            details = g_strdup (_("Fully charged"));
            break;
          case UP_DEVICE_STATE_EMPTY:
            /* TRANSLATORS: primary battery */
            details = g_strdup (_("Empty"));
            break;
          default:
            details = g_strdup_printf ("error: %s",
                                       up_device_state_to_string (state));
            break;
        }
    }

  return details;
}

static void
set_primary (CcPowerPanel *panel, UpDevice *device)
{
  CcPowerPanelPrivate *priv = panel->priv;
  gchar *details = NULL;
  gdouble percentage;
  guint64 time_empty, time_full, time;
  UpDeviceState state;
  GtkWidget *box, *box2, *label;
  GtkWidget *levelbar;
  gchar *s;
  gdouble energy_full, energy_rate;
  guint64 capacity;

  g_object_get (device,
                "state", &state,
                "percentage", &percentage,
                "time-to-empty", &time_empty,
                "time-to-full", &time_full,
                "energy-full", &energy_full,
                "energy-rate", &energy_rate,
                NULL);
  if (state == UP_DEVICE_STATE_DISCHARGING)
    time = time_empty;
  else
    time = time_full;

  if (energy_rate > 0)
    {
      gchar *time_string, *s;
      capacity = 3600 * (energy_full / energy_rate);
      time_string = get_timestring (capacity);
      s = g_strdup_printf (_("Estimated battery capacity: %s"), time_string);
      gtk_label_set_label (GTK_LABEL (priv->battery_capacity), s);
      gtk_widget_show (priv->battery_capacity);
      g_free (s);
      g_free (time_string);
    }
  else
    {
      gtk_widget_hide (priv->battery_capacity);
    }

  details = get_details_string (percentage, state, time);

  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_size_group_add_widget (priv->battery_sizegroup, box);
  gtk_widget_set_margin_left (box, 20);
  gtk_widget_set_margin_right (box, 20);
  gtk_widget_set_margin_top (box, 6);
  gtk_widget_set_margin_bottom (box, 6);

  levelbar = gtk_level_bar_new ();
  gtk_level_bar_set_value (GTK_LEVEL_BAR (levelbar), percentage / 100.0);
  gtk_widget_set_hexpand (levelbar, TRUE);
  gtk_widget_set_halign (levelbar, GTK_ALIGN_FILL);
  gtk_widget_set_valign (levelbar, GTK_ALIGN_CENTER);
  gtk_box_pack_start (GTK_BOX (box), levelbar, TRUE, TRUE, 0);

  box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);
  gtk_box_pack_start (GTK_BOX (box), box2, FALSE, TRUE, 0);

  label = gtk_label_new (details);
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (box2), label, TRUE, TRUE, 0);

  s = g_strdup_printf ("%d%%", (int)(percentage + 0.5));
  label = gtk_label_new (s);
  g_free (s);
  gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
  gtk_style_context_add_class (gtk_widget_get_style_context (label), GTK_STYLE_CLASS_DIM_LABEL);
  gtk_box_pack_start (GTK_BOX (box2), label, FALSE, TRUE, 0);

  gtk_container_add (GTK_CONTAINER (priv->battery_list), box);
  gtk_size_group_add_widget (priv->row_sizegroup, box);
  gtk_widget_show_all (box);

  g_object_set_data (G_OBJECT (box), "primary", GINT_TO_POINTER (TRUE));

  g_free (details);

  gtk_widget_set_visible (priv->battery_section, TRUE);
}

static void
add_battery (CcPowerPanel *panel, UpDevice *device)
{
  CcPowerPanelPrivate *priv = panel->priv;
  gdouble percentage;
  UpDeviceKind kind;
  UpDeviceState state;
  GtkWidget *box;
  GtkWidget *box2;
  GtkWidget *label;
  GtkWidget *levelbar;
  GtkWidget *widget;
  gchar *s;
  gchar *native_path;
  const gchar *name;

  g_object_get (device,
                "kind", &kind,
                "state", &state,
                "percentage", &percentage,
                "native-path", &native_path,
                NULL);

  if (native_path && strstr (native_path, "BAT0"))
    name = C_("Battery name", "Main");
  else
    name = C_("Battery name", "Extra");

  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  label = gtk_label_new (name);
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_size_group_add_widget (priv->battery_sizegroup, box2);
  gtk_widget_set_margin_left (label, 20);
  gtk_widget_set_margin_right (label, 20);
  gtk_widget_set_margin_top (label, 6);
  gtk_widget_set_margin_bottom (label, 6);
  gtk_box_pack_start (GTK_BOX (box2), label, FALSE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (box), box2, FALSE, TRUE, 0);

#if 1
  if (state == UP_DEVICE_STATE_DISCHARGING ||
      state == UP_DEVICE_STATE_CHARGING)
    {
      widget = gtk_image_new_from_icon_name ("battery-good-charging-symbolic", GTK_ICON_SIZE_BUTTON);
      gtk_style_context_add_class (gtk_widget_get_style_context (widget), GTK_STYLE_CLASS_DIM_LABEL);
      gtk_widget_set_halign (widget, GTK_ALIGN_END);
      gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
      gtk_box_pack_start (GTK_BOX (box2), widget, TRUE, TRUE, 0);
    }
#endif

  box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
  gtk_widget_set_margin_left (box2, 20);
  gtk_widget_set_margin_right (box2, 20);

  s = g_strdup_printf ("%d%%", (int)percentage);
  label = gtk_label_new (s);
  g_free (s);
  gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
  gtk_style_context_add_class (gtk_widget_get_style_context (label), GTK_STYLE_CLASS_DIM_LABEL);
  gtk_box_pack_start (GTK_BOX (box2), label, FALSE, TRUE, 0);
  gtk_size_group_add_widget (priv->charge_sizegroup, label);

  levelbar = gtk_level_bar_new ();
  gtk_level_bar_set_value (GTK_LEVEL_BAR (levelbar), percentage / 100.0);
  gtk_widget_set_hexpand (levelbar, TRUE);
  gtk_widget_set_halign (levelbar, GTK_ALIGN_FILL);
  gtk_widget_set_valign (levelbar, GTK_ALIGN_CENTER);
  gtk_box_pack_start (GTK_BOX (box2), levelbar, TRUE, TRUE, 0);
  gtk_size_group_add_widget (priv->level_sizegroup, levelbar);
  gtk_box_pack_start (GTK_BOX (box), box2, TRUE, TRUE, 0);

  g_object_set_data (G_OBJECT (box), "kind", GINT_TO_POINTER (kind));
  gtk_container_add (GTK_CONTAINER (priv->battery_list), box);
  gtk_size_group_add_widget (priv->row_sizegroup, box);
  gtk_widget_show_all (box);

  g_free (native_path);

  gtk_widget_set_visible (priv->battery_section, TRUE);
}

static void
add_device (CcPowerPanel *panel, UpDevice *device)
{
  CcPowerPanelPrivate *priv = panel->priv;
  UpDeviceKind kind;
  UpDeviceState state;
  GtkWidget *hbox;
  GtkWidget *box2;
  GtkWidget *widget;
  GString *status;
  GString *description;
  gdouble percentage;
  gchar *s;
  gboolean show_caution = FALSE;

  g_object_get (device,
                "kind", &kind,
                "percentage", &percentage,
                "state", &state,
                NULL);

  switch (kind)
    {
      case UP_DEVICE_KIND_MOUSE:
        /* TRANSLATORS: secondary battery */
        description = g_string_new (_("Wireless mouse"));
        break;
      case UP_DEVICE_KIND_KEYBOARD:
        /* TRANSLATORS: secondary battery */
        description = g_string_new (_("Wireless keyboard"));
        break;
      case UP_DEVICE_KIND_UPS:
        /* TRANSLATORS: secondary battery */
        description = g_string_new (_("Uninterruptible power supply"));
        show_caution = TRUE;
        break;
      case UP_DEVICE_KIND_PDA:
        /* TRANSLATORS: secondary battery */
        description = g_string_new (_("Personal digital assistant"));
        break;
      case UP_DEVICE_KIND_PHONE:
        /* TRANSLATORS: secondary battery */
        description = g_string_new (_("Cellphone"));
        break;
      case UP_DEVICE_KIND_MEDIA_PLAYER:
        /* TRANSLATORS: secondary battery */
        description = g_string_new (_("Media player"));
        break;
      case UP_DEVICE_KIND_TABLET:
        /* TRANSLATORS: secondary battery */
        description = g_string_new (_("Tablet"));
        break;
      case UP_DEVICE_KIND_COMPUTER:
        /* TRANSLATORS: secondary battery */
        description = g_string_new (_("Computer"));
        break;
      default:
        /* TRANSLATORS: secondary battery, misc */
        description = g_string_new (_("Battery"));
        break;
    }

  switch (state)
    {
      case UP_DEVICE_STATE_CHARGING:
      case UP_DEVICE_STATE_PENDING_CHARGE:
        /* TRANSLATORS: secondary battery */
        status = g_string_new(C_("Battery power", "Charging"));
        break;
      case UP_DEVICE_STATE_DISCHARGING:
      case UP_DEVICE_STATE_PENDING_DISCHARGE:
        if (percentage < 10 && show_caution)
          {
            /* TRANSLATORS: secondary battery */
            status = g_string_new (C_("Battery power", "Caution"));
          }
        else if (percentage < 30)
          {
            /* TRANSLATORS: secondary battery */
            status = g_string_new (C_("Battery power", "Low"));
          }
        else
          {
            /* TRANSLATORS: secondary battery */
            status = g_string_new (C_("Battery power", "Good"));
          }
        break;
      case UP_DEVICE_STATE_FULLY_CHARGED:
        /* TRANSLATORS: primary battery */
        status = g_string_new (C_("Battery power", "Fully charged"));
        break;
      case UP_DEVICE_STATE_EMPTY:
        /* TRANSLATORS: primary battery */
        status = g_string_new (C_("Battery power", "Empty"));
        break;
      default:
        status = g_string_new (up_device_state_to_string (state));
        break;
    }
  g_string_prepend (status, "<small>");
  g_string_append (status, "</small>");

  /* create the new widget */
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  widget = gtk_label_new ("");
  gtk_misc_set_alignment (GTK_MISC (widget), 0.0f, 0.5f);
  gtk_label_set_markup (GTK_LABEL (widget), description->str);
  gtk_widget_set_margin_left (widget, 20);
  gtk_widget_set_margin_right (widget, 20);
  gtk_widget_set_margin_top (widget, 6);
  gtk_widget_set_margin_bottom (widget, 6);
  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, TRUE, 0);
  gtk_size_group_add_widget (priv->battery_sizegroup, widget);

  box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
  gtk_widget_set_margin_left (box2, 20);
  gtk_widget_set_margin_right (box2, 20);
  s = g_strdup_printf ("%d%%", (int)percentage);
  widget = gtk_label_new (s);
  g_free (s);
  gtk_misc_set_alignment (GTK_MISC (widget), 1, 0.5);
  gtk_style_context_add_class (gtk_widget_get_style_context (widget), GTK_STYLE_CLASS_DIM_LABEL);
  gtk_box_pack_start (GTK_BOX (box2), widget, FALSE, TRUE, 0);
  gtk_size_group_add_widget (priv->charge_sizegroup, widget);

  widget = gtk_level_bar_new ();
  gtk_widget_set_halign (widget, TRUE);
  gtk_widget_set_halign (widget, GTK_ALIGN_FILL);
  gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
  gtk_level_bar_set_value (GTK_LEVEL_BAR (widget), percentage / 100.0f);
  gtk_box_pack_start (GTK_BOX (box2), widget, TRUE, TRUE, 0);
  gtk_size_group_add_widget (priv->level_sizegroup, widget);
  gtk_box_pack_start (GTK_BOX (hbox), box2, TRUE, TRUE, 0);
  gtk_widget_show_all (hbox);

  gtk_container_add (GTK_CONTAINER (priv->device_list), hbox);
  gtk_size_group_add_widget (priv->row_sizegroup, hbox);
  g_object_set_data (G_OBJECT (hbox), "kind", GINT_TO_POINTER (kind));

  g_string_free (description, TRUE);
  g_string_free (status, TRUE);

  gtk_widget_set_visible (priv->device_section, TRUE);
}

static void
up_client_changed (UpClient     *client,
                   UpDevice     *device,
                   CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;
  GPtrArray *devices;
  GList *children, *l;
  gint i;
  UpDeviceKind kind;
  UpDeviceState state;
  guint n_batteries;
  gboolean on_ups;
  UpDevice *composite;
  gdouble percentage = 0.0;
  gdouble energy = 0.0;
  gdouble energy_full = 0.0;
  gdouble energy_rate = 0.0;
  gdouble energy_total = 0.0;
  gdouble energy_full_total = 0.0;
  gdouble energy_rate_total = 0.0;
  gint64 time_to_empty = 0;
  gint64 time_to_full = 0;
  gboolean is_charging = FALSE;
  gboolean is_discharging = FALSE;
  gboolean is_fully_charged = TRUE;
  gchar *s;

  children = gtk_container_get_children (GTK_CONTAINER (priv->battery_list));
  for (l = children; l != NULL; l = l->next)
    gtk_container_remove (GTK_CONTAINER (priv->battery_list), l->data);
  g_list_free (children);
  gtk_widget_hide (priv->battery_section);

  children = gtk_container_get_children (GTK_CONTAINER (priv->device_list));
  for (l = children; l != NULL; l = l->next)
    gtk_container_remove (GTK_CONTAINER (priv->device_list), l->data);
  g_list_free (children);
  gtk_widget_hide (priv->device_section);

  devices = up_client_get_devices (client);

#ifdef TEST_FAKE_DEVICES
  {
    static gboolean fake_devices_added = FALSE;

    if (!fake_devices_added)
      {
        fake_devices_added = TRUE;
        g_print ("adding fake devices\n");
        device = up_device_new ();
        g_object_set (device,
                      "kind", UP_DEVICE_KIND_MOUSE,
                      "percentage", 71.0,
                      "state", UP_DEVICE_STATE_DISCHARGING,
                      "time-to-empty", 287,
                      NULL);
        g_ptr_array_add (devices, device);
        device = up_device_new ();
        g_object_set (device,
                      "kind", UP_DEVICE_KIND_KEYBOARD,
                      "percentage", 69.0,
                      "state", UP_DEVICE_STATE_DISCHARGING,
                      "time-to-empty", 250,
                      NULL);
        g_ptr_array_add (devices, device);
        device = up_device_new ();
        g_object_set (device,
                      "kind", UP_DEVICE_KIND_BATTERY,
                      "percentage", 100.0,
                      "state", UP_DEVICE_STATE_FULLY_CHARGED,
                      "energy", 55.0,
                      "energy-full", 55.0,
                      "energy-rate", 15.0,
                      "time-to-empty", 400,
                      NULL);
        g_ptr_array_add (devices, device);
      }
  }
#endif

  on_ups = FALSE;
  n_batteries = 0;
  composite = up_device_new ();
  g_object_set (composite,
                "kind", UP_DEVICE_KIND_BATTERY,
                "is-rechargeable", TRUE,
                "native-path", "dummy:composite_battery",
                "power-supply", TRUE,
                "is-present", TRUE,
                NULL);
  for (i = 0; i < devices->len; i++)
    {
      UpDevice *device = (UpDevice*) g_ptr_array_index (devices, i);
      g_object_get (device,
                    "kind", &kind,
                    "state", &state,
                    "energy", &energy,
                    "energy-full", &energy_full,
                    "energy-rate", &energy_rate,
                    NULL);
      if (kind == UP_DEVICE_KIND_UPS && state == UP_DEVICE_STATE_DISCHARGING)
        on_ups = TRUE;
      if (kind == UP_DEVICE_KIND_BATTERY)
        {
          if (state == UP_DEVICE_STATE_CHARGING)
            is_charging = TRUE;
          if (state == UP_DEVICE_STATE_DISCHARGING)
            is_discharging = TRUE;
          if (state != UP_DEVICE_STATE_FULLY_CHARGED)
            is_fully_charged = FALSE;
          energy_total += energy;
          energy_full_total += energy_full;
          energy_rate_total += energy_rate;
          n_batteries++;
        }
    }

  if (n_batteries > 1)
    s = g_strdup_printf ("<b>%s</b>", _("Batteries"));
  else
    s = g_strdup_printf ("<b>%s</b>", _("Battery"));
  gtk_label_set_label (GTK_LABEL (priv->battery_heading), s);
  g_free (s);

  if (energy_full_total > 0.0)
    percentage = 100.0 * energy_total / energy_full_total;

  if (is_charging)
    state = UP_DEVICE_STATE_CHARGING;
  else if (is_discharging)
    state = UP_DEVICE_STATE_DISCHARGING;
  else if (is_fully_charged)
    state = UP_DEVICE_STATE_FULLY_CHARGED;
  else
    state = UP_DEVICE_STATE_UNKNOWN;

  if (energy_rate_total > 0)
    {
      if (state == UP_DEVICE_STATE_DISCHARGING)
        time_to_empty = 3600 * (energy_total / energy_rate_total);
      else if (state == UP_DEVICE_STATE_CHARGING)
        time_to_full = 3600 * ((energy_full_total - energy_total) / energy_rate_total);
    }

  g_object_set (composite,
                "energy", energy_total,
                "energy-full", energy_full_total,
                "energy-rate", energy_rate_total,
                "time-to-empty", time_to_empty,
                "time-to-full", time_to_full,
                "percentage", percentage,
                "state", state,
                NULL);

  if (!on_ups && n_batteries > 1)
    set_primary (self, composite);

  for (i = 0; i < devices->len; i++)
    {
      UpDevice *device = (UpDevice*) g_ptr_array_index (devices, i);
      g_object_get (device, "kind", &kind, NULL);
      if (kind == UP_DEVICE_KIND_LINE_POWER)
        {
          /* do nothing */
        }
      else if (kind == UP_DEVICE_KIND_UPS && on_ups)
        {
          set_primary (self, device);
        }
      else if (kind == UP_DEVICE_KIND_BATTERY && !on_ups && n_batteries == 1)
        {
          set_primary (self, device);
        }
      else if (kind == UP_DEVICE_KIND_BATTERY)
        {
          add_battery (self, device);
        }
      else
        {
          add_device (self, device);
        }
    }

  g_ptr_array_unref (devices);
  g_object_unref (composite);
}

static void
set_brightness_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
  GError *error = NULL;
  GVariant *result;
  CcPowerPanelPrivate *priv = CC_POWER_PANEL (user_data)->priv;

  /* not setting, so pay attention to changed signals */
  priv->setting_brightness = FALSE;
  result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
  if (result == NULL)
    {
      g_printerr ("Error setting brightness: %s\n", error->message);
      g_error_free (error);
      return;
    }
}

static void
brightness_slider_value_changed_cb (GtkRange *range, gpointer user_data)
{
  guint percentage;
  CcPowerPanelPrivate *priv = CC_POWER_PANEL (user_data)->priv;

  /* do not loop */
  if (priv->setting_brightness)
    return;

  priv->setting_brightness = TRUE;

  /* push this to g-s-d */
  percentage = (guint) gtk_range_get_value (range);
  g_dbus_proxy_call (priv->screen_proxy,
                     "SetPercentage",
                     g_variant_new ("(u)",
                                    percentage),
                     G_DBUS_CALL_FLAGS_NONE,
                     -1,
                     priv->cancellable,
                     set_brightness_cb,
                     user_data);
}

static void
get_brightness_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
  CcPowerPanel *self = CC_POWER_PANEL (user_data);
  GError *error = NULL;
  GVariant *result;
  guint brightness;
  GtkRange *range;

  result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
  if (result == NULL)
    {
      /* We got cancelled, so we're probably exiting */
      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
        {
          g_error_free (error);
          return;
        }

      gtk_widget_hide (self->priv->brightness_row);
      gtk_widget_hide (self->priv->dim_screen_row);

      if (error->message &&
          strstr (error->message, "No backlight devices present") == NULL)
        {
          g_warning ("Error getting brightness: %s", error->message);
        }
      g_error_free (error);
      return;
    }

  /* set the slider */
  g_variant_get (result, "(u)", &brightness);
  range = GTK_RANGE (self->priv->brightness_scale);
  gtk_range_set_range (range, 0, 100);
  gtk_range_set_increments (range, 1, 10);
  gtk_range_set_value (range, brightness);
  g_signal_connect (range, "value-changed",
                    G_CALLBACK (brightness_slider_value_changed_cb), user_data);
  g_variant_unref (result);
}

static void
on_signal (GDBusProxy *proxy,
           gchar      *sender_name,
           gchar      *signal_name,
           GVariant   *parameters,
           gpointer    user_data)
{
  CcPowerPanel *self = CC_POWER_PANEL (user_data);

  if (g_strcmp0 (signal_name, "Changed") == 0)
    {
      /* changed, but ignoring */
      if (self->priv->setting_brightness)
        return;

      /* retrieve the value again from g-s-d */
      g_dbus_proxy_call (self->priv->screen_proxy,
                         "GetPercentage",
                         NULL,
                         G_DBUS_CALL_FLAGS_NONE,
                         200, /* we don't want to randomly move the bar */
                         self->priv->cancellable,
                         get_brightness_cb,
                         user_data);
    }
}

static void
got_screen_proxy_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
  GError *error = NULL;
  CcPowerPanelPrivate *priv = CC_POWER_PANEL (user_data)->priv;

  priv->screen_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
  if (priv->screen_proxy == NULL)
    {
      g_printerr ("Error creating proxy: %s\n", error->message);
      g_error_free (error);
      return;
    }

  /* we want to change the bar if the user presses brightness buttons */
  g_signal_connect (priv->screen_proxy, "g-signal",
                    G_CALLBACK (on_signal), user_data);

  /* get the initial state */
  g_dbus_proxy_call (priv->screen_proxy,
                     "GetPercentage",
                     NULL,
                     G_DBUS_CALL_FLAGS_NONE,
                     200, /* we don't want to randomly move the bar */
                     priv->cancellable,
                     get_brightness_cb,
                     user_data);
}

static void
combo_time_changed_cb (GtkWidget *widget, CcPowerPanel *self)
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  gint value;
  gboolean ret;
  const gchar *key = (const gchar *)g_object_get_data (G_OBJECT(widget), "_gsettings_key");

  /* no selection */
  ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX(widget), &iter);
  if (!ret)
    return;

  /* get entry */
  model = gtk_combo_box_get_model (GTK_COMBO_BOX(widget));
  gtk_tree_model_get (model, &iter,
                      1, &value,
                      -1);

  /* set both keys */
  g_settings_set_int (self->priv->gsd_settings, key, value);
}

static void
combo_enum_changed_cb (GtkWidget *widget, CcPowerPanel *self)
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  gint value;
  gboolean ret;
  const gchar *key = (const gchar *)g_object_get_data (G_OBJECT(widget), "_gsettings_key");

  /* no selection */
  ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX(widget), &iter);
  if (!ret)
    return;

  /* get entry */
  model = gtk_combo_box_get_model (GTK_COMBO_BOX(widget));
  gtk_tree_model_get (model, &iter,
                      1, &value,
                      -1);

  /* set both battery and ac keys */
  g_settings_set_enum (self->priv->gsd_settings, key, value);
}

static void
set_value_for_combo (GtkComboBox *combo_box, gint value)
{
  GtkTreeIter iter;
  GtkTreeIter last;
  GtkTreeModel *model;
  gint value_tmp;
  gboolean ret;

  /* get entry */
  model = gtk_combo_box_get_model (combo_box);
  ret = gtk_tree_model_get_iter_first (model, &iter);
  if (!ret)
    return;

  /* try to make the UI match the setting */
  do
    {
      gtk_tree_model_get (model, &iter,
                          ACTION_MODEL_VALUE, &value_tmp,
                          -1);
      if (value == value_tmp)
        {
          gtk_combo_box_set_active_iter (combo_box, &iter);
          return;
        }
      last = iter;
    } while (gtk_tree_model_iter_next (model, &iter));

  gtk_combo_box_set_active_iter (combo_box, &last);
}

static void
set_ac_battery_ui_mode (CcPowerPanel *self)
{
  gboolean has_batteries = FALSE;
  gboolean ret;
  GError *error = NULL;
  GPtrArray *devices;
  guint i;
  UpDevice *device;
  UpDeviceKind kind;

  /* this is sync, but it's cached in the daemon and so quick */
  ret = up_client_enumerate_devices_sync (self->priv->up_client, NULL, &error);
  if (!ret)
    {
      g_warning ("failed to get device list: %s", error->message);
      g_error_free (error);
      goto out;
    }

  devices = up_client_get_devices (self->priv->up_client);
  g_debug ("got %d devices from upower\n", devices->len);

  for (i = 0; i < devices->len; i++)
    {
      device = g_ptr_array_index (devices, i);
      g_object_get (device, "kind", &kind, NULL);
      if (kind == UP_DEVICE_KIND_BATTERY || kind == UP_DEVICE_KIND_UPS)
        {
          has_batteries = TRUE;
          break;
        }
    }
  g_ptr_array_unref (devices);

#ifdef TEST_NO_BATTERIES
  g_print ("forcing no batteries\n");
  has_batteries = FALSE;
#endif

out:
  self->priv->has_batteries = has_batteries;

  gtk_widget_set_visible (self->priv->critical_battery_row, has_batteries);

  if (!has_batteries)
    {
      gtk_widget_hide (WID (self->priv->builder, "suspend_on_battery_switch"));
      gtk_widget_hide (WID (self->priv->builder, "suspend_on_battery_label"));
      gtk_widget_hide (WID (self->priv->builder, "suspend_on_battery_delay_label"));
      gtk_widget_hide (WID (self->priv->builder, "suspend_on_battery_delay_combo"));
      gtk_label_set_label (GTK_LABEL (WID (self->priv->builder, "suspend_on_ac_label")),
                           _("When _idle"));
    }
}

static void
update_separator_func (GtkWidget **separator,
                       GtkWidget  *child,
                       GtkWidget  *before,
                       gpointer    user_data)
{
  if (before == NULL)
    return;

  if (*separator == NULL)
    {
      *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
      g_object_ref_sink (*separator);
      gtk_widget_show (*separator);
    }
}

#ifdef HAVE_BLUETOOTH
static void
bt_set_powered (BluetoothClient *client,
                gboolean         powered)
{
  GVariant *ret;
  const gchar *adapter_path;
  GDBusConnection *bus;

  bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);

  ret = g_dbus_connection_call_sync (bus,
                                    "org.bluez",
                                    "/",
                                    "org.bluez.Manager",
                                    "DefaultAdapter",
                                    NULL,
                                    NULL,
                                    0,
                                    G_MAXINT,
                                    NULL, NULL);
  g_variant_get (ret, "(&o)", &adapter_path);

  g_dbus_connection_call (bus,
                          "org.bluez",
                          adapter_path,
                          "org.freedesktop.Properties",
                          "SetProperty",
                          g_variant_new ("(sv)", "Powered", g_variant_new_boolean (powered)),
                          NULL,
                          0,
                          G_MAXINT,
                          NULL, NULL, NULL);

  g_variant_unref (ret);
}

static void
bt_switch_changed (GtkSwitch    *sw,
                   GParamSpec   *pspec,
                   CcPowerPanel *panel)
{
  gboolean powered;

  powered = gtk_switch_get_active (sw);

  g_debug ("Setting bt power %s", powered ? "on" : "off");

  bt_set_powered (panel->priv->bt_client, powered);
}

static void
bt_powered_state_changed (GObject      *client,
                          GParamSpec   *pspec,
                          CcPowerPanel *panel)
{
  CcPowerPanelPrivate *priv = panel->priv;
  gboolean powered;

  g_object_get (client, "default-adapter-powered", &powered, NULL);
  g_debug ("bt powered state changed to %s", powered ? "on" : "off");

  g_signal_handlers_block_by_func (priv->bt_switch, bt_switch_changed, panel);
  gtk_switch_set_active (GTK_SWITCH (priv->bt_switch), powered);
  g_signal_handlers_unblock_by_func (priv->bt_switch, bt_switch_changed, panel);
}
#endif

#ifdef HAVE_NETWORK_MANAGER
static gboolean
has_wifi_devices (NMClient *client)
{
  const GPtrArray *devices;
  NMDevice *device;
  gint i;

  if (!nm_client_get_manager_running (client))
    return FALSE;

  devices = nm_client_get_devices (client);
  if (devices == NULL)
    return FALSE;

  for (i = 0; i < devices->len; i++)
    {
      device = g_ptr_array_index (devices, i);
      switch (nm_device_get_device_type (device))
        {
        case NM_DEVICE_TYPE_WIFI:
          return TRUE;
        default:
          break;
        }
    }

  return FALSE;
}

static void
wifi_switch_changed (GtkSwitch    *sw,
                     GParamSpec   *pspec,
                     CcPowerPanel *panel)
{
  gboolean enabled;

  enabled = gtk_switch_get_active (sw);
  g_debug ("Setting wifi %s", enabled ? "enabled" : "disabled");
  nm_client_wireless_set_enabled (panel->priv->nm_client, enabled);
}

static gboolean
has_mobile_devices (NMClient *client)
{
  const GPtrArray *devices;
  NMDevice *device;
  gint i;

  if (!nm_client_get_manager_running (client))
    return FALSE;

  devices = nm_client_get_devices (client);
  if (devices == NULL)
    return FALSE;

  for (i = 0; i < devices->len; i++)
    {
      device = g_ptr_array_index (devices, i);
      switch (nm_device_get_device_type (device))
        {
        case NM_DEVICE_TYPE_WIMAX:
        case NM_DEVICE_TYPE_MODEM:
          return TRUE;
        default:
          break;
        }
    }

  return FALSE;
}

static void
mobile_switch_changed (GtkSwitch    *sw,
                       GParamSpec   *pspec,
                       CcPowerPanel *panel)
{
  gboolean enabled;

  enabled = gtk_switch_get_active (sw);
  g_debug ("Setting wwan %s", enabled ? "enabled" : "disabled");
  nm_client_wwan_set_enabled (panel->priv->nm_client, enabled);
  g_debug ("Setting wimax %s", enabled ? "enabled" : "disabled");
  nm_client_wimax_set_enabled (panel->priv->nm_client, enabled);
}

static void
nm_client_state_changed (NMClient     *client,
                         GParamSpec   *pspec,
                         CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;
  gboolean visible;
  gboolean active;
  gboolean sensitive;

  visible = has_wifi_devices (priv->nm_client);
  active = nm_client_networking_get_enabled (client) &&
           nm_client_wireless_get_enabled (client) &&
           nm_client_wireless_hardware_get_enabled (client);
  sensitive = nm_client_networking_get_enabled (client) &&
              nm_client_wireless_hardware_get_enabled (client);

  g_debug ("wifi state changed to %s", active ? "enabled" : "disabled");

  g_signal_handlers_block_by_func (priv->wifi_switch, wifi_switch_changed, self);
  gtk_switch_set_active (GTK_SWITCH (priv->wifi_switch), active);
  gtk_widget_set_sensitive (priv->wifi_switch, sensitive);
  gtk_widget_set_visible (priv->wifi_row, visible);
  g_signal_handlers_unblock_by_func (priv->wifi_switch, wifi_switch_changed, self);

  visible = has_mobile_devices (priv->nm_client);
  active = nm_client_networking_get_enabled (client) &&
           nm_client_wimax_get_enabled (client) &&
           nm_client_wireless_hardware_get_enabled (client);
  sensitive = nm_client_networking_get_enabled (client) &&
              nm_client_wireless_hardware_get_enabled (client);

  g_debug ("mobile state changed to %s", active ? "enabled" : "disabled");

  g_signal_handlers_block_by_func (priv->mobile_switch, mobile_switch_changed, self);
  gtk_switch_set_active (GTK_SWITCH (priv->mobile_switch), active);
  gtk_widget_set_sensitive (priv->mobile_switch, sensitive);
  gtk_widget_set_visible (priv->mobile_row, visible);
  g_signal_handlers_unblock_by_func (priv->mobile_switch, mobile_switch_changed, self);
}

static void
nm_device_changed (NMClient     *client,
                   NMDevice     *device,
                   CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;

  gtk_widget_set_visible (priv->wifi_row, has_wifi_devices (priv->nm_client));
  gtk_widget_set_visible (priv->mobile_row, has_mobile_devices (priv->nm_client));
}

#endif

static gboolean
keynav_failed (GtkWidget *list, GtkDirectionType direction, CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;
  GtkWidget *next_list = NULL;
  GList *item, *boxes_list;
  gdouble value, lower, upper, page;

  /* Find the list in the list of EggListBoxes */
  if (direction == GTK_DIR_DOWN)
    boxes_list = priv->boxes;
  else
    boxes_list = priv->boxes_reverse;

  item = g_list_find (boxes_list, list);
  g_assert (item);
  item = item->next;
  while (1)
    {
      if (item == NULL)
        item = boxes_list;

      /* Avoid looping */
      if (item->data == list)
        break;

      if (gtk_widget_is_visible (item->data))
        {
          next_list = item->data;
          break;
        }

    item = item->next;
  }

  if (next_list)
    {
      gtk_widget_child_focus (next_list, direction);
      return TRUE;
    }

  value = gtk_adjustment_get_value (priv->focus_adjustment);
  lower = gtk_adjustment_get_lower (priv->focus_adjustment);
  upper = gtk_adjustment_get_upper (priv->focus_adjustment);
  page  = gtk_adjustment_get_page_size (priv->focus_adjustment);

  if (direction == GTK_DIR_UP && value > lower)
    {
      gtk_adjustment_set_value (priv->focus_adjustment, lower);
      return TRUE;
    }
  else if (direction == GTK_DIR_DOWN && value < upper - page)
    {
      gtk_adjustment_set_value (priv->focus_adjustment, upper - page);
      return TRUE;
    }

  return FALSE;
}

static void
combo_idle_delay_changed_cb (GtkWidget *widget, CcPowerPanel *self)
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  gint value;
  gboolean ret;

  /* no selection */
  ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX(widget), &iter);
  if (!ret)
    return;

  /* get entry */
  model = gtk_combo_box_get_model (GTK_COMBO_BOX(widget));
  gtk_tree_model_get (model, &iter,
                      1, &value,
                      -1);

  /* set both keys */
  g_settings_set_uint (self->priv->session_settings, "idle-delay", value);
}

static void
add_power_saving_section (CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;
  GtkWidget *vbox;
  GtkWidget *widget, *box, *label, *scale;
  GtkWidget *combo;
  GtkWidget *box2;
  GtkWidget *sw;
  GtkWidget *w;
  int value;
  gchar *s;

  vbox = WID (priv->builder, "vbox_power");

  s = g_strdup_printf ("<b>%s</b>", _("Power Saving"));
  widget = gtk_label_new (s);
  g_free (s);
  gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
  gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
  gtk_widget_set_margin_left (widget, 56);
  gtk_widget_set_margin_right (widget, 56);
  gtk_widget_set_margin_bottom (widget, 6);
  gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, TRUE, 0);
  gtk_widget_show (widget);

  widget = GTK_WIDGET (egg_list_box_new ());
  priv->boxes_reverse = g_list_prepend (priv->boxes_reverse, widget);
  g_signal_connect (widget, "keynav-failed", G_CALLBACK (keynav_failed), self);
  egg_list_box_set_selection_mode (EGG_LIST_BOX (widget), GTK_SELECTION_NONE);
  egg_list_box_set_separator_funcs (EGG_LIST_BOX (widget),
                                    update_separator_func,
                                    NULL, NULL);

  box = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_IN);
  gtk_widget_set_margin_left (box, 50);
  gtk_widget_set_margin_right (box, 50);
  gtk_widget_set_margin_bottom (box, 24);
  gtk_widget_show (box);
  gtk_container_add (GTK_CONTAINER (box), widget);
  gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, TRUE, 0);

  priv->brightness_row = box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  label = gtk_label_new (_("_Screen Brightness"));
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
  gtk_widget_set_margin_left (label, 20);
  gtk_widget_set_margin_right (label, 20);
  gtk_widget_set_margin_top (label, 6);
  gtk_widget_set_margin_bottom (label, 6);
  gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
  gtk_size_group_add_widget (priv->battery_sizegroup, label);
  box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
  w = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (box2), w, FALSE, TRUE, 0);
  gtk_size_group_add_widget (priv->charge_sizegroup, w);

  priv->brightness_scale = scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 100, 1);
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), scale);
  gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
  gtk_widget_set_margin_left (scale, 20);
  gtk_widget_set_margin_right (scale, 20);
  gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0);
  gtk_size_group_add_widget (priv->level_sizegroup, scale);

  gtk_box_pack_start (GTK_BOX (box), box2, TRUE, TRUE, 0);
  gtk_container_add (GTK_CONTAINER (widget), box);
  gtk_size_group_add_widget (priv->row_sizegroup, box);

  priv->dim_screen_row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);

  label = gtk_label_new (_("_Dim Screen when Inactive"));
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
  gtk_widget_set_margin_left (label, 20);
  gtk_widget_set_margin_right (label, 20);
  gtk_widget_set_margin_top (label, 6);
  gtk_widget_set_margin_bottom (label, 6);
  gtk_box_pack_start (GTK_BOX (priv->dim_screen_row), label, TRUE, TRUE, 0);

  sw = gtk_switch_new ();
  g_settings_bind (priv->gsd_settings, "idle-dim",
                   sw, "active",
                   G_SETTINGS_BIND_DEFAULT);
  gtk_widget_set_margin_left (sw, 20);
  gtk_widget_set_margin_right (sw, 20);
  gtk_widget_set_valign (sw, GTK_ALIGN_CENTER);
  gtk_box_pack_start (GTK_BOX (priv->dim_screen_row), sw, FALSE, TRUE, 0);
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), sw);
  gtk_container_add (GTK_CONTAINER (widget), priv->dim_screen_row);
  gtk_size_group_add_widget (priv->row_sizegroup, priv->dim_screen_row);

  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);

  label = gtk_label_new (_("_Mark As Inactive After"));
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
  gtk_widget_set_margin_left (label, 20);
  gtk_widget_set_margin_right (label, 20);
  gtk_widget_set_margin_top (label, 6);
  gtk_widget_set_margin_bottom (label, 6);
  gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);

  combo = gtk_combo_box_text_new ();
  gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo), 0);
  gtk_combo_box_set_model (GTK_COMBO_BOX (combo),
                           GTK_TREE_MODEL (gtk_builder_get_object (priv->builder, "liststore_idle_time")));
  value = g_settings_get_uint (priv->session_settings, "idle-delay");
  set_value_for_combo (GTK_COMBO_BOX (combo), value);
  g_signal_connect (combo, "changed",
                    G_CALLBACK (combo_idle_delay_changed_cb), self);
  gtk_widget_set_margin_left (combo, 20);
  gtk_widget_set_margin_right (combo, 20);
  gtk_widget_set_valign (combo, GTK_ALIGN_CENTER);
  gtk_box_pack_start (GTK_BOX (box), combo, FALSE, TRUE, 0);
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
  gtk_container_add (GTK_CONTAINER (widget), box);
  gtk_size_group_add_widget (priv->row_sizegroup, box);

#ifdef HAVE_NETWORK_MANAGER
  priv->wifi_row = box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);

  box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_widget_set_margin_left (box2, 20);
  gtk_widget_set_margin_right (box2, 20);
  gtk_widget_set_margin_top (box2, 6);
  gtk_widget_set_margin_bottom (box2, 6);
  gtk_box_pack_start (GTK_BOX (box), box2, TRUE, TRUE, 0);

  label = gtk_label_new (_("_Wi-Fi"));
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
  gtk_box_pack_start (GTK_BOX (box2), label, TRUE, TRUE, 0);

  w = gtk_label_new ("Turns off wireless devices");
  gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5);
  gtk_style_context_add_class (gtk_widget_get_style_context (w), GTK_STYLE_CLASS_DIM_LABEL);
  gtk_box_pack_start (GTK_BOX (box2), w, TRUE, TRUE, 0);

  priv->wifi_switch = sw = gtk_switch_new ();
  gtk_widget_set_margin_left (sw, 20);
  gtk_widget_set_margin_right (sw, 20);
  gtk_widget_set_valign (sw, GTK_ALIGN_CENTER);
  gtk_box_pack_start (GTK_BOX (box), sw, FALSE, TRUE, 0);
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), sw);
  gtk_container_add (GTK_CONTAINER (widget), box);
  gtk_size_group_add_widget (priv->row_sizegroup, box);

  priv->mobile_row = box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);

  box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_widget_set_margin_left (box2, 20);
  gtk_widget_set_margin_right (box2, 20);
  gtk_widget_set_margin_top (box2, 6);
  gtk_widget_set_margin_bottom (box2, 6);
  gtk_box_pack_start (GTK_BOX (box), box2, TRUE, TRUE, 0);

  label = gtk_label_new (_("_Mobile Broadband"));
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
  gtk_box_pack_start (GTK_BOX (box2), label, TRUE, TRUE, 0);

  w = gtk_label_new ("Turns off Mobile Broadband (3G, 4G, WiMax, etc.) devices");
  gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5);
  gtk_style_context_add_class (gtk_widget_get_style_context (w), GTK_STYLE_CLASS_DIM_LABEL);
  gtk_box_pack_start (GTK_BOX (box2), w, TRUE, TRUE, 0);

  priv->mobile_switch = sw = gtk_switch_new ();
  gtk_widget_set_margin_left (sw, 20);
  gtk_widget_set_margin_right (sw, 20);
  gtk_widget_set_valign (sw, GTK_ALIGN_CENTER);
  gtk_box_pack_start (GTK_BOX (box), sw, FALSE, TRUE, 0);
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), sw);
  gtk_container_add (GTK_CONTAINER (widget), box);
  gtk_size_group_add_widget (priv->row_sizegroup, box);

  g_signal_connect (G_OBJECT (priv->mobile_switch), "notify::active",
                    G_CALLBACK (mobile_switch_changed), self);

  priv->nm_client = nm_client_new ();
  g_signal_connect (priv->nm_client, "notify",
                    G_CALLBACK (nm_client_state_changed), self);
  g_signal_connect (priv->nm_client, "device-added",
                    G_CALLBACK (nm_device_changed), self);
  g_signal_connect (priv->nm_client, "device-removed",
                    G_CALLBACK (nm_device_changed), self);
  nm_device_changed (priv->nm_client, NULL, self);

  g_signal_connect (G_OBJECT (priv->wifi_switch), "notify::active",
                    G_CALLBACK (wifi_switch_changed), self);
#endif

#ifdef HAVE_BLUETOOTH
    priv->bt_client = bluetooth_client_new ();
    box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);
    label = gtk_label_new (_("_Bluetooth"));
    gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
    gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
    gtk_widget_set_margin_left (label, 20);
    gtk_widget_set_margin_right (label, 20);
    gtk_widget_set_margin_top (label, 6);
    gtk_widget_set_margin_bottom (label, 6);
    gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);

    priv->bt_switch = sw = gtk_switch_new ();
    gtk_widget_set_margin_left (sw, 20);
    gtk_widget_set_margin_right (sw, 20);
    gtk_widget_set_valign (sw, GTK_ALIGN_CENTER);
    gtk_box_pack_start (GTK_BOX (box), sw, FALSE, TRUE, 0);
    gtk_label_set_mnemonic_widget (GTK_LABEL (label), sw);
    gtk_container_add (GTK_CONTAINER (widget), box);
    gtk_size_group_add_widget (priv->row_sizegroup, box);
    g_signal_connect (G_OBJECT (priv->bt_client), "notify::default-adapter-powered",
                      G_CALLBACK (bt_powered_state_changed), self);
    g_signal_connect (G_OBJECT (priv->bt_switch), "notify::active",
                      G_CALLBACK (bt_switch_changed), self);
#endif

  gtk_widget_show_all (widget);
}

static void
update_automatic_suspend_label (CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;
  GsdPowerActionType ac_action;
  GsdPowerActionType battery_action;
  gint ac_timeout;
  gint battery_timeout;
  const gchar *s;

  ac_action = g_settings_get_enum (priv->gsd_settings, "sleep-inactive-ac-type");
  battery_action = g_settings_get_enum (priv->gsd_settings, "sleep-inactive-battery-type");
  ac_timeout = g_settings_get_int (priv->gsd_settings, "sleep-inactive-ac-timeout");
  battery_timeout = g_settings_get_int (priv->gsd_settings, "sleep-inactive-battery-timeout");

  if (ac_action == GSD_POWER_ACTION_NOTHING)
    ac_timeout = 0;
  if (battery_action == GSD_POWER_ACTION_NOTHING)
    battery_timeout = 0;

  if (priv->has_batteries)
    {
      if (ac_timeout == 0 && battery_timeout == 0)
        s = _("Off");
      else if (ac_timeout == 0 && battery_timeout != 0)
        s = _("When on battery power");
      else if (ac_timeout != 0 && battery_timeout == 0)
        s = _("When plugged in");
      else
        s = _("On");
    }
  else
    {
      if (ac_timeout == 0)
        s = _("Off");
      else
        s = _("On");
    }

  gtk_label_set_label (GTK_LABEL (priv->automatic_suspend_label), s);
}

static void
on_suspend_settings_changed (GSettings    *settings,
                             const char   *key,
                             CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;
  gint value;

  if (g_strcmp0 (key, "critical-battery-action") == 0)
    {
      value = g_settings_get_enum (settings, "critical-battery-action");
      set_value_for_combo (GTK_COMBO_BOX (priv->critical_battery_combo), value);
    }
  if (g_str_has_prefix (key, "sleep-inactive-"))
    {
      update_automatic_suspend_label (self);
    }
}

static void
activate_child (CcPowerPanel *self,
                GtkWidget    *child)
{
  CcPowerPanelPrivate *priv = self->priv;
  GtkWidget *w;
  GtkWidget *toplevel;

  if (child == priv->automatic_suspend_row)
    {
      w = WID (priv->builder, "automatic_suspend_dialog");
      toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
      gtk_window_set_transient_for (GTK_WINDOW (w), GTK_WINDOW (toplevel));
      gtk_window_set_modal (GTK_WINDOW (w), TRUE);
      gtk_window_present (GTK_WINDOW (w));
    }
}

static gboolean
automatic_suspend_activate (GtkWidget    *widget,
                            gboolean      cycle,
                            CcPowerPanel *self)
{
  activate_child (self, self->priv->automatic_suspend_row);
  return TRUE;
}

static gboolean
get_sleep_type (GValue   *value,
                GVariant *variant,
                gpointer  data)
{
  gboolean enabled;

  if (g_strcmp0 (g_variant_get_string (variant, NULL), "nothing") == 0)
    enabled = FALSE;
  else
    enabled = TRUE;

  g_value_set_boolean (value, enabled);

  return TRUE;
}

static GVariant *
set_sleep_type (const GValue       *value,
                const GVariantType *expected_type,
                gpointer            data)
{
  GVariant *res;

  if (g_value_get_boolean (value))
    res = g_variant_new_string ("suspend");
  else
    res = g_variant_new_string ("nothing");

  return res;
}

static void
add_automatic_suspend_section (CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;
  GtkWidget *vbox;
  GtkWidget *widget, *box, *label;
  GtkWidget *sw;
  gchar *s;
  gint value;
  GtkTreeModel *model;
  GtkWidget *dialog;
  GtkWidget *combo;
  GtkCellRenderer *cell;

  /* The default values for these settings are unfortunate for us;
   * timeout == 0, action == suspend means 'do nothing' - just
   * as timout === anything, action == nothing.
   * For our switch/combobox combination, the second choice works
   * much better, so translate the first to the second here.
   */
  if (g_settings_get_int (priv->gsd_settings, "sleep-inactive-ac-timeout") == 0)
    {
      g_settings_set_enum (priv->gsd_settings, "sleep-inactive-ac-type", GSD_POWER_ACTION_NOTHING);
      g_settings_set_int (priv->gsd_settings, "sleep-inactive-ac-timeout", 3600);
    }
  if (g_settings_get_int (priv->gsd_settings, "sleep-inactive-battery-timeout") == 0)
    {
      g_settings_set_enum (priv->gsd_settings, "sleep-inactive-battery-type", GSD_POWER_ACTION_NOTHING);
      g_settings_set_int (priv->gsd_settings, "sleep-inactive-battery-timeout", 1800);
    }


  vbox = WID (priv->builder, "vbox_power");

  s = g_markup_printf_escaped ("<b>%s</b>", _("Suspend & Power Off"));
  widget = gtk_label_new (s);
  g_free (s);
  gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
  gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
  gtk_widget_set_margin_left (widget, 56);
  gtk_widget_set_margin_right (widget, 50);
  gtk_widget_set_margin_bottom (widget, 6);
  gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, TRUE, 0);
  gtk_widget_show (widget);

  widget = GTK_WIDGET (egg_list_box_new ());
  priv->boxes_reverse = g_list_prepend (priv->boxes_reverse, widget);
  g_signal_connect (widget, "keynav-failed", G_CALLBACK (keynav_failed), self);
  egg_list_box_set_selection_mode (EGG_LIST_BOX (widget), GTK_SELECTION_NONE);
  egg_list_box_set_separator_funcs (EGG_LIST_BOX (widget),
                                    update_separator_func,
                                    NULL, NULL);
  g_signal_connect_swapped (widget, "child-activated",
                            G_CALLBACK (activate_child), self);

  box = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_IN);
  gtk_widget_set_margin_left (box, 50);
  gtk_widget_set_margin_right (box, 50);
  gtk_widget_set_margin_bottom (box, 24);
  gtk_widget_show (box);
  gtk_container_add (GTK_CONTAINER (box), widget);
  gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, TRUE, 0);

  self->priv->automatic_suspend_row = box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);
  label = gtk_label_new (_("_Automatic Suspend"));
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
  gtk_widget_set_margin_left (label, 20);
  gtk_widget_set_margin_right (label, 20);
  gtk_widget_set_margin_top (label, 6);
  gtk_widget_set_margin_bottom (label, 6);
  gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);

  priv->automatic_suspend_label = sw = gtk_label_new ("");
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), sw);
  g_signal_connect (sw, "mnemonic-activate",
                    G_CALLBACK (automatic_suspend_activate), self);
  gtk_misc_set_alignment (GTK_MISC (sw), 1, 0.5);
  gtk_widget_set_margin_left (sw, 24);
  gtk_widget_set_margin_right (sw, 24);
  gtk_box_pack_start (GTK_BOX (box), sw, FALSE, TRUE, 0);
  gtk_container_add (GTK_CONTAINER (widget), box);
  gtk_size_group_add_widget (priv->row_sizegroup, box);
  update_automatic_suspend_label (self);

  priv->critical_battery_row = box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  label = gtk_label_new (_("When Battery Power is _Critical"));
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
  gtk_widget_set_margin_left (label, 20);
  gtk_widget_set_margin_right (label, 20);
  gtk_widget_set_margin_top (label, 6);
  gtk_widget_set_margin_bottom (label, 6);
  gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);

  if (up_client_get_can_hibernate (self->priv->up_client))
    {
      model = (GtkTreeModel*)gtk_builder_get_object (priv->builder, "liststore_critical");
      priv->critical_battery_combo = sw = gtk_combo_box_new_with_model (model);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), sw);
      cell = gtk_cell_renderer_text_new ();
      gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (sw), cell, TRUE);
      gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (sw), cell, "text", 0);
      gtk_widget_set_margin_left (sw, 20);
      gtk_widget_set_margin_right (sw, 20);
      gtk_widget_set_valign (sw, GTK_ALIGN_CENTER);

      g_object_set_data (G_OBJECT (sw), "_gsettings_key", "critical-battery-action");
      value = g_settings_get_enum (priv->gsd_settings, "critical-battery-action");
      set_value_for_combo (GTK_COMBO_BOX (sw), value);
      g_signal_connect (sw, "changed",
                        G_CALLBACK (combo_enum_changed_cb), self);

      gtk_box_pack_start (GTK_BOX (box), sw, FALSE, TRUE, 0);
      g_signal_connect (priv->gsd_settings, "changed",
                        G_CALLBACK (on_suspend_settings_changed), self);
    }
  else
    {
      label = gtk_label_new (_("Power Off"));
      gtk_widget_set_margin_left (label, 20);
      gtk_widget_set_margin_right (label, 20);
      gtk_widget_set_margin_top (label, 6);
      gtk_widget_set_margin_bottom (label, 6);
      gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
    }

  gtk_container_add (GTK_CONTAINER (widget), box);
  gtk_size_group_add_widget (priv->row_sizegroup, box);
  gtk_widget_show_all (widget);

  dialog = GTK_WIDGET (gtk_builder_get_object (priv->builder, "automatic_suspend_dialog"));
  sw = GTK_WIDGET (gtk_builder_get_object (priv->builder, "automatic_suspend_close"));
  g_signal_connect_swapped (sw, "clicked", G_CALLBACK (gtk_widget_hide), dialog);
  g_signal_connect (dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);

  sw = GTK_WIDGET (gtk_builder_get_object (priv->builder, "suspend_on_battery_switch"));
  g_settings_bind_with_mapping (priv->gsd_settings, "sleep-inactive-battery-type",
                                sw, "active",
                                G_SETTINGS_BIND_DEFAULT,
                                get_sleep_type, set_sleep_type, NULL, NULL);

  combo = GTK_WIDGET (gtk_builder_get_object (priv->builder, "suspend_on_battery_delay_combo"));
  g_object_set_data (G_OBJECT (combo), "_gsettings_key", "sleep-inactive-battery-timeout");
  value = g_settings_get_int (priv->gsd_settings, "sleep-inactive-battery-timeout");
  set_value_for_combo (GTK_COMBO_BOX (combo), value);
  g_signal_connect (combo, "changed",
                    G_CALLBACK (combo_time_changed_cb), self);
  g_object_bind_property (sw, "active", combo, "sensitive",
                          G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);

  sw = GTK_WIDGET (gtk_builder_get_object (priv->builder, "suspend_on_ac_switch"));
  g_settings_bind_with_mapping (priv->gsd_settings, "sleep-inactive-ac-type",
                                sw, "active",
                                G_SETTINGS_BIND_DEFAULT,
                                get_sleep_type, set_sleep_type, NULL, NULL);

  combo = GTK_WIDGET (gtk_builder_get_object (priv->builder, "suspend_on_ac_delay_combo"));
  g_object_set_data (G_OBJECT (combo), "_gsettings_key", "sleep-inactive-ac-timeout");
  value = g_settings_get_int (priv->gsd_settings, "sleep-inactive-ac-timeout");
  set_value_for_combo (GTK_COMBO_BOX (combo), value);
  g_signal_connect (combo, "changed",
                    G_CALLBACK (combo_time_changed_cb), self);
  g_object_bind_property (sw, "active", combo, "sensitive",
                          G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
}

static gint
battery_sort_func (gconstpointer a, gconstpointer b, gpointer data)
{
  GObject *row_a = (GObject*)a;
  GObject *row_b = (GObject*)b;
  gboolean a_primary;
  gboolean b_primary;
  gint a_kind;
  gint b_kind;

  a_primary = GPOINTER_TO_INT (g_object_get_data (row_a, "primary"));
  b_primary = GPOINTER_TO_INT (g_object_get_data (row_b, "primary"));

  if (a_primary)
    return -1;
  else if (b_primary)
    return 1;

  a_kind = GPOINTER_TO_INT (g_object_get_data (row_a, "kind"));
  b_kind = GPOINTER_TO_INT (g_object_get_data (row_b, "kind"));

  return a_kind - b_kind;
}

static void
add_battery_section (CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;
  GtkWidget *vbox;
  GtkWidget *widget, *box;
  GtkWidget *frame;
  gchar *s;

  vbox = WID (priv->builder, "vbox_power");

  priv->battery_section = box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_widget_set_margin_left (box, 50);
  gtk_widget_set_margin_right (box, 50);
  gtk_widget_set_margin_bottom (box, 6);
  gtk_widget_set_margin_bottom (box, 24);
  gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, TRUE, 0);

  s = g_markup_printf_escaped ("<b>%s</b>", _("Battery"));
  priv->battery_heading = widget = gtk_label_new (s);
  g_free (s);
  gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
  gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
  gtk_widget_set_margin_left (widget, 6);
  gtk_widget_set_margin_right (widget, 6);
  gtk_widget_set_margin_bottom (widget, 6);
  gtk_widget_set_margin_bottom (box, 24);
  gtk_box_pack_start (GTK_BOX (box), widget, FALSE, TRUE, 0);

  priv->battery_list = widget = GTK_WIDGET (egg_list_box_new ());
  priv->boxes_reverse = g_list_prepend (priv->boxes_reverse, priv->battery_list);
  g_signal_connect (widget, "keynav-failed", G_CALLBACK (keynav_failed), self);
  egg_list_box_set_selection_mode (EGG_LIST_BOX (widget), GTK_SELECTION_NONE);
  egg_list_box_set_separator_funcs (EGG_LIST_BOX (widget),
                                    update_separator_func,
                                    NULL, NULL);
  egg_list_box_set_sort_func (EGG_LIST_BOX (widget),
                              battery_sort_func, NULL, NULL);

  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_container_add (GTK_CONTAINER (frame), widget);
  gtk_box_pack_start (GTK_BOX (box), frame, FALSE, TRUE, 0);

  priv->battery_capacity = widget = gtk_label_new ("");
  gtk_widget_set_margin_top (widget, 6);
  gtk_widget_set_halign (widget, GTK_ALIGN_CENTER);
  gtk_style_context_add_class (gtk_widget_get_style_context (widget), GTK_STYLE_CLASS_DIM_LABEL);
  gtk_box_pack_start (GTK_BOX (box), priv->battery_capacity, FALSE, TRUE, 0);
  gtk_widget_show_all (box);
}

static void
add_device_section (CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv = self->priv;
  GtkWidget *vbox;
  GtkWidget *widget, *box;
  GtkWidget *frame;
  gchar *s;

  vbox = WID (priv->builder, "vbox_power");

  priv->device_section = box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_widget_set_margin_left (box, 50);
  gtk_widget_set_margin_right (box, 50);
  gtk_widget_set_margin_top (box, 6);
  gtk_widget_set_margin_bottom (box, 24);
  gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, TRUE, 0);

  s = g_markup_printf_escaped ("<b>%s</b>", _("Devices"));
  priv->device_heading = widget = gtk_label_new (s);
  g_free (s);
  gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
  gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
  gtk_widget_set_margin_left (widget, 6);
  gtk_widget_set_margin_right (widget, 6);
  gtk_widget_set_margin_bottom (widget, 6);
  gtk_box_pack_start (GTK_BOX (box), widget, FALSE, TRUE, 0);

  priv->device_list = widget = GTK_WIDGET (egg_list_box_new ());
  priv->boxes_reverse = g_list_prepend (priv->boxes_reverse, priv->device_list);
  g_signal_connect (widget, "keynav-failed", G_CALLBACK (keynav_failed), self);
  egg_list_box_set_selection_mode (EGG_LIST_BOX (widget), GTK_SELECTION_NONE);
  egg_list_box_set_separator_funcs (EGG_LIST_BOX (widget),
                                    update_separator_func,
                                    NULL, NULL);
  egg_list_box_set_sort_func (EGG_LIST_BOX (widget),
                              battery_sort_func, NULL, NULL);

  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_container_add (GTK_CONTAINER (frame), widget);
  gtk_box_pack_start (GTK_BOX (box), frame, FALSE, TRUE, 0);

  gtk_widget_show_all (box);
}

static void
on_content_size_changed (GtkWidget *widget, GtkAllocation *allocation, gpointer data)
{
  GtkWidget *box;

  box = gtk_widget_get_parent (gtk_widget_get_parent (widget));
  if (allocation->height < 490)
    {
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
                                      GTK_POLICY_NEVER, GTK_POLICY_NEVER);
    }
  else
    {
      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
                                      GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
      gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (box), 490);
    }
}

static void
cc_power_panel_init (CcPowerPanel *self)
{
  CcPowerPanelPrivate *priv;
  GError     *error;
  GtkWidget  *widget;
  GtkWidget  *box;

  priv = self->priv = POWER_PANEL_PRIVATE (self);
  g_resources_register (cc_power_get_resource ());

  priv->builder = gtk_builder_new ();

  error = NULL;
  gtk_builder_add_from_resource (self->priv->builder,
                                 "/org/gnome/control-center/power/power.ui",
                                 &error);

  if (error != NULL)
    {
      g_warning ("Could not load interface file: %s", error->message);
      g_error_free (error);
      return;
    }

  priv->cancellable = g_cancellable_new ();

  g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
                            G_DBUS_PROXY_FLAGS_NONE,
                            NULL,
                            "org.gnome.SettingsDaemon.Power",
                            "/org/gnome/SettingsDaemon/Power",
                            "org.gnome.SettingsDaemon.Power.Screen",
                            priv->cancellable,
                            got_screen_proxy_cb,
                            self);

  priv->up_client = up_client_new ();

  priv->gsd_settings = g_settings_new ("org.gnome.settings-daemon.plugins.power");
  priv->session_settings = g_settings_new ("org.gnome.desktop.session");

  priv->row_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
  priv->battery_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
  priv->charge_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
  priv->level_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);

  add_battery_section (self);
  add_device_section (self);
  add_power_saving_section (self);
  add_automatic_suspend_section (self);

  priv->boxes = g_list_copy (priv->boxes_reverse);
  priv->boxes = g_list_reverse (priv->boxes);

  set_ac_battery_ui_mode (self);
  update_automatic_suspend_label (self);

  /* populate batteries */
  g_signal_connect (priv->up_client, "device-added", G_CALLBACK (up_client_changed), self);
  g_signal_connect (priv->up_client, "device-changed", G_CALLBACK (up_client_changed), self);
  g_signal_connect (priv->up_client, "device-removed", G_CALLBACK (up_client_changed), self);
  up_client_changed (priv->up_client, NULL, self);

  widget = WID (priv->builder, "vbox_power");
  box = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
  g_signal_connect (widget, "size-allocate",
                    G_CALLBACK (on_content_size_changed), NULL);
  gtk_widget_show (box);
  gtk_container_add (GTK_CONTAINER (self), box);
  g_object_ref (widget);
  gtk_widget_unparent (widget);
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (box), widget);
  g_object_unref (widget);

  priv->focus_adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (box));
  gtk_container_set_focus_vadjustment (GTK_CONTAINER (widget), priv->focus_adjustment);
}
