/*
 * * Copyright (C) 2014 Eric Koegel <eric@xfce.org>
 * * Copyright (C) 2019 Kacper PiwiƄski
 *
 * Licensed under the GNU General Public License Version 2
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>
#include <libxfce4util/libxfce4util.h>
#include <libxfce4ui/libxfce4ui.h>
#include <upower.h>
#include <xfconf/xfconf.h>

#include "common/xfpm-common.h"
#include "common/xfpm-config.h"
#include "common/xfpm-icons.h"
#include "common/xfpm-power-common.h"
#include "common/xfpm-brightness.h"
#include "common/xfpm-debug.h"
#ifdef XFPM_SYSTRAY
#include "src/xfpm-inhibit.h"
#endif

#include "power-manager-button.h"
#include "scalemenuitem.h"


#define SET_LEVEL_TIMEOUT (50)
#define SAFE_SLIDER_MIN_LEVEL (5)

struct PowerManagerButtonPrivate
{
#ifdef XFCE_PLUGIN
  XfcePanelPlugin *plugin;
  GDBusProxy      *inhibit_proxy;
#else
  XfpmInhibit     *inhibit;
#endif

  XfconfChannel   *channel;

  UpClient        *upower;

  /* A list of BatteryDevices  */
  GList           *devices;

  /* The left-click popup menu, if one is being displayed */
  GtkWidget       *menu;

  /* The actual panel icon image */
  GtkWidget       *panel_icon_image;
  GtkWidget       *panel_presentation_mode;
  GtkWidget       *panel_label;
  GtkWidget       *hbox;
  /* Keep track of icon name to redisplay during size changes */
  gchar           *panel_icon_name;
  gchar           *panel_fallback_icon_name;
  /* Keep track of the last icon size for use during updates */
  gint             panel_icon_width;
  /* Keep track of the tooltip */
  gchar           *tooltip;

  /* Upower 0.99 has a display device that can be used for the
   * panel image and tooltip description */
  UpDevice        *display_device;

  XfpmBrightness  *brightness;

  /* display brightness slider widget */
  GtkWidget       *range;
  /* Some laptops (and mostly newer ones with intel graphics) can turn off the
   * backlight completely. If the user is not careful and sets the brightness
   * very low using the slider, he might not be able to see the screen contents
   * anymore. Brightness keys do not work on every laptop, so it's better to use
   * a safe default minimum level that the user can change via the settings
   * editor if desired.
   */
  gint32           brightness_min_level;

  gint             show_panel_label;
  gboolean         presentation_mode;
  gboolean         show_presentation_indicator;

  /* filter range value changed events for snappier UI feedback */
  guint            set_level_timeout;
};

typedef struct
{
  cairo_surface_t *surface;           /* Icon */
  GtkWidget       *img;               /* Icon image in the menu */
  gchar           *details;           /* Description of the device + state */
  gchar           *object_path;       /* UpDevice object path */
  UpDevice        *device;            /* Pointer to the UpDevice */
  gulong           changed_signal_id; /* device changed callback id */
  gulong           expose_signal_id;  /* expose-event callback id */
  GtkWidget       *menu_item;         /* The device's item on the menu (if shown) */
} BatteryDevice;

typedef enum
{
  PROP_0 = 0,
  PROP_BRIGHTNESS_MIN_LEVEL,
  PROP_SHOW_PANEL_LABEL,
  PROP_PRESENTATION_MODE,
  PROP_SHOW_PRESENTATION_INDICATOR,
} POWER_MANAGER_BUTTON_PROPERTIES;

enum {
  SIG_ICON_NAME_CHANGED = 0,
  SIG_TOOLTIP_CHANGED,
  SIG_N_SIGNALS,
};

static guint __signals[SIG_N_SIGNALS] = { 0, };

G_DEFINE_TYPE_WITH_PRIVATE (PowerManagerButton, power_manager_button, GTK_TYPE_TOGGLE_BUTTON)

static void       power_manager_button_finalize                         (GObject *object);
static GList*     find_device_in_list                                   (PowerManagerButton *button,
                                                                         const gchar *object_path);
static gboolean   power_manager_button_device_icon_draw                 (GtkWidget *img,
                                                                         cairo_t *cr,
                                                                         gpointer userdata);
static void       power_manager_button_set_icon                         (PowerManagerButton *button);
static void       power_manager_button_set_label                        (PowerManagerButton *button,
                                                                         gdouble percentage,
                                                                         guint64 time_to_empty_or_full);
#ifdef XFCE_PLUGIN
static void       power_manager_button_toggle_presentation_mode         (GtkMenuItem *mi,
                                                                         GtkSwitch *sw);
static void       power_manager_button_update_presentation_indicator    (PowerManagerButton *button);
#endif
static void       power_manager_button_update_label                     (PowerManagerButton *button,
                                                                         UpDevice *device);
static gboolean   power_manager_button_press_event                      (GtkWidget *widget,
                                                                         GdkEventButton *event);
static gboolean   power_manager_button_menu_add_device                  (PowerManagerButton *button,
                                                                         BatteryDevice *battery_device,
                                                                         gboolean append);
static void       increase_brightness                                   (PowerManagerButton *button);
static void       decrease_brightness                                   (PowerManagerButton *button);
static void       battery_device_remove_surface                         (BatteryDevice *battery_device);


static BatteryDevice*
get_display_device (PowerManagerButton *button)
{
  GList *item = NULL;
  gdouble highest_percentage = 0;
  BatteryDevice *display_device = NULL;

  TRACE("entering");

  g_return_val_if_fail (POWER_MANAGER_IS_BUTTON(button), NULL);

  if (button->priv->display_device)
  {
    item = find_device_in_list (button, up_device_get_object_path (button->priv->display_device));
    if (item)
    {
      return item->data;
    }
  }

  /* We want to find the battery or ups device with the highest percentage
   * and use that to get our tooltip from */
  for (item = g_list_first (button->priv->devices); item != NULL; item = g_list_next (item))
  {
    BatteryDevice *battery_device = item->data;
    guint type = 0;
    gdouble percentage;

    if (!battery_device->device || !UP_IS_DEVICE(battery_device->device))
    {
      continue;
    }

    g_object_get (battery_device->device,
                  "kind", &type,
                  "percentage", &percentage,
                  NULL);

    if (type == UP_DEVICE_KIND_BATTERY || type == UP_DEVICE_KIND_UPS)
    {
      if (highest_percentage < percentage)
      {
        display_device = battery_device;
        highest_percentage = percentage;
      }
    }
  }

  return display_device;
}

static GList*
find_device_in_list (PowerManagerButton *button, const gchar *object_path)
{
  GList *item = NULL;

  TRACE("entering");

  g_return_val_if_fail ( POWER_MANAGER_IS_BUTTON(button), NULL );

  for (item = g_list_first (button->priv->devices); item != NULL; item = g_list_next (item))
  {
    BatteryDevice *battery_device = item->data;
    if (battery_device == NULL)
    {
      DBG("!battery_device");
      continue;
    }

    if (g_strcmp0 (battery_device->object_path, object_path) == 0)
      return item;
  }

  return NULL;
}

static void
power_manager_button_set_icon (PowerManagerButton *button)
{
  g_return_if_fail (GTK_IS_WIDGET (button->priv->panel_presentation_mode));

  if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), button->priv->panel_icon_name))
    gtk_image_set_from_icon_name (GTK_IMAGE (button->priv->panel_icon_image), button->priv->panel_icon_name, GTK_ICON_SIZE_BUTTON);
  else
    gtk_image_set_from_icon_name (GTK_IMAGE (button->priv->panel_icon_image), button->priv->panel_fallback_icon_name, GTK_ICON_SIZE_BUTTON);
  gtk_image_set_pixel_size (GTK_IMAGE (button->priv->panel_icon_image), button->priv->panel_icon_width);

  gtk_image_set_pixel_size (GTK_IMAGE (button->priv->panel_presentation_mode), button->priv->panel_icon_width);

  /* Notify others the icon name changed */
  g_signal_emit (button, __signals[SIG_ICON_NAME_CHANGED], 0);
}

static void
power_manager_button_set_tooltip (PowerManagerButton *button)
{
  BatteryDevice *display_device = get_display_device (button);

  TRACE("entering");

  if (!GTK_IS_WIDGET (button))
  {
    g_critical ("power_manager_button_set_tooltip: !GTK_IS_WIDGET (button)");
    return;
  }

  if (button->priv->tooltip != NULL)
  {
    g_free (button->priv->tooltip);
    button->priv->tooltip = NULL;
  }

  if (display_device)
  {
    /* if we have something, display it */
    if( display_device->details )
    {
      button->priv->tooltip = g_strdup(display_device->details);
      gtk_widget_set_tooltip_markup (GTK_WIDGET (button), display_device->details);
      /* Tooltip changed! */
      g_signal_emit (button, __signals[SIG_TOOLTIP_CHANGED], 0);
      return;
    }
  }

  /* Odds are this is a desktop without any batteries attached */
  button->priv->tooltip = g_strdup (_("Display battery levels for attached devices"));
  gtk_widget_set_tooltip_text (GTK_WIDGET (button), button->priv->tooltip);
  /* Tooltip changed! */
  g_signal_emit (button, __signals[SIG_TOOLTIP_CHANGED], 0);
}

const gchar *
power_manager_button_get_icon_name (PowerManagerButton *button)
{
  return button->priv->panel_icon_name;
}

const gchar *
power_manager_button_get_tooltip (PowerManagerButton *button)
{
  return button->priv->tooltip;
}

static void
power_manager_button_set_label (PowerManagerButton *button, gdouble percentage,
                                guint64 time_to_empty_or_full)
{
  gchar *label_string = NULL;
  gint   hours;
  gint   minutes;
  gchar *remaining_time = NULL;

  /* Create the short timestring in the format hh:mm */
  minutes = (int) ( ( time_to_empty_or_full / 60.0 ) + 0.5 );
  if (minutes < 60)
  {
    if (minutes < 10)
      remaining_time = g_strdup_printf ("0:0%d", minutes);
    else
      remaining_time = g_strdup_printf ("0:%d", minutes);
  }
  else
  {
    hours = minutes / 60;
    minutes = minutes % 60;
    if (minutes < 10)
      remaining_time = g_strdup_printf ("%d:0%d", hours, minutes);
    else
      remaining_time = g_strdup_printf ("%d:%d", hours, minutes);
  }

  /* Set the label accordingly or hide it if the battery is full */
  if (button->priv->show_panel_label == 1)
    label_string = g_strdup_printf ("%d%%", (int) percentage);
  else if (button->priv->show_panel_label == 2)
    label_string = g_strdup_printf ("%s", remaining_time);
  else if (button->priv->show_panel_label == 3)
    label_string = g_strdup_printf ("%d%% - %s", (int) percentage, remaining_time);

  gtk_label_set_text (GTK_LABEL (button->priv->panel_label), label_string);

  g_free (label_string);
  g_free (remaining_time);
}

static gboolean
power_manager_button_device_icon_draw (GtkWidget *img, cairo_t *cr, gpointer userdata)
{
  UpDevice *device = NULL;
  guint type = 0, state = 0;
  gdouble percentage;
  gint height, width;
  gdouble min_height = 2;
  PangoLayout *layout = NULL;
  PangoRectangle ink_extent, log_extent;
  GtkAllocation allocation;

  TRACE("entering");

  /* sanity checks */
  if (!img || !GTK_IS_WIDGET (img))
    return FALSE;

  if (UP_IS_DEVICE (userdata))
  {
    device = UP_DEVICE(userdata);

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

    /* Don't draw the progressbar for Battery */
    if (type == UP_DEVICE_KIND_BATTERY)
      return FALSE;
  }
  else
  {
    /* If the UpDevice hasn't fully updated yet it then we'll want
     * a question mark for sure. */
    state = UP_DEVICE_STATE_UNKNOWN;
  }

  gtk_widget_get_allocation (img, &allocation);

  width = allocation.width;
  height = allocation.height;

  if (state != UP_DEVICE_STATE_UNKNOWN)
  {
    /* Draw the trough of the progressbar */
    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    cairo_set_line_width (cr, 1.0);
    cairo_rectangle (cr, width - 3.5, allocation.y + 1.5, 5, height - 2);
    cairo_set_source_rgb (cr, 0.87, 0.87, 0.87);
    cairo_fill_preserve (cr);
    cairo_set_source_rgb (cr, 0.53, 0.54, 0.52);
    cairo_stroke (cr);

    /* Draw the fill of the progressbar
       Use yellow for 20% and below, green for 100%, red for 5% and below and blue for the rest */
    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);

    if ((height * (percentage / 100)) > min_height)
       min_height = (height - 3) * (percentage / 100);

    cairo_rectangle (cr, width - 3, allocation.y + height - min_height - 1, 4, min_height);
    if (percentage > 5 && percentage < 20)
        cairo_set_source_rgb (cr, 0.93, 0.83, 0.0);
    else if (percentage > 20 && percentage < 100)
        cairo_set_source_rgb (cr, 0.2, 0.4, 0.64);
    else if (percentage == 100)
        cairo_set_source_rgb (cr, 0.45, 0.82, 0.08);
    else
        cairo_set_source_rgb (cr, 0.94, 0.16, 0.16);
    cairo_fill (cr);

    cairo_rectangle (cr, width - 2.5, allocation.y + 2.5, 3, height - 4);
    cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.75);
    cairo_stroke (cr);
  }
  else
  {
    /* Draw a bubble with a question mark for devices with unknown state */
    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    cairo_set_line_width (cr, 1.0);
    cairo_arc(cr, width - 4.5, allocation.y + 6.5, 6, 0, 2*3.14159);
    cairo_set_source_rgb (cr, 0.2, 0.54, 0.9);
    cairo_fill_preserve (cr);
    cairo_set_source_rgb (cr, 0.1, 0.37, 0.6);
    cairo_stroke (cr);

    layout = gtk_widget_create_pango_layout (GTK_WIDGET (img), "?");
    pango_layout_set_font_description (layout, pango_font_description_from_string ("Sans Bold 9"));
    pango_layout_get_pixel_extents (layout, &ink_extent, &log_extent);
    cairo_move_to (cr, (width - 5.5) - (log_extent.width / 2), (allocation.y + 5.5) - (log_extent.height / 2));
    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
    pango_cairo_show_layout (cr, layout);
  }

  if (layout)
    g_object_unref (layout);

  return FALSE;
}


static void
power_manager_button_update_device_icon_and_details (PowerManagerButton *button, UpDevice *device)
{
  GList           *item;
  BatteryDevice   *battery_device;
  BatteryDevice   *display_device;
  const gchar     *object_path = up_device_get_object_path(device);
  gchar           *details = NULL;
  gchar           *icon_name = NULL;
  gchar           *menu_icon_name = NULL;
  gint             scale_factor;
  GdkPixbuf       *pix;
  cairo_surface_t *surface = NULL;

  XFPM_DEBUG("entering for %s", object_path);

  if (!POWER_MANAGER_IS_BUTTON (button))
    return;

  item = find_device_in_list (button, object_path);

  if (item == NULL)
    return;

  battery_device = item->data;

  if (button->priv->upower != NULL)
  {
    icon_name = get_device_icon_name (button->priv->upower, device, TRUE);
    menu_icon_name = get_device_icon_name (button->priv->upower, device, FALSE);
    details = get_device_description (button->priv->upower, device);
  }

  /* If UPower doesn't give us an icon, just use the default */
  if (g_strcmp0(menu_icon_name, "") == 0)
  {
    /* ignore empty icon names */
    g_free (menu_icon_name);
    menu_icon_name = NULL;
  }

  if (menu_icon_name == NULL)
    menu_icon_name = g_strdup (PANEL_DEFAULT_ICON);

  scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (button));
  pix = gtk_icon_theme_load_icon_for_scale (gtk_icon_theme_get_default (),
                                            menu_icon_name,
                                            32,
                                            scale_factor,
                                            GTK_ICON_LOOKUP_USE_BUILTIN
                                            | GTK_ICON_LOOKUP_FORCE_SIZE,
                                            NULL);
  if (G_LIKELY (pix != NULL))
  {
    surface = gdk_cairo_surface_create_from_pixbuf (pix,
                                                    scale_factor,
                                                    gtk_widget_get_window (GTK_WIDGET (button)));
    g_object_unref (pix);
  }

  if (battery_device->details)
    g_free (battery_device->details);

  battery_device->details = details;

  /* If we had an image before, remove it and the callback */
  battery_device_remove_surface(battery_device);

  battery_device->surface = surface;

  /* Get the display device, which may now be this one */
  display_device = get_display_device (button);
  if (battery_device == display_device)
  {
    DBG("this is the display device, updating");
    /* update the icon */
    g_free (button->priv->panel_icon_name);
#ifdef XFCE_PLUGIN
    button->priv->panel_icon_name = g_strdup (icon_name);
    button->priv->panel_fallback_icon_name = g_strdup_printf ("%s-%s", menu_icon_name, "symbolic");
#else
    button->priv->panel_icon_name = g_strdup (icon_name);
    button->priv->panel_fallback_icon_name = g_strdup (menu_icon_name);
#endif
    power_manager_button_set_icon (button);
    /* update the tooltip */
    power_manager_button_set_tooltip (button);
    /* update the label */
    power_manager_button_update_label (button, device);
  }
  g_free (icon_name);
  g_free (menu_icon_name);

  /* If the menu is being displayed, update it */
  if (button->priv->menu && battery_device->menu_item)
  {
    gtk_menu_item_set_label (GTK_MENU_ITEM (battery_device->menu_item), details);

    /* update the image, keep track of the signal ids and the img
     * so we can disconnect it later */
    battery_device->img = gtk_image_new_from_surface (battery_device->surface);
    g_object_ref (battery_device->img);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(battery_device->menu_item), battery_device->img);
G_GNUC_END_IGNORE_DEPRECATIONS
    battery_device->expose_signal_id = g_signal_connect_after (G_OBJECT (battery_device->img),
                                                               "draw",
                                                               G_CALLBACK (power_manager_button_device_icon_draw),
                                                               device);
  }
}

static void
device_changed_cb (UpDevice *device, GParamSpec *pspec, PowerManagerButton *button)
{
    power_manager_button_update_device_icon_and_details (button, device);
}

static void
power_manager_button_add_device (UpDevice *device, PowerManagerButton *button)
{
  BatteryDevice *battery_device;
  guint type = 0;
  const gchar *object_path = up_device_get_object_path(device);
  gulong signal_id;

  XFPM_DEBUG("entering for %s", object_path);

  g_return_if_fail (POWER_MANAGER_IS_BUTTON (button ));

  /* don't add the same device twice */
  if (find_device_in_list (button, object_path))
    return;

  battery_device = g_new0 (BatteryDevice, 1);

  /* hack, this depends on XFPM_DEVICE_TYPE_* being in sync with UP_DEVICE_KIND_* */
  g_object_get (device,
                "kind", &type,
                NULL);

  signal_id = g_signal_connect (device, "notify", G_CALLBACK (device_changed_cb), button);

  /* populate the struct */
  battery_device->object_path = g_strdup (object_path);
  battery_device->changed_signal_id = signal_id;
  battery_device->device = g_object_ref(device);

  /* add it to the list */
  button->priv->devices = g_list_append (button->priv->devices, battery_device);

  /* Add the icon and description for the device */
  power_manager_button_update_device_icon_and_details (button, device);

  /* If the menu is being shown, add this new device to it */
  if (button->priv->menu)
  {
    power_manager_button_menu_add_device (button, battery_device, FALSE);
  }
}

/* This function unrefs the pix and img from the battery device and
 * disconnects the expose-event callback on the img.
 */
static void
battery_device_remove_surface (BatteryDevice *battery_device)
{
  TRACE("entering");

  if (battery_device == NULL)
    return;

  if (battery_device->surface != NULL)
  {
    if (GTK_IS_WIDGET (battery_device->img))
    {
      if (battery_device->expose_signal_id != 0)
      {
          g_signal_handler_disconnect (battery_device->img, battery_device->expose_signal_id);
          battery_device->expose_signal_id = 0;
      }
      g_object_unref (battery_device->img);
      battery_device->img = NULL;
    }
    cairo_surface_destroy (battery_device->surface);
    battery_device->surface = NULL;
  }
}

static void
remove_battery_device (PowerManagerButton *button, BatteryDevice *battery_device)
{
  g_return_if_fail (POWER_MANAGER_IS_BUTTON (button));
  g_return_if_fail (battery_device != NULL);

  /* If it is being shown in the menu, remove it */
  if (battery_device->menu_item && button->priv->menu)
    gtk_container_remove (GTK_CONTAINER (button->priv->menu), battery_device->menu_item);

  g_free (battery_device->details);
  g_free (battery_device->object_path);

  battery_device_remove_surface (battery_device);

  if (battery_device->device != NULL && UP_IS_DEVICE(battery_device->device))
  {
    /* disconnect the signal handler if we were using it */
    if (battery_device->changed_signal_id != 0)
      g_signal_handler_disconnect (battery_device->device, battery_device->changed_signal_id);
    battery_device->changed_signal_id = 0;

    g_object_unref (battery_device->device);
    battery_device->device = NULL;
  }

  g_free (battery_device);
}

static void
power_manager_button_remove_device (PowerManagerButton *button, const gchar *object_path)
{
  GList *item;
  BatteryDevice *battery_device;

  TRACE("entering for %s", object_path);

  item = find_device_in_list (button, object_path);

  if (item == NULL)
    return;

  battery_device = item->data;

  /* Remove its resources */
  remove_battery_device (button, battery_device);

  /* remove it item and free the battery device */
  button->priv->devices = g_list_delete_link (button->priv->devices, item);
}

static void
device_added_cb (UpClient *upower, UpDevice *device, PowerManagerButton *button)
{
  power_manager_button_add_device (device, button);
}

static void
device_removed_cb (UpClient *upower, const gchar *object_path, PowerManagerButton *button)
{
  power_manager_button_remove_device (button, object_path);
}


static void
power_manager_button_add_all_devices (PowerManagerButton *button)
{
  GPtrArray *array = NULL;
  guint i;

  if (button->priv->upower != NULL)
  {
    button->priv->display_device = up_client_get_display_device (button->priv->upower);
    power_manager_button_add_device (button->priv->display_device, button);

#if UP_CHECK_VERSION(0, 99, 8)
    array = up_client_get_devices2 (button->priv->upower);
#else
    array = up_client_get_devices (button->priv->upower);
#endif
  }

  if (array)
  {
    for (i = 0; i < array->len; i++)
    {
      UpDevice *device = g_ptr_array_index (array, i);

      power_manager_button_add_device (device, button);
    }
    g_ptr_array_free (array, TRUE);
  }
}

static void
power_manager_button_remove_all_devices (PowerManagerButton *button)
{
  GList *item = NULL;

  TRACE("entering");

  g_return_if_fail (POWER_MANAGER_IS_BUTTON (button));

  for (item = g_list_first (button->priv->devices); item != NULL; item = g_list_next (item))
  {
    BatteryDevice *battery_device = item->data;
    if (battery_device == NULL)
    {
      DBG("!battery_device");
      continue;
    }

    /* Remove its resources */
    remove_battery_device (button, battery_device);
  }
}

static gboolean
power_manager_button_scroll_event (GtkWidget *widget, GdkEventScroll *ev)
{
  gboolean hw_found;
  PowerManagerButton *button;

  button = POWER_MANAGER_BUTTON (widget);

  hw_found = xfpm_brightness_has_hw (button->priv->brightness);

  if (!hw_found)
    return FALSE;

  if (ev->direction == GDK_SCROLL_UP)
  {
    increase_brightness (button);
    return TRUE;
  }
  else if (ev->direction == GDK_SCROLL_DOWN)
  {
    decrease_brightness (button);
    return TRUE;
  }
  return FALSE;
}

static void
set_brightness_min_level (PowerManagerButton *button, gint32 new_brightness_level)
{
  gint32 max_level = xfpm_brightness_get_max_level (button->priv->brightness);

  /* sanity check */
  if (new_brightness_level > max_level)
    new_brightness_level = -1;

  /* -1 = auto, we set the step value to a hopefully sane default */
  if (new_brightness_level == -1)
  {
    button->priv->brightness_min_level = (max_level > 100) ? SAFE_SLIDER_MIN_LEVEL : 0;
  }
  else
  {
    button->priv->brightness_min_level = new_brightness_level;
  }

  DBG("button->priv->brightness_min_level : %d", button->priv->brightness_min_level);

  /* update the range if it's being shown */
  if (button->priv->range)
  {
    gtk_range_set_range (GTK_RANGE (button->priv->range), button->priv->brightness_min_level, max_level);
  }
}

static void
power_manager_button_set_property (GObject *object,
                                   guint property_id,
                                   const GValue *value,
                                   GParamSpec *pspec)
{
  PowerManagerButton *button;

  button = POWER_MANAGER_BUTTON (object);

  switch (property_id)
  {
    case PROP_BRIGHTNESS_MIN_LEVEL:
      set_brightness_min_level (button, g_value_get_int (value));
      break;
#ifdef XFCE_PLUGIN
    case PROP_SHOW_PANEL_LABEL:
      button->priv->show_panel_label = g_value_get_int (value);
      power_manager_button_update_label (button, button->priv->display_device);
      break;
    case PROP_PRESENTATION_MODE:
      button->priv->presentation_mode = g_value_get_boolean (value);
      if (GTK_IS_WIDGET (button->priv->panel_presentation_mode))
          power_manager_button_update_presentation_indicator (button);
      break;
    case PROP_SHOW_PRESENTATION_INDICATOR:
      button->priv->show_presentation_indicator = g_value_get_boolean (value);
      if (GTK_IS_WIDGET (button->priv->panel_presentation_mode))
          power_manager_button_update_presentation_indicator (button);
      break;
#else
    case PROP_SHOW_PANEL_LABEL:
    case PROP_PRESENTATION_MODE:
    case PROP_SHOW_PRESENTATION_INDICATOR:
      break;
#endif
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}

static void
power_manager_button_get_property(GObject *object,
                                  guint property_id,
                                  GValue *value,
                                  GParamSpec *pspec)
{
  PowerManagerButton *button;

  button = POWER_MANAGER_BUTTON (object);

  switch(property_id)
  {
    case PROP_BRIGHTNESS_MIN_LEVEL:
      g_value_set_int (value, button->priv->brightness_min_level);
      break;
#ifdef XFCE_PLUGIN
    case PROP_SHOW_PANEL_LABEL:
      g_value_set_int (value, button->priv->show_panel_label);
      break;
    case PROP_PRESENTATION_MODE:
      g_value_set_boolean (value, button->priv->presentation_mode);
      break;
    case PROP_SHOW_PRESENTATION_INDICATOR:
      g_value_set_boolean (value, button->priv->show_presentation_indicator);
      break;
#else
    case PROP_SHOW_PANEL_LABEL:
    case PROP_PRESENTATION_MODE:
    case PROP_SHOW_PRESENTATION_INDICATOR:
      break;
#endif
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}

static void
power_manager_button_class_init (PowerManagerButtonClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  object_class->finalize = power_manager_button_finalize;
  object_class->set_property = power_manager_button_set_property;
  object_class->get_property = power_manager_button_get_property;

  widget_class->button_press_event = power_manager_button_press_event;
  widget_class->scroll_event = power_manager_button_scroll_event;

  __signals[SIG_TOOLTIP_CHANGED] = g_signal_new ("tooltip-changed",
                                                 POWER_MANAGER_TYPE_BUTTON,
                                                 G_SIGNAL_RUN_LAST,
                                                 G_STRUCT_OFFSET(PowerManagerButtonClass,
                                                                 tooltip_changed),
                                                 NULL, NULL,
                                                 g_cclosure_marshal_VOID__VOID,
                                                 G_TYPE_NONE, 0);

  __signals[SIG_ICON_NAME_CHANGED] = g_signal_new ("icon-name-changed",
                                                   POWER_MANAGER_TYPE_BUTTON,
                                                   G_SIGNAL_RUN_LAST,
                                                   G_STRUCT_OFFSET(PowerManagerButtonClass,
                                                                   icon_name_changed),
                                                   NULL, NULL,
                                                   g_cclosure_marshal_VOID__VOID,
                                                   G_TYPE_NONE, 0);

#define XFPM_PARAM_FLAGS  (G_PARAM_READWRITE \
                           | G_PARAM_CONSTRUCT \
                           | G_PARAM_STATIC_NAME \
                           | G_PARAM_STATIC_NICK \
                           | G_PARAM_STATIC_BLURB)

  /* We allow and default to -1 only so that we can automagically set a
   * sane value if the user hasn't selected one already */
  g_object_class_install_property (object_class, PROP_BRIGHTNESS_MIN_LEVEL,
                                   g_param_spec_int(BRIGHTNESS_SLIDER_MIN_LEVEL,
                                                    BRIGHTNESS_SLIDER_MIN_LEVEL,
                                                    BRIGHTNESS_SLIDER_MIN_LEVEL,
                                                    -1, G_MAXINT32, -1,
                                                    XFPM_PARAM_FLAGS));

  g_object_class_install_property (object_class, PROP_SHOW_PANEL_LABEL,
                                   g_param_spec_int (SHOW_PANEL_LABEL,
                                                     NULL, NULL,
                                                     0, G_MAXINT16, 3,
                                                     XFPM_PARAM_FLAGS));

  g_object_class_install_property (object_class, PROP_PRESENTATION_MODE,
                                   g_param_spec_boolean (PRESENTATION_MODE,
                                                         NULL, NULL,
                                                         FALSE,
                                                         XFPM_PARAM_FLAGS));

  g_object_class_install_property (object_class, PROP_SHOW_PRESENTATION_INDICATOR,
                                   g_param_spec_boolean (SHOW_PRESENTATION_INDICATOR,
                                                         NULL, NULL,
                                                         FALSE,
                                                         XFPM_PARAM_FLAGS));
#undef XFPM_PARAM_FLAGS
}

#ifdef XFCE_PLUGIN
static void
inhibit_proxy_ready_cb (GObject *source_object,
                        GAsyncResult *res,
                        gpointer user_data)
{
  GError *error = NULL;
  PowerManagerButton *button = POWER_MANAGER_BUTTON (user_data);

  button->priv->inhibit_proxy = g_dbus_proxy_new_finish (res, &error);
  if (error != NULL)
  {
    g_warning ("error getting inhibit proxy: %s", error->message);
    g_clear_error (&error);
  }
}
#endif

static void
power_manager_button_init (PowerManagerButton *button)
{
  GError *error = NULL;
  GtkCssProvider *css_provider;

  button->priv = power_manager_button_get_instance_private (button);

  gtk_widget_set_can_default (GTK_WIDGET (button), FALSE);
  gtk_widget_set_can_focus (GTK_WIDGET (button), FALSE);
  gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
  gtk_widget_set_focus_on_click (GTK_WIDGET (button), FALSE);
  gtk_widget_set_name (GTK_WIDGET (button), "xfce4-power-manager-plugin");

  button->priv->brightness = xfpm_brightness_new ();
  xfpm_brightness_setup (button->priv->brightness);
  button->priv->set_level_timeout = 0;

  button->priv->upower  = up_client_new ();
  if ( !xfconf_init (&error) )
  {
    if (error)
    {
      g_critical ("xfconf_init failed: %s\n", error->message);
      g_error_free (error);
    }
  }
  else
  {
    button->priv->channel = xfconf_channel_get (XFPM_CHANNEL);
  }

#ifdef XFCE_PLUGIN
  g_dbus_proxy_new (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
                    G_DBUS_PROXY_FLAGS_NONE,
                    NULL,
                    "org.freedesktop.PowerManagement",
                    "/org/freedesktop/PowerManagement/Inhibit",
                    "org.freedesktop.PowerManagement.Inhibit",
                    NULL,
                    inhibit_proxy_ready_cb,
                    button);
#else
  button->priv->inhibit = xfpm_inhibit_new ();
#endif

  /* Sane defaults for the systray and panel icon */
#ifdef XFCE_PLUGIN
  button->priv->panel_icon_name = g_strdup (PANEL_DEFAULT_ICON_SYMBOLIC);
  button->priv->panel_fallback_icon_name = g_strdup (PANEL_DEFAULT_ICON_SYMBOLIC);
#else
  button->priv->panel_icon_name = g_strdup (PANEL_DEFAULT_ICON);
  button->priv->panel_fallback_icon_name = g_strdup (PANEL_DEFAULT_ICON);
#endif
  button->priv->panel_icon_width = 24;

  /* Sane default Gtk style */
  css_provider = gtk_css_provider_new ();
  gtk_css_provider_load_from_data (css_provider,
                                   "#xfce4-power-manager-plugin {"
                                   "padding: 1px;"
                                   "border-width: 1px;}",
                                   -1, NULL);
  gtk_style_context_add_provider (GTK_STYLE_CONTEXT (gtk_widget_get_style_context (GTK_WIDGET (button))),
                                  GTK_STYLE_PROVIDER (css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  /* Intercept scroll events */
  gtk_widget_add_events (GTK_WIDGET (button), GDK_SCROLL_MASK);

  if (button->priv->upower != NULL)
  {
    g_signal_connect (button->priv->upower, "device-added", G_CALLBACK (device_added_cb), button);
    g_signal_connect (button->priv->upower, "device-removed", G_CALLBACK (device_removed_cb), button);
  }
}

static void
power_manager_button_finalize (GObject *object)
{
  PowerManagerButton *button;

  DBG("entering");

  button = POWER_MANAGER_BUTTON (object);

  g_free (button->priv->panel_icon_name);
  g_free (button->priv->panel_fallback_icon_name);

  g_object_unref (button->priv->brightness);
  if (button->priv->set_level_timeout)
  {
    g_source_remove(button->priv->set_level_timeout);
    button->priv->set_level_timeout = 0;
  }

  if (button->priv->upower != NULL)
  {
    g_signal_handlers_disconnect_by_data (button->priv->upower, button);
    g_object_unref (button->priv->upower);
  }

  power_manager_button_remove_all_devices (button);
  g_list_free (button->priv->devices);

#ifdef XFCE_PLUGIN
  g_object_unref (button->priv->plugin);
  if (button->priv->inhibit_proxy != NULL)
    g_object_unref (button->priv->inhibit_proxy);
#else
  g_object_unref (button->priv->inhibit);
#endif

  if (button->priv->channel != NULL)
    xfconf_shutdown ();

  G_OBJECT_CLASS (power_manager_button_parent_class)->finalize (object);
}

GtkWidget *
#ifdef XFCE_PLUGIN
power_manager_button_new (XfcePanelPlugin *plugin)
#endif
#ifdef XFPM_SYSTRAY
power_manager_button_new (void)
#endif
{
  PowerManagerButton *button = NULL;
  button = g_object_new (POWER_MANAGER_TYPE_BUTTON, NULL, NULL);

#ifdef XFCE_PLUGIN
  button->priv->plugin = XFCE_PANEL_PLUGIN (g_object_ref (plugin));
#endif

  xfconf_g_property_bind (button->priv->channel,
                          XFPM_PROPERTIES_PREFIX BRIGHTNESS_SLIDER_MIN_LEVEL, G_TYPE_INT,
                          G_OBJECT (button), BRIGHTNESS_SLIDER_MIN_LEVEL);
  xfconf_g_property_bind (button->priv->channel, XFPM_PROPERTIES_PREFIX SHOW_PANEL_LABEL, G_TYPE_INT,
                          G_OBJECT (button), SHOW_PANEL_LABEL);
  xfconf_g_property_bind (button->priv->channel, XFPM_PROPERTIES_PREFIX PRESENTATION_MODE, G_TYPE_BOOLEAN,
                          G_OBJECT (button), PRESENTATION_MODE);
  xfconf_g_property_bind (button->priv->channel, XFPM_PROPERTIES_PREFIX SHOW_PRESENTATION_INDICATOR, G_TYPE_BOOLEAN,
                          G_OBJECT (button), SHOW_PRESENTATION_INDICATOR);
  return GTK_WIDGET (button);
}

static gboolean
power_manager_button_press_event (GtkWidget *widget, GdkEventButton *event)
{
  PowerManagerButton *button = POWER_MANAGER_BUTTON (widget);

  if (event->button == 1 && !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
  {
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
    power_manager_button_show_menu (button);
    return TRUE;
  }

  if (event->button == 2)
  {
    gboolean state;

    state = xfconf_channel_get_bool (button->priv->channel, XFPM_PROPERTIES_PREFIX PRESENTATION_MODE, FALSE);
    xfconf_channel_set_bool (button->priv->channel, XFPM_PROPERTIES_PREFIX PRESENTATION_MODE, !state);
    return TRUE;
  }

  return FALSE;
}

#ifdef XFCE_PLUGIN
static void
power_manager_button_size_changed_cb (XfcePanelPlugin *plugin, gint size, PowerManagerButton *button)
{
#if !LIBXFCE4PANEL_CHECK_VERSION (4, 13, 0)
  GtkStyleContext *context;
  GtkBorder padding, border;
  gint width;
  gint xthickness;
  gint ythickness;
#endif

  g_return_if_fail (POWER_MANAGER_IS_BUTTON (button));
  g_return_if_fail (XFCE_IS_PANEL_PLUGIN (plugin));
  g_return_if_fail (size > 0);

  size /= xfce_panel_plugin_get_nrows (plugin);

#if LIBXFCE4PANEL_CHECK_VERSION (4, 13, 0)
  button->priv->panel_icon_width = xfce_panel_plugin_get_icon_size (plugin);
#else
  /* Calculate the size of the widget because the theme can override it */
  context = gtk_widget_get_style_context (GTK_WIDGET (button));
  gtk_style_context_get_padding (context, gtk_widget_get_state_flags (GTK_WIDGET (button)), &padding);
  gtk_style_context_get_border (context, gtk_widget_get_state_flags (GTK_WIDGET (button)), &border);
  xthickness = padding.left + padding.right + border.left + border.right;
  ythickness = padding.top + padding.bottom + border.top + border.bottom;

  /* Calculate the size of the space left for the icon */
  width = size - 2 * MAX (xthickness, ythickness);

  /* Since symbolic icons are usually only provided in 16px we
   * try to be clever and use size steps */
  if (width <= 21)
    button->priv->panel_icon_width = 16;
  else if (width >=22 && width <= 29)
    button->priv->panel_icon_width = 24;
  else if (width >= 30 && width <= 40)
    button->priv->panel_icon_width = 32;
  else
    button->priv->panel_icon_width = width;
#endif

  /* resize the plugin */
  gtk_widget_set_size_request (GTK_WIDGET (plugin), size, size);
  power_manager_button_set_icon (button);

  /* resize the plugin button too */
  gtk_widget_set_size_request (GTK_WIDGET (button), -1, -1);
}

static void
power_manager_button_style_update_cb (XfcePanelPlugin *plugin, PowerManagerButton *button)
{
  gtk_widget_reset_style (GTK_WIDGET (plugin));
  power_manager_button_size_changed_cb (plugin, xfce_panel_plugin_get_size (plugin), button);
}

static void
power_manager_button_free_data_cb (XfcePanelPlugin *plugin, PowerManagerButton *button)
{
  gtk_widget_destroy (GTK_WIDGET (button));
}

static void
about_cb (GtkMenuItem *menuitem, gpointer user_data)
{
  xfpm_about ("org.xfce.powermanager");
}
#endif

void
power_manager_button_show (PowerManagerButton *button)
{
  GtkWidget *hbox;
  GtkStyleContext *context;
  GtkCssProvider *css_provider;

  g_return_if_fail (POWER_MANAGER_IS_BUTTON (button));

#ifdef XFCE_PLUGIN
  xfce_panel_plugin_add_action_widget (button->priv->plugin, GTK_WIDGET (button));
  xfce_panel_plugin_set_small (button->priv->plugin, TRUE);
#endif

  button->priv->panel_icon_image = gtk_image_new ();
  button->priv->panel_presentation_mode = gtk_image_new_from_icon_name (PRESENTATION_MODE_ICON, GTK_ICON_SIZE_BUTTON);
  gtk_image_set_pixel_size (GTK_IMAGE (button->priv->panel_presentation_mode), button->priv->panel_icon_width);
  context = gtk_widget_get_style_context (button->priv->panel_presentation_mode);
  css_provider = gtk_css_provider_new ();
  gtk_css_provider_load_from_data (css_provider,
                                   ".presentation-mode { color: @warning_color; }",
                                   -1, NULL);
  gtk_style_context_add_provider (context,
                                  GTK_STYLE_PROVIDER (css_provider),
                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  g_object_unref (css_provider);
  gtk_style_context_add_class (context, "presentation-mode");
  button->priv->panel_label = gtk_label_new ("");
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (button->priv->panel_presentation_mode), TRUE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (button->priv->panel_icon_image), TRUE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (button->priv->panel_label), TRUE, FALSE, 0);

  gtk_container_add (GTK_CONTAINER (button), GTK_WIDGET (hbox));

#ifdef XFCE_PLUGIN
  xfce_panel_plugin_menu_show_about (button->priv->plugin);
  g_signal_connect (button->priv->plugin, "about", G_CALLBACK (about_cb), NULL);

  g_signal_connect (button->priv->plugin, "size-changed",
                    G_CALLBACK (power_manager_button_size_changed_cb), button);

  g_signal_connect (button->priv->plugin, "style-updated",
                    G_CALLBACK (power_manager_button_style_update_cb), button);

  g_signal_connect (button->priv->plugin, "free-data",
                    G_CALLBACK (power_manager_button_free_data_cb), button);
#endif

  gtk_widget_show_all (GTK_WIDGET(button));

  gtk_widget_set_visible (button->priv->panel_presentation_mode, button->priv->presentation_mode &&
                                                                 button->priv->show_presentation_indicator);
  power_manager_button_update_label (button, button->priv->display_device);
  power_manager_button_set_tooltip (button);

  /* Add all the devcies currently attached to the system */
  power_manager_button_add_all_devices (button);
}

#ifdef XFCE_PLUGIN
static void
power_manager_button_update_presentation_indicator (PowerManagerButton *button)
{
  gtk_image_set_pixel_size (GTK_IMAGE (button->priv->panel_presentation_mode), button->priv->panel_icon_width);

  gtk_widget_set_visible (button->priv->panel_presentation_mode, button->priv->presentation_mode &&
                                                                 button->priv->show_presentation_indicator);
}
#endif

static void
power_manager_button_update_label (PowerManagerButton *button, UpDevice *device)
{
  guint state;
  gdouble percentage;
  guint64 time_to_empty;
  guint64 time_to_full;

  if (!POWER_MANAGER_IS_BUTTON (button) || !UP_IS_DEVICE (device))
      return;

#ifdef XFCE_PLUGIN
  if (button->priv->show_panel_label <= 0 || button->priv->show_panel_label >3)
  {
    gtk_widget_hide (GTK_WIDGET (button->priv->panel_label));
    power_manager_button_size_changed_cb (button->priv->plugin,
                                          xfce_panel_plugin_get_size (button->priv->plugin),
                                          button);
    return;
  }
  else
    gtk_widget_show (GTK_WIDGET (button->priv->panel_label));
#endif

  g_object_get (device,
                "state", &state,
                "percentage", &percentage,
                "time-to-empty", &time_to_empty,
                "time-to-full", &time_to_full,
                NULL);

  /* Hide the label if the battery is fully charged,
   * if the state is unknown (no battery available)
     or if it's a desktop system */
  if (state == UP_DEVICE_STATE_CHARGING)
    power_manager_button_set_label (button, percentage, time_to_full);
  else if (state == UP_DEVICE_STATE_FULLY_CHARGED
           || state == UP_DEVICE_STATE_UNKNOWN
           || g_strcmp0 (button->priv->panel_icon_name, "ac-adapter-symbolic") == 0
           || g_strcmp0 (button->priv->panel_fallback_icon_name, "ac-adapter-symbolic") == 0)
    gtk_widget_hide (GTK_WIDGET (button->priv->panel_label));
  else
    power_manager_button_set_label (button, percentage, time_to_empty);
}

static void
menu_destroyed_cb(GtkMenuShell *menu, gpointer user_data)
{
  PowerManagerButton *button = POWER_MANAGER_BUTTON (user_data);

  TRACE("entering");

  /* menu destroyed, range slider is gone */
  button->priv->range = NULL;

  /* untoggle panel icon */
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), FALSE);

  gtk_menu_detach (GTK_MENU (button->priv->menu));

  button->priv->menu = NULL;
}

static void
menu_item_destroyed_cb(GtkWidget *object, gpointer user_data)
{
  PowerManagerButton *button = POWER_MANAGER_BUTTON (user_data);
  GList *item;

  for (item = g_list_first (button->priv->devices); item != NULL; item = g_list_next (item))
  {
    BatteryDevice *battery_device = item->data;

    if (battery_device->menu_item == object)
    {
      battery_device->menu_item = NULL;
      return;
    }
  }
}

static void
menu_item_activate_cb(GtkWidget *object, gpointer user_data)
{
  PowerManagerButton *button = POWER_MANAGER_BUTTON (user_data);
  GList *item;

  for (item = g_list_first (button->priv->devices); item != NULL; item = g_list_next (item))
  {
    BatteryDevice *battery_device = item->data;

    if (battery_device->menu_item == object)
    {
      /* Call xfpm settings with the device id */
      xfpm_preferences_device_id (battery_device->object_path);
      return;
    }
  }
}

static gboolean
power_manager_button_menu_add_device (PowerManagerButton *button, BatteryDevice *battery_device, gboolean append)
{
  GtkWidget *mi, *label;
  guint type = 0;

  g_return_val_if_fail (POWER_MANAGER_IS_BUTTON (button), FALSE);

  /* We need a menu to attach it to */
  g_return_val_if_fail (button->priv->menu, FALSE);

  if (UP_IS_DEVICE (battery_device->device))
  {
    g_object_get (battery_device->device,
                  "kind", &type,
                  NULL);

    /* Don't add the display device or line power to the menu */
    if (type == UP_DEVICE_KIND_LINE_POWER || battery_device->device == button->priv->display_device)
    {
      DBG("filtering device from menu (display or line power device)");
      return FALSE;
    }
  }
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  mi = gtk_image_menu_item_new_with_label(battery_device->details);
G_GNUC_END_IGNORE_DEPRECATIONS
  /* Make the menu item be bold and multi-line */
  label = gtk_bin_get_child (GTK_BIN (mi));
  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
  /* add the image */
  battery_device->img = gtk_image_new_from_surface (battery_device->surface);
  g_object_ref (battery_device->img);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), battery_device->img);
G_GNUC_END_IGNORE_DEPRECATIONS
  /* keep track of the menu item in the battery_device so we can update it */
  battery_device->menu_item = mi;
  g_signal_connect(G_OBJECT (mi), "destroy", G_CALLBACK (menu_item_destroyed_cb), button);
  battery_device->expose_signal_id = g_signal_connect_after (G_OBJECT (battery_device->img),
                                                             "draw",
                                                             G_CALLBACK (power_manager_button_device_icon_draw),
                                                             battery_device->device);

  /* Active calls xfpm settings with the device's id to display details */
  g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menu_item_activate_cb), button);

  /* Add it to the menu */
  gtk_widget_show (mi);
  if (append)
    gtk_menu_shell_append (GTK_MENU_SHELL(button->priv->menu), mi);
  else
    gtk_menu_shell_prepend (GTK_MENU_SHELL(button->priv->menu), mi);

  return TRUE;
}

static void
add_inhibitor_to_menu (PowerManagerButton *button, const gchar *text)
{
  GtkWidget *mi, *img;

  /* Translators this is to display which app is inhibiting
   * power in the plugin menu. Example:
   * VLC is currently inhibiting power management
   */
  gchar *label = g_strdup_printf (_("%s is currently inhibiting power management"), text);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  mi = gtk_image_menu_item_new_with_label(label);
G_GNUC_END_IGNORE_DEPRECATIONS
  /* add the image */
  img = gtk_image_new_from_icon_name ("dialog-information", GTK_ICON_SIZE_MENU);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
G_GNUC_END_IGNORE_DEPRECATIONS

  gtk_widget_set_can_focus (mi, FALSE);
  gtk_widget_show (mi);
  gtk_menu_shell_append (GTK_MENU_SHELL(button->priv->menu), mi);
  g_free (label);
}

#ifdef XFCE_PLUGIN
static void
display_inhibitors (PowerManagerButton *button, GtkWidget *menu)
{
  GtkWidget *separator_mi;
  gboolean needs_seperator = FALSE;

  g_return_if_fail (POWER_MANAGER_IS_BUTTON (button));
  g_return_if_fail (GTK_IS_MENU (menu));

  if (button->priv->inhibit_proxy)
  {
    GVariant *reply;
    GError   *error = NULL;

    reply = g_dbus_proxy_call_sync (button->priv->inhibit_proxy,
                                    "GetInhibitors",
                                    g_variant_new ("()"),
                                    G_DBUS_CALL_FLAGS_NONE,
                                    1000,
                                    NULL,
                                    &error);

    if (reply != NULL)
    {
      GVariantIter *iter;
      gchar        *value;

      g_variant_get (reply, "(as)", &iter);

      if (g_variant_iter_n_children (iter) > 0)
      {
        needs_seperator = TRUE;
      }

      /* Add the list of programs to the menu */
      while (g_variant_iter_next (iter, "s", &value))
      {
        add_inhibitor_to_menu (button, value);
      }
      g_variant_iter_free (iter);
      g_variant_unref (reply);

    } else {
      g_warning ("failed calling GetInhibitors: %s", error->message);
      g_clear_error (&error);
    }

    if (needs_seperator)
    {
      /* add a separator */
      separator_mi = gtk_separator_menu_item_new ();
      gtk_widget_show (separator_mi);
      gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator_mi);
    }
  }
}
#else
static void
display_inhibitors (PowerManagerButton *button, GtkWidget *menu)
{
  guint i;
  GtkWidget *separator_mi;
  const gchar **inhibitors;

  g_return_if_fail (POWER_MANAGER_IS_BUTTON (button));
  g_return_if_fail (GTK_IS_MENU (menu));

  inhibitors = xfpm_inhibit_get_inhibit_list (button->priv->inhibit);
  if (inhibitors != NULL && inhibitors[0] != NULL)
  {
    for (i=0; inhibitors[i] != NULL; i++)
    {
      add_inhibitor_to_menu (button, inhibitors[i]);
    }

    /* add a separator */
    separator_mi = gtk_separator_menu_item_new ();
    gtk_widget_show (separator_mi);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator_mi);
  }

  g_free (inhibitors);
}
#endif

static void
decrease_brightness (PowerManagerButton *button)
{
  gint32 level, next_level;


  TRACE("entering");

  if ( !xfpm_brightness_has_hw (button->priv->brightness) )
    return;

  xfpm_brightness_get_level (button->priv->brightness, &level);
  next_level = MAX (button->priv->brightness_min_level,
                    xfpm_brightness_dec (button->priv->brightness, level));

  xfpm_brightness_set_level(button->priv->brightness, next_level);
  if (button->priv->range)
    gtk_range_set_value (GTK_RANGE (button->priv->range), next_level);
}

static void
increase_brightness (PowerManagerButton *button)
{
  gint32 level, next_level;

  TRACE("entering");

  if (!xfpm_brightness_has_hw (button->priv->brightness))
    return;

  xfpm_brightness_get_level (button->priv->brightness, &level);
  next_level = xfpm_brightness_inc (button->priv->brightness, level);

  xfpm_brightness_set_level (button->priv->brightness, next_level);
  if (button->priv->range)
      gtk_range_set_value (GTK_RANGE (button->priv->range), next_level);
}

static gboolean
brightness_set_level_with_timeout (PowerManagerButton *button)
{
  gint32 range_level, hw_level;

  TRACE("entering");

  range_level = (gint32) gtk_range_get_value (GTK_RANGE (button->priv->range));

  xfpm_brightness_get_level (button->priv->brightness, &hw_level);

  if (hw_level != range_level)
  {
    xfpm_brightness_set_level (button->priv->brightness, range_level);
  }

  if (button->priv->set_level_timeout)
  {
    g_source_remove(button->priv->set_level_timeout);
    button->priv->set_level_timeout = 0;
  }

  return FALSE;
}

static void
range_value_changed_cb (PowerManagerButton *button, GtkWidget *widget)
{
  TRACE("entering");

  if (button->priv->set_level_timeout)
    return;

  button->priv->set_level_timeout =
    g_timeout_add (SET_LEVEL_TIMEOUT,
                   (GSourceFunc) brightness_set_level_with_timeout, button);
}

static void
range_scroll_cb (GtkWidget *widget, GdkEvent *event, PowerManagerButton *button)
{
  GdkEventScroll *scroll_event;

  TRACE("entering");

  scroll_event = (GdkEventScroll*)event;

  if (scroll_event->direction == GDK_SCROLL_UP)
    increase_brightness (button);
  else if (scroll_event->direction == GDK_SCROLL_DOWN)
    decrease_brightness (button);
}

static void
range_show_cb (GtkWidget *widget, PowerManagerButton *button)
{
  GdkSeat   *seat = gdk_display_get_default_seat (gdk_display_get_default());
  GdkDevice *pointer = gdk_seat_get_pointer (seat);

  TRACE("entering");
  /* Release these grabs as they will cause a lockup if pkexec is called
   * for the brightness helper */
  if (pointer)
  {
    gdk_seat_ungrab (seat);
  }

  gtk_grab_remove (widget);
}

#ifdef XFCE_PLUGIN
static void
power_manager_button_toggle_presentation_mode (GtkMenuItem *mi, GtkSwitch *sw)
{
  g_return_if_fail (GTK_IS_SWITCH (sw));

  gtk_switch_set_active (sw, !gtk_switch_get_active (sw));
}
#endif


void
power_manager_button_show_menu (PowerManagerButton *button)
{
  GtkWidget *menu, *mi, *img = NULL;
#ifdef XFCE_PLUGIN
  GtkWidget *box, *label, *sw;
#endif
  GdkScreen *gscreen;
  GList *item;
  gboolean show_separator_flag = FALSE;
  gint32 max_level, current_level = 0;

  TRACE("entering");

  g_return_if_fail (POWER_MANAGER_IS_BUTTON (button));

  if (gtk_widget_has_screen (GTK_WIDGET (button)))
      gscreen = gtk_widget_get_screen(GTK_WIDGET(button));
  else
      gscreen = gdk_display_get_default_screen(gdk_display_get_default());

  menu = gtk_menu_new ();
  gtk_menu_set_screen(GTK_MENU(menu), gscreen);
  /* keep track of the menu while it's being displayed */
  button->priv->menu = menu;
  g_signal_connect(GTK_MENU_SHELL(menu), "deactivate", G_CALLBACK(menu_destroyed_cb), button);
  gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (button), NULL);

  for (item = g_list_first (button->priv->devices); item != NULL; item = g_list_next (item))
  {
    BatteryDevice *battery_device = item->data;

    if (power_manager_button_menu_add_device (button, battery_device, TRUE))
    {
      /* If we add an item to the menu, show the separator */
      show_separator_flag = TRUE;
    }
  }

  if (show_separator_flag)
  {
    /* separator */
    mi = gtk_separator_menu_item_new ();
    gtk_widget_show (mi);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
  }

  /* Display brightness slider - show if there's hardware support for it */
  if ( xfpm_brightness_has_hw (button->priv->brightness) )
  {
    guint brightness_step_count;
    gboolean brightness_exponential;

    max_level = xfpm_brightness_get_max_level (button->priv->brightness);

    /* Setup brightness steps */
    brightness_step_count =
      xfconf_channel_get_uint (button->priv->channel,
                               XFPM_PROPERTIES_PREFIX BRIGHTNESS_STEP_COUNT,
                               10);
    brightness_exponential =
      xfconf_channel_get_bool (button->priv->channel,
                               XFPM_PROPERTIES_PREFIX BRIGHTNESS_EXPONENTIAL,
                               FALSE);
    xfpm_brightness_set_step_count (button->priv->brightness,
                                    brightness_step_count,
                                    brightness_exponential);

    mi = scale_menu_item_new_with_range (button->priv->brightness_min_level, max_level, 1);

    scale_menu_item_set_description_label (SCALE_MENU_ITEM (mi), _("<b>Display brightness</b>"));

    /* range slider */
    button->priv->range = scale_menu_item_get_scale (SCALE_MENU_ITEM (mi));

    /* update the slider to the current brightness level */
    xfpm_brightness_get_level (button->priv->brightness, &current_level);
    gtk_range_set_value (GTK_RANGE (button->priv->range), current_level);

    g_signal_connect_swapped (mi, "value-changed", G_CALLBACK (range_value_changed_cb), button);
    g_signal_connect (mi, "scroll-event", G_CALLBACK (range_scroll_cb), button);
    g_signal_connect (menu, "show", G_CALLBACK (range_show_cb), button);

    /* load and display the brightness icon and force it to 32px size */
    img = gtk_image_new_from_icon_name (XFPM_DISPLAY_BRIGHTNESS_ICON, GTK_ICON_SIZE_DND);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM(mi), img);
G_GNUC_END_IGNORE_DEPRECATIONS
    gtk_image_set_pixel_size (GTK_IMAGE (img), 32);
    gtk_widget_show_all (mi);
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
  }

  /* Presentation mode checkbox */
#ifdef XFCE_PLUGIN
  mi = gtk_menu_item_new ();
  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
  label = gtk_label_new_with_mnemonic (_("Presentation _mode"));
  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
  sw = gtk_switch_new ();
  gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (box), sw, FALSE, FALSE, 0);
  gtk_container_add (GTK_CONTAINER (mi), box);
  g_signal_connect (G_OBJECT (mi), "activate", G_CALLBACK (power_manager_button_toggle_presentation_mode), sw);
  g_object_bind_property (G_OBJECT (button), PRESENTATION_MODE,
                          G_OBJECT (sw), "active",
                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
#else
  mi = gtk_check_menu_item_new_with_mnemonic (_("Presentation _mode"));
  gtk_widget_set_sensitive (mi, TRUE);
  xfconf_g_property_bind (button->priv->channel,
                          XFPM_PROPERTIES_PREFIX PRESENTATION_MODE,
                          G_TYPE_BOOLEAN, G_OBJECT (mi), "active");
#endif
  gtk_widget_show_all (mi);
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);

  /* Show any applications currently inhibiting now */
  display_inhibitors (button, menu);

  /* Power manager settings */
  mi = gtk_menu_item_new_with_mnemonic (_("_Settings..."));
  gtk_widget_show (mi);
  gtk_menu_shell_append (GTK_MENU_SHELL(menu), mi);
  g_signal_connect (G_OBJECT(mi), "activate", G_CALLBACK(xfpm_preferences), NULL);

  gtk_menu_popup_at_widget (GTK_MENU (menu),
                            GTK_WIDGET (button),
#ifdef XFCE_PLUGIN
                            xfce_panel_plugin_get_orientation (button->priv->plugin) == GTK_ORIENTATION_VERTICAL
                            ? GDK_GRAVITY_WEST : GDK_GRAVITY_NORTH,
                            xfce_panel_plugin_get_orientation (button->priv->plugin) == GTK_ORIENTATION_VERTICAL
                            ? GDK_GRAVITY_EAST : GDK_GRAVITY_SOUTH,
#else
                            GDK_GRAVITY_NORTH,
                            GDK_GRAVITY_SOUTH,
#endif
                            NULL);

#ifdef XFCE_PLUGIN
  xfce_panel_plugin_register_menu (button->priv->plugin,
                                   GTK_MENU (menu));
#endif
}