Newer
Older
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
*
* 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 <dbus/dbus-glib.h>
#include <upower.h>
#include "common/xfpm-common.h"
#include "common/xfpm-icons.h"
#include "common/xfpm-power-common.h"
#include "battery-button.h"
#define BATTERY_BUTTON_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), BATTERY_TYPE_BUTTON, BatteryButtonPrivate))
struct BatteryButtonPrivate
{
XfcePanelPlugin *plugin;
UpClient *upower;
/* A list of BatteryDevices */
GList *devices;
/* The left-click popup menu, if one is being displayed */
GtkWidget *menu;
/* Keep track of icon name to redisplay during size changes */
/* Keep track of the last icon size for use during updates */
/* Upower 0.99 has a display device that can be used for the
* panel image and tooltip description */
UpDevice *display_device;
};
enum
{
PROP_0,
PROP_PLUGIN
};
GdkPixbuf *pix; /* Icon */
gchar *details; /* Description of the device + state */
gchar *object_path; /* UpDevice object path */
UpDevice *device; /* Pointer to the UpDevice */
gulong signal_id; /* device changed callback id */
GtkWidget *menu_item; /* The device's item on the menu (if shown) */
G_DEFINE_TYPE (BatteryButton, battery_button, GTK_TYPE_BUTTON)
static void battery_button_finalize (GObject *object);
static gchar* get_device_description (BatteryButton *button, UpDevice *device);
static GList* find_device_in_list (BatteryButton *button, const gchar *object_path);
static gboolean battery_button_set_icon (BatteryButton *button);
static void battery_button_clicked (GtkButton *b);
static void battery_button_show_menu (BatteryButton *button);
static void battery_button_menu_add_device (BatteryButton *button, BatteryDevice *battery_device, gboolean append);
static void
battery_button_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BatteryButton *button = BATTERY_BUTTON (object);
switch (prop_id)
{
case PROP_PLUGIN:
button->priv->plugin = XFCE_PANEL_PLUGIN (g_object_ref (g_value_get_object (value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
battery_button_set_tooltip (BatteryButton *button)
{
if (button->priv->display_device)
{
GList *item = find_device_in_list (button, up_device_get_object_path (button->priv->display_device));
if (item)
{
BatteryDevice *battery_device = item->data;
gtk_widget_set_tooltip_markup (GTK_WIDGET (button), battery_device->details);
return;
}
}
gtk_widget_set_tooltip_text (GTK_WIDGET (button), _("Display battery levels for attached devices"));
}
static gchar*
get_device_description (BatteryButton *button, UpDevice *device)
{
gchar *tip = NULL;
gchar *est_time_str = NULL;
guint type = 0, state = 0;
gchar *model = NULL, *vendor = NULL;
gboolean present;
gdouble percentage;
guint64 time_to_empty, time_to_full;
/* hack, this depends on XFPM_DEVICE_TYPE_* being in sync with UP_DEVICE_KIND_* */
g_object_get (device,
"kind", &type,
"vendor", &vendor,
"model", &model,
"state", &state,
"is-present", &present,
"percentage", &percentage,
"time-to-empty", &time_to_empty,
"time-to-full", &time_to_full,
NULL);
if (type == UP_DEVICE_KIND_LINE_POWER)
{
tip = g_strdup_printf(_("<b>Plugged In</b>\t"));
tip = g_strdup_printf(_("<b>On Battery</b>\t"));
}
return tip;
}
if (device == button->priv->display_device)
{
vendor = g_strdup (_("Computer"));
}
if ( state == UP_DEVICE_STATE_FULLY_CHARGED )
{
if ( time_to_empty > 0 )
{
est_time_str = xfpm_battery_get_time_string (time_to_empty);
tip = g_strdup_printf (_("<b>%s %s</b>\t\nFully charged (%0.0f%%, %s runtime).\t"),
vendor, model,
percentage,
est_time_str);
g_free (est_time_str);
}
else
{
tip = g_strdup_printf (_("<b>%s %s</b>\t\nFully charged (%0.0f%%).\t"),
vendor, model,
percentage);
}
}
else if ( state == UP_DEVICE_STATE_CHARGING )
{
if ( time_to_full != 0 )
{
est_time_str = xfpm_battery_get_time_string (time_to_full);
tip = g_strdup_printf (_("<b>%s %s</b>\t\nCharging (%0.0f%%, %s).\t"),
vendor, model,
percentage,
est_time_str);
g_free (est_time_str);
}
else
{
tip = g_strdup_printf (_("<b>%s %s</b>\t\nCharging (%0.0f%%).\t"),
vendor, model,
percentage);
}
}
else if ( state == UP_DEVICE_STATE_DISCHARGING )
{
if ( time_to_empty != 0 )
{
est_time_str = xfpm_battery_get_time_string (time_to_empty);
tip = g_strdup_printf (_("<b>%s %s</b>\t\nDischarging (%0.0f%%, %s).\t"),
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
vendor, model,
percentage,
est_time_str);
g_free (est_time_str);
}
else
{
tip = g_strdup_printf (_("<b>%s %s</b>\t\nDischarging (%0.0f%%).\t"),
vendor, model,
percentage);
}
}
else if ( state == UP_DEVICE_STATE_PENDING_CHARGE )
{
tip = g_strdup_printf (_("<b>%s %s</b>\t\nWaiting to discharge (%0.0f%%).\t"),
vendor, model,
percentage);
}
else if ( state == UP_DEVICE_STATE_PENDING_DISCHARGE )
{
tip = g_strdup_printf (_("<b>%s %s</b>\t\nWaiting to charge (%0.0f%%).\t"),
vendor, model,
percentage);
}
else if ( state == UP_DEVICE_STATE_EMPTY )
{
tip = g_strdup_printf (_("<b>%s %s</b>\t\nis empty\t"),
vendor, model);
}
else
{
/* unknown device state, just display the percentage */
tip = g_strdup_printf (_("<b>%s %s</b>\t\nis at (%0.0f%%).\t"),
vendor, model,
percentage);
}
return tip;
}
static GList*
find_device_in_list (BatteryButton *button, const gchar *object_path)
TRACE("entering");
g_return_val_if_fail ( BATTERY_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 (g_strcmp0 (battery_device->object_path, object_path) == 0)
return item;
}
return NULL;
}
static void
battery_button_update_device_icon_and_details (BatteryButton *button, UpDevice *device)
GList *item;
BatteryDevice *battery_device;
const gchar *object_path = up_device_get_object_path(device);
gchar *details, *icon_name;
GdkPixbuf *pix;
TRACE("entering for %s", object_path);
g_return_if_fail ( BATTERY_IS_BUTTON (button) );
item = find_device_in_list (button, object_path);
/* hack, this depends on XFPM_DEVICE_TYPE_* being in sync with UP_DEVICE_KIND_* */
g_object_get (device,
"kind", &type,
NULL);
icon_name = get_device_icon_name (button->priv->upower, device);
details = get_device_description(button, device);
/* Add AC power status to display device since it replaces the line
* power menu item in UPower 0.99 */
if (button->priv->display_device == device)
{
gboolean online;
gchar *tip;
g_object_get (device,
"online", &online,
NULL);
if ( online )
{
tip = g_strdup_printf(_("<b>Plugged In</b>\t\n%s"), details);
}
else
{
tip = g_strdup_printf(_("<b>On Battery</b>\t\n%s"), details);
}
g_free (details);
details = tip;
}
pix = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
icon_name,
48,
GTK_ICON_LOOKUP_USE_BUILTIN,
NULL);
if (battery_device->details)
g_free(battery_device->details);
if (battery_device->pix)
g_object_unref (battery_device->pix);
battery_device->pix = pix;
if ( type == UP_DEVICE_KIND_LINE_POWER || device == button->priv->display_device)
/* Update the panel icon with priority to the display device */
if (!button->priv->display_device || device == button->priv->display_device)
{
g_free(button->priv->panel_icon_name);
button->priv->panel_icon_name = icon_name;
battery_button_set_icon (button);
/* update tooltip */
battery_button_set_tooltip (button);
}
/* If the menu is being displayed, update it */
if (button->priv->menu && battery_device->menu_item)
{
GtkWidget *img;
gtk_menu_item_set_label (GTK_MENU_ITEM (battery_device->menu_item), details);
/* update the image */
img = gtk_image_new_from_pixbuf(battery_device->pix);
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(battery_device->menu_item), img);
}
static void
#if UP_CHECK_VERSION(0, 99, 0)
device_changed_cb (UpDevice *device, GParamSpec *pspec, BatteryButton *button)
#else
device_changed_cb (UpDevice *device, BatteryButton *button)
#endif
{
battery_button_update_device_icon_and_details (button, device);
}
static void
battery_button_add_device (UpDevice *device, BatteryButton *button)
{
const gchar *object_path = up_device_get_object_path(device);
TRACE("entering for %s", object_path);
g_return_if_fail ( BATTERY_IS_BUTTON (button ) );
/* don't add the same device twice */
if ( find_device_in_list (button, object_path) )
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);
/* If there's a display device it will also show the power state so
* avoid adding a duplicate icon */
if (type == UP_DEVICE_KIND_LINE_POWER && button->priv->display_device)
{
g_free (battery_device);
return;
}
#if UP_CHECK_VERSION(0, 99, 0)
signal_id = g_signal_connect (device, "notify", G_CALLBACK (device_changed_cb), button);
#else
signal_id = g_signal_connect (device, "changed", G_CALLBACK (device_changed_cb), button);
#endif
/* populate the struct */
battery_device->object_path = g_strdup (object_path);
battery_device->signal_id = signal_id;
battery_device->device = device;
/* add it to the list */
if ( type == UP_DEVICE_KIND_LINE_POWER || device == button->priv->display_device)
/* The PC's plugged in status and display device show up first */
button->priv->devices = g_list_prepend (button->priv->devices, battery_device);
button->priv->devices = g_list_append (button->priv->devices, battery_device);
/* Add the icon and description for the device */
battery_button_update_device_icon_and_details (button, device);
/* If the menu is being shown, add this new device to it */
if (button->priv->menu)
{
battery_button_menu_add_device (button, battery_device, FALSE);
}
}
static void
battery_button_remove_device (BatteryButton *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 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);
if (battery_device->pix)
g_object_unref (battery_device->pix);
g_free(battery_device->details);
g_free(battery_device->object_path);
if (battery_device->device)
{
if (battery_device->signal_id)
g_signal_handler_disconnect (battery_device->device, battery_device->signal_id);
g_object_unref (battery_device->device);
}
/* remove it item and free the battery device */
button->priv->devices = g_list_delete_link (button->priv->devices, item);
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
}
static void
device_added_cb (UpClient *upower, UpDevice *device, BatteryButton *button)
{
battery_button_add_device (device, button);
}
#if UP_CHECK_VERSION(0, 99, 0)
static void
device_removed_cb (UpClient *upower, const gchar *object_path, BatteryButton *button)
{
battery_button_remove_device (button, object_path);
}
#else
static void
device_removed_cb (UpClient *upower, UpDevice *device, BatteryButton *button)
{
const gchar *object_path = up_device_get_object_path(device);
battery_button_remove_device (button, object_path);
}
#endif
static void
battery_button_add_all_devices (BatteryButton *button)
{
#if !UP_CHECK_VERSION(0, 99, 0)
/* the device-add callback is called for each device */
up_client_enumerate_devices_sync(button->priv->upower, NULL, NULL);
#else
GPtrArray *array = NULL;
guint i;
button->priv->display_device = up_client_get_display_device (button->priv->upower);
battery_button_add_device (button->priv->display_device, button);
array = up_client_get_devices(button->priv->upower);
if ( array )
{
for ( i = 0; i < array->len; i++)
{
UpDevice *device = g_ptr_array_index (array, i);
battery_button_add_device (device, button);
}
g_ptr_array_free (array, TRUE);
}
#endif
}
static void
battery_button_class_init (BatteryButtonClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
object_class->finalize = battery_button_finalize;
object_class->set_property = battery_button_set_property;
button_class->clicked = battery_button_clicked;
g_object_class_install_property (object_class,
PROP_PLUGIN,
g_param_spec_object ("plugin",
NULL,
NULL,
XFCE_TYPE_PANEL_PLUGIN,
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_WRITABLE));
g_type_class_add_private (klass, sizeof (BatteryButtonPrivate));
}
static void
battery_button_init (BatteryButton *button)
{
button->priv = BATTERY_BUTTON_GET_PRIVATE (button);
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
button->priv->upower = up_client_new ();
button->priv->panel_icon_name = g_strdup(XFPM_AC_ADAPTER_ICON);
button->priv->panel_icon_width = 24;
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
battery_button_finalize (GObject *object)
{
BatteryButton *button;
button = BATTERY_BUTTON (object);
g_free(button->priv->panel_icon_name);
g_signal_handlers_disconnect_by_data (button->priv->upower, button);
g_object_unref (button->priv->plugin);
G_OBJECT_CLASS (battery_button_parent_class)->finalize (object);
}
GtkWidget *
battery_button_new (XfcePanelPlugin *plugin)
{
BatteryButton *button = NULL;
button = g_object_new (BATTERY_TYPE_BUTTON, "plugin", plugin, NULL);
return GTK_WIDGET (button);
}
battery_button_set_icon (BatteryButton *button)
DBG("icon_width %d", button->priv->panel_icon_width);
pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
button->priv->panel_icon_name,
button->priv->panel_icon_width,
GTK_ICON_LOOKUP_FORCE_SIZE,
NULL);
if ( pixbuf )
{
gtk_image_set_from_pixbuf (GTK_IMAGE (button->priv->panel_icon_image), pixbuf);
g_object_unref (pixbuf);
return TRUE;
}
return FALSE;
}
static void
battery_button_clicked (GtkButton *b)
{
BatteryButton *button = BATTERY_BUTTON (b);
battery_button_show_menu (button);
}
static gboolean
battery_button_size_changed_cb (XfcePanelPlugin *plugin, gint size, BatteryButton *button)
{
gint width = size -2 - 2* MAX(gtk_widget_get_style(GTK_WIDGET(button))->xthickness,
gtk_widget_get_style(GTK_WIDGET(button))->ythickness);
gtk_widget_set_size_request (GTK_WIDGET(plugin), size, size);
button->priv->panel_icon_width = width;
return battery_button_set_icon (button);
}
static void
battery_button_free_data_cb (XfcePanelPlugin *plugin, BatteryButton *button)
{
gtk_widget_destroy (GTK_WIDGET (button));
}
static void
help_cb (GtkMenuItem *menuitem, gpointer user_data)
{
xfce_dialog_show_help (NULL, "xfce4-power-manager", "start", NULL);
void
battery_button_show (BatteryButton *button)
{
GtkWidget *mi;
g_return_if_fail (BATTERY_IS_BUTTON (button));
xfce_panel_plugin_add_action_widget (button->priv->plugin, GTK_WIDGET (button));
button->priv->panel_icon_image = gtk_image_new ();
gtk_container_add (GTK_CONTAINER (button), button->priv->panel_icon_image);
/* help dialog */
mi = gtk_image_menu_item_new_from_stock (GTK_STOCK_HELP, NULL);
gtk_widget_set_sensitive (mi, TRUE);
gtk_widget_show (mi);
g_signal_connect (mi, "activate", G_CALLBACK (help_cb), button);
xfce_panel_plugin_menu_insert_item (button->priv->plugin, GTK_MENU_ITEM (mi));
g_signal_connect (button->priv->plugin, "size-changed",
G_CALLBACK (battery_button_size_changed_cb), button);
g_signal_connect (button->priv->plugin, "free-data",
G_CALLBACK (battery_button_free_data_cb), button);
gtk_widget_show_all (GTK_WIDGET(button));
battery_button_set_tooltip (button);
/* Add all the devcies currently attached to the system */
battery_button_add_all_devices (button);
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
static void
menu_destroyed_cb(GtkWidget *object, gpointer user_data)
{
BatteryButton *button = BATTERY_BUTTON (user_data);
button->priv->menu = NULL;
}
static void
menu_item_destroyed_cb(GtkWidget *object, gpointer user_data)
{
BatteryButton *button = BATTERY_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
battery_button_menu_add_device (BatteryButton *button, BatteryDevice *battery_device, gboolean append)
{
GtkWidget *mi, *label, *img;
/* We need a menu to attach it to */
g_return_if_fail (button->priv->menu);
mi = gtk_image_menu_item_new_with_label(battery_device->details);
/* 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 */
img = gtk_image_new_from_pixbuf(battery_device->pix);
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
/* 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);
/* 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);
}
static void
battery_button_show_menu (BatteryButton *button)
{
GdkScreen *gscreen;
GList *item;
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(G_OBJECT(menu), "destroy", G_CALLBACK(menu_destroyed_cb), button);
for (item = g_list_first (button->priv->devices); item != NULL; item = g_list_next (item))
{
BatteryDevice *battery_device = item->data;
battery_button_menu_add_device (button, battery_device, TRUE);
}
/* separator */
mi = gtk_separator_menu_item_new();
gtk_widget_show(mi);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
/* Preferences option */
mi = gtk_menu_item_new_with_mnemonic ("_Preferences...");
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 (GTK_MENU (menu),
NULL,
NULL,
xfce_panel_plugin_position_menu,
button->priv->plugin,
0,
gtk_get_current_event_time ());
}