diff options
author | Stefano Karapetsas <[email protected]> | 2011-12-11 12:55:19 +0100 |
---|---|---|
committer | Stefano Karapetsas <[email protected]> | 2011-12-11 12:55:19 +0100 |
commit | 51175189c6d7313a3b84019e39496f957c4e6164 (patch) | |
tree | e4c2c130fa3140bca28685ef900f04a012e53dcd /src/gpm-statistics.c | |
download | mate-power-manager-51175189c6d7313a3b84019e39496f957c4e6164.tar.bz2 mate-power-manager-51175189c6d7313a3b84019e39496f957c4e6164.tar.xz |
moved from Mate-Extra
Diffstat (limited to 'src/gpm-statistics.c')
-rw-r--r-- | src/gpm-statistics.c | 1829 |
1 files changed, 1829 insertions, 0 deletions
diff --git a/src/gpm-statistics.c b/src/gpm-statistics.c new file mode 100644 index 0000000..7ac6f2e --- /dev/null +++ b/src/gpm-statistics.c @@ -0,0 +1,1829 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 Richard Hughes <[email protected]> + * + * 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. + */ + +#include "config.h" + +#include <locale.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include <gtk/gtk.h> +#include <dbus/dbus-glib.h> +#include <mateconf/mateconf-client.h> +#include <libupower-glib/upower.h> + +#include "egg-debug.h" +#include "egg-color.h" +#include "egg-array-float.h" +#include "egg-unique.h" + +#include "gpm-common.h" +#include "gpm-stock-icons.h" +#include "gpm-upower.h" +#include "gpm-graph-widget.h" + +static GtkBuilder *builder = NULL; +static GtkListStore *list_store_info = NULL; +static GtkListStore *list_store_devices = NULL; +static GtkListStore *list_store_wakeups = NULL; +gchar *current_device = NULL; +static const gchar *history_type; +static const gchar *stats_type; +static guint history_time; +static MateConfClient *mateconf_client; +static gfloat sigma_smoothing = 0.0f; +static UpWakeups *wakeups = NULL; +static GtkWidget *graph_history = NULL; +static GtkWidget *graph_statistics = NULL; + +enum { + GPM_INFO_COLUMN_TEXT, + GPM_INFO_COLUMN_VALUE, + GPM_INFO_COLUMN_LAST +}; + +enum { + GPM_DEVICES_COLUMN_ICON, + GPM_DEVICES_COLUMN_TEXT, + GPM_DEVICES_COLUMN_ID, + GPM_DEVICES_COLUMN_LAST +}; + +enum { + GPM_WAKEUPS_COLUMN_ICON, + GPM_WAKEUPS_COLUMN_ID, + GPM_WAKEUPS_COLUMN_VALUE, + GPM_WAKEUPS_COLUMN_CMDLINE, + GPM_WAKEUPS_COLUMN_DETAILS, + GPM_WAKEUPS_COLUMN_LAST +}; + +#define GPM_HISTORY_RATE_TEXT _("Rate") +#define GPM_HISTORY_CHARGE_TEXT _("Charge") +#define GPM_HISTORY_TIME_FULL_TEXT _("Time to full") +#define GPM_HISTORY_TIME_EMPTY_TEXT _("Time to empty") + +#define GPM_HISTORY_RATE_VALUE "rate" +#define GPM_HISTORY_CHARGE_VALUE "charge" +#define GPM_HISTORY_TIME_FULL_VALUE "time-full" +#define GPM_HISTORY_TIME_EMPTY_VALUE "time-empty" + +#define GPM_HISTORY_MINUTE_TEXT _("10 minutes") +#define GPM_HISTORY_HOUR_TEXT _("2 hours") +#define GPM_HISTORY_HOURS_TEXT _("6 hours") +#define GPM_HISTORY_DAY_TEXT _("1 day") +#define GPM_HISTORY_WEEK_TEXT _("1 week") + +#define GPM_HISTORY_MINUTE_VALUE 10*60 +#define GPM_HISTORY_HOUR_VALUE 2*60*60 +#define GPM_HISTORY_HOURS_VALUE 6*60*60 +#define GPM_HISTORY_DAY_VALUE 24*60*60 +#define GPM_HISTORY_WEEK_VALUE 7*24*60*60 + +/* TRANSLATORS: what we've observed about the device */ +#define GPM_STATS_CHARGE_DATA_TEXT _("Charge profile") +#define GPM_STATS_DISCHARGE_DATA_TEXT _("Discharge profile") +/* TRANSLATORS: how accurately we can predict the time remaining of the battery */ +#define GPM_STATS_CHARGE_ACCURACY_TEXT _("Charge accuracy") +#define GPM_STATS_DISCHARGE_ACCURACY_TEXT _("Discharge accuracy") + +#define GPM_STATS_CHARGE_DATA_VALUE "charge-data" +#define GPM_STATS_CHARGE_ACCURACY_VALUE "charge-accuracy" +#define GPM_STATS_DISCHARGE_DATA_VALUE "discharge-data" +#define GPM_STATS_DISCHARGE_ACCURACY_VALUE "discharge-accuracy" + +/** + * gpm_stats_button_help_cb: + **/ +static void +gpm_stats_button_help_cb (GtkWidget *widget, gboolean data) +{ + gpm_help_display ("statistics"); +} + +/** + * gpm_stats_add_info_columns: + **/ +static void +gpm_stats_add_info_columns (GtkTreeView *treeview) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + /* image */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Attribute"), renderer, + "markup", GPM_INFO_COLUMN_TEXT, NULL); + gtk_tree_view_column_set_sort_column_id (column, GPM_INFO_COLUMN_TEXT); + gtk_tree_view_append_column (treeview, column); + + /* column for text */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Value"), renderer, + "markup", GPM_INFO_COLUMN_VALUE, NULL); + gtk_tree_view_append_column (treeview, column); +} + +/** + * gpm_stats_add_devices_columns: + **/ +static void +gpm_stats_add_devices_columns (GtkTreeView *treeview) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + /* image */ + renderer = gtk_cell_renderer_pixbuf_new (); + g_object_set (renderer, "stock-size", GTK_ICON_SIZE_DIALOG, NULL); + column = gtk_tree_view_column_new_with_attributes (_("Image"), renderer, + "icon-name", GPM_DEVICES_COLUMN_ICON, NULL); + gtk_tree_view_append_column (treeview, column); + + /* column for text */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Description"), renderer, + "markup", GPM_DEVICES_COLUMN_TEXT, NULL); + gtk_tree_view_column_set_sort_column_id (column, GPM_INFO_COLUMN_TEXT); + gtk_tree_view_append_column (treeview, column); + gtk_tree_view_column_set_expand (column, TRUE); +} + +/** + * gpm_stats_add_wakeups_columns: + **/ +static void +gpm_stats_add_wakeups_columns (GtkTreeView *treeview) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + /* image */ + renderer = gtk_cell_renderer_pixbuf_new (); + g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL); + column = gtk_tree_view_column_new_with_attributes (_("Type"), renderer, + "icon-name", GPM_WAKEUPS_COLUMN_ICON, NULL); + gtk_tree_view_append_column (treeview, column); + + /* column for id */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("ID"), renderer, + "markup", GPM_WAKEUPS_COLUMN_ID, NULL); + gtk_tree_view_append_column (treeview, column); + gtk_tree_view_column_set_expand (column, TRUE); + + /* column for value */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Wakeups"), renderer, + "markup", GPM_WAKEUPS_COLUMN_VALUE, NULL); + gtk_tree_view_append_column (treeview, column); + gtk_tree_view_column_set_expand (column, TRUE); + + /* column for cmdline */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Command"), renderer, + "markup", GPM_WAKEUPS_COLUMN_CMDLINE, NULL); + gtk_tree_view_append_column (treeview, column); + gtk_tree_view_column_set_expand (column, TRUE); + + /* column for details */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Details"), renderer, + "markup", GPM_WAKEUPS_COLUMN_DETAILS, NULL); + gtk_tree_view_append_column (treeview, column); + gtk_tree_view_column_set_expand (column, TRUE); +} + +/** + * gpm_stats_add_info_data: + **/ +static void +gpm_stats_add_info_data (const gchar *attr, const gchar *text) +{ + GtkTreeIter iter; + gtk_list_store_append (list_store_info, &iter); + gtk_list_store_set (list_store_info, &iter, + GPM_INFO_COLUMN_TEXT, attr, + GPM_INFO_COLUMN_VALUE, text, -1); +} + +/** + * gpm_stats_update_smooth_data: + **/ +static GPtrArray * +gpm_stats_update_smooth_data (GPtrArray *list) +{ + guint i; + GpmPointObj *point; + GpmPointObj *point_new; + GPtrArray *new; + EggArrayFloat *raw; + EggArrayFloat *convolved; + EggArrayFloat *outliers; + EggArrayFloat *gaussian = NULL; + + /* convert the y data to a EggArrayFloat array */ + raw = egg_array_float_new (list->len); + for (i=0; i<list->len; i++) { + point = (GpmPointObj *) g_ptr_array_index (list, i); + egg_array_float_set (raw, i, point->y); + } + + /* remove any outliers */ + outliers = egg_array_float_remove_outliers (raw, 3, 0.1); + + /* convolve with gaussian */ + gaussian = egg_array_float_compute_gaussian (15, sigma_smoothing); + convolved = egg_array_float_convolve (outliers, gaussian); + + /* add the smoothed data back into a new array */ + new = g_ptr_array_new_with_free_func ((GDestroyNotify) gpm_point_obj_free); + for (i=0; i<list->len; i++) { + point = (GpmPointObj *) g_ptr_array_index (list, i); + point_new = g_new0 (GpmPointObj, 1); + point_new->color = point->color; + point_new->x = point->x; + point_new->y = egg_array_float_get (convolved, i); + g_ptr_array_add (new, point_new); + } + + /* free data */ + egg_array_float_free (gaussian); + egg_array_float_free (raw); + egg_array_float_free (convolved); + egg_array_float_free (outliers); + + return new; +} + +/** + * gpm_stats_time_to_string: + **/ +static gchar * +gpm_stats_time_to_string (gint seconds) +{ + gfloat value = seconds; + + if (value < 0) { + /* TRANSLATORS: this is when the stats time is not known */ + return g_strdup (_("Unknown")); + } + if (value < 60) { + /* TRANSLATORS: this is a time value, usually to show on a graph */ + return g_strdup_printf (ngettext ("%.0f second", "%.0f seconds", value), value); + } + value /= 60.0; + if (value < 60) { + /* TRANSLATORS: this is a time value, usually to show on a graph */ + return g_strdup_printf (ngettext ("%.1f minute", "%.1f minutes", value), value); + } + value /= 60.0; + if (value < 60) { + /* TRANSLATORS: this is a time value, usually to show on a graph */ + return g_strdup_printf (ngettext ("%.1f hour", "%.1f hours", value), value); + } + value /= 24.0; + /* TRANSLATORS: this is a time value, usually to show on a graph */ + return g_strdup_printf (ngettext ("%.1f day", "%.1f days", value), value); +} + +/** + * gpm_stats_bool_to_string: + **/ +static const gchar * +gpm_stats_bool_to_string (gboolean ret) +{ + return ret ? _("Yes") : _("No"); +} + +/** + * gpm_stats_get_printable_device_path: + **/ +static gchar * +gpm_stats_get_printable_device_path (UpDevice *device) +{ + const gchar *object_path; + gchar *device_path = NULL; + + /* get object path */ + object_path = up_device_get_object_path (device); + if (object_path != NULL) + device_path = g_filename_display_basename (object_path); + + return device_path; +} + +/** + * gpm_stats_update_info_page_details: + **/ +static void +gpm_stats_update_info_page_details (UpDevice *device) +{ + struct tm *time_tm; + time_t t; + gchar time_buf[256]; + gchar *text; + guint refreshed; + UpDeviceKind kind; + UpDeviceState state; + UpDeviceTechnology technology; + gdouble percentage; + gdouble capacity; + gdouble energy; + gdouble energy_empty; + gdouble energy_full; + gdouble energy_full_design; + gdouble energy_rate; + gdouble voltage; + gboolean online; + gboolean is_present; + gboolean power_supply; + gboolean is_rechargeable; + guint64 update_time; + gint64 time_to_full; + gint64 time_to_empty; + gchar *vendor = NULL; + gchar *serial = NULL; + gchar *model = NULL; + gchar *device_path = NULL; + + gtk_list_store_clear (list_store_info); + + /* get device properties */ + g_object_get (device, + "kind", &kind, + "state", &state, + "percentage", &percentage, + "online", &online, + "update_time", &update_time, + "power_supply", &power_supply, + "is_rechargeable", &is_rechargeable, + "is-present", &is_present, + "time-to-full", &time_to_full, + "time-to-empty", &time_to_empty, + "technology", &technology, + "capacity", &capacity, + "energy", &energy, + "energy-empty", &energy_empty, + "energy-full", &energy_full, + "energy-full-design", &energy_full_design, + "energy-rate", &energy_rate, + "voltage", &voltage, + "vendor", &vendor, + "serial", &serial, + "model", &model, + NULL); + + /* get a human readable time */ + t = (time_t) update_time; + time_tm = localtime (&t); + strftime (time_buf, sizeof time_buf, "%c", time_tm); + + /* remove prefix */ + device_path = gpm_stats_get_printable_device_path (device); + /* TRANSLATORS: the device ID of the current device, e.g. "battery0" */ + gpm_stats_add_info_data (_("Device"), device_path); + g_free (device_path); + + gpm_stats_add_info_data (_("Type"), gpm_device_kind_to_localised_text (kind, 1)); + if (vendor != NULL && vendor[0] != '\0') + gpm_stats_add_info_data (_("Vendor"), vendor); + if (model != NULL && model[0] != '\0') + gpm_stats_add_info_data (_("Model"), model); + if (serial != NULL && serial[0] != '\0') + gpm_stats_add_info_data (_("Serial number"), serial); + + /* TRANSLATORS: a boolean attribute that means if the device is supplying the + * main power for the computer. For instance, an AC adapter or laptop battery + * would be TRUE, but a mobile phone or mouse taking power is FALSE */ + gpm_stats_add_info_data (_("Supply"), gpm_stats_bool_to_string (power_supply)); + + refreshed = (int) (time (NULL) - update_time); + text = g_strdup_printf (ngettext ("%d second", "%d seconds", refreshed), refreshed); + + /* TRANSLATORS: when the device was last updated with new data. It's + * usually a few seconds when a device is discharging or charging. */ + gpm_stats_add_info_data (_("Refreshed"), text); + g_free (text); + + if (kind == UP_DEVICE_KIND_BATTERY || + kind == UP_DEVICE_KIND_MOUSE || + kind == UP_DEVICE_KIND_KEYBOARD || + kind == UP_DEVICE_KIND_UPS) { + /* TRANSLATORS: Present is whether the device is currently attached + * to the computer, as some devices (e.g. laptop batteries) can + * be removed, but still observed as devices on the system */ + gpm_stats_add_info_data (_("Present"), gpm_stats_bool_to_string (is_present)); + } + if (kind == UP_DEVICE_KIND_BATTERY || + kind == UP_DEVICE_KIND_MOUSE || + kind == UP_DEVICE_KIND_KEYBOARD) { + /* TRANSLATORS: If the device can be recharged, e.g. lithium + * batteries rather than alkaline ones */ + gpm_stats_add_info_data (_("Rechargeable"), gpm_stats_bool_to_string (is_rechargeable)); + } + if (kind == UP_DEVICE_KIND_BATTERY || + kind == UP_DEVICE_KIND_MOUSE || + kind == UP_DEVICE_KIND_KEYBOARD) { + /* TRANSLATORS: The state of the device, e.g. "Changing" or "Fully charged" */ + gpm_stats_add_info_data (_("State"), gpm_device_state_to_localised_string (state)); + } + if (kind == UP_DEVICE_KIND_BATTERY) { + text = g_strdup_printf ("%.1f Wh", energy); + gpm_stats_add_info_data (_("Energy"), text); + g_free (text); + text = g_strdup_printf ("%.1f Wh", energy_empty); + gpm_stats_add_info_data (_("Energy when empty"), text); + g_free (text); + text = g_strdup_printf ("%.1f Wh", energy_full); + gpm_stats_add_info_data (_("Energy when full"), text); + g_free (text); + text = g_strdup_printf ("%.1f Wh", energy_full_design); + gpm_stats_add_info_data (_("Energy (design)"), text); + g_free (text); + } + if (kind == UP_DEVICE_KIND_BATTERY || + kind == UP_DEVICE_KIND_MONITOR) { + text = g_strdup_printf ("%.1f W", energy_rate); + /* TRANSLATORS: the rate of discharge for the device */ + gpm_stats_add_info_data (_("Rate"), text); + g_free (text); + } + if (kind == UP_DEVICE_KIND_UPS || + kind == UP_DEVICE_KIND_BATTERY || + kind == UP_DEVICE_KIND_MONITOR) { + text = g_strdup_printf ("%.1f V", voltage); + gpm_stats_add_info_data (_("Voltage"), text); + g_free (text); + } + if (kind == UP_DEVICE_KIND_BATTERY || + kind == UP_DEVICE_KIND_UPS) { + if (time_to_full >= 0) { + text = gpm_stats_time_to_string (time_to_full); + gpm_stats_add_info_data (_("Time to full"), text); + g_free (text); + } + if (time_to_empty >= 0) { + text = gpm_stats_time_to_string (time_to_empty); + gpm_stats_add_info_data (_("Time to empty"), text); + g_free (text); + } + } + if (kind == UP_DEVICE_KIND_BATTERY || + kind == UP_DEVICE_KIND_MOUSE || + kind == UP_DEVICE_KIND_KEYBOARD || + kind == UP_DEVICE_KIND_UPS) { + text = g_strdup_printf ("%.1f%%", percentage); + /* TRANSLATORS: the amount of charge the cell contains */ + gpm_stats_add_info_data (_("Percentage"), text); + g_free (text); + } + if (kind == UP_DEVICE_KIND_BATTERY) { + text = g_strdup_printf ("%.1f%%", capacity); + /* TRANSLATORS: the capacity of the device, which is basically a measure + * of how full it can get, relative to the design capacity */ + gpm_stats_add_info_data (_("Capacity"), text); + g_free (text); + } + if (kind == UP_DEVICE_KIND_BATTERY) { + /* TRANSLATORS: the type of battery, e.g. lithium or nikel metal hydroxide */ + gpm_stats_add_info_data (_("Technology"), gpm_device_technology_to_localised_string (technology)); + } + if (kind == UP_DEVICE_KIND_LINE_POWER) { + /* TRANSLATORS: this is when the device is plugged in, typically + * only shown for the ac adaptor device */ + gpm_stats_add_info_data (_("Online"), gpm_stats_bool_to_string (online)); + } + + g_free (vendor); + g_free (serial); + g_free (model); +} + +/** + * gpm_stats_set_graph_data: + **/ +static void +gpm_stats_set_graph_data (GtkWidget *widget, GPtrArray *data, gboolean use_smoothed, gboolean use_points) +{ + GPtrArray *smoothed; + + gpm_graph_widget_data_clear (GPM_GRAPH_WIDGET (widget)); + + /* add correct data */ + if (!use_smoothed) { + if (use_points) + gpm_graph_widget_data_assign (GPM_GRAPH_WIDGET (widget), GPM_GRAPH_WIDGET_PLOT_BOTH, data); + else + gpm_graph_widget_data_assign (GPM_GRAPH_WIDGET (widget), GPM_GRAPH_WIDGET_PLOT_LINE, data); + } else { + smoothed = gpm_stats_update_smooth_data (data); + if (use_points) + gpm_graph_widget_data_assign (GPM_GRAPH_WIDGET (widget), GPM_GRAPH_WIDGET_PLOT_POINTS, data); + gpm_graph_widget_data_assign (GPM_GRAPH_WIDGET (widget), GPM_GRAPH_WIDGET_PLOT_LINE, smoothed); + g_ptr_array_unref (smoothed); + } + + /* show */ + gtk_widget_show (widget); +} + +/** + * gpm_stats_update_info_page_history: + **/ +static void +gpm_stats_update_info_page_history (UpDevice *device) +{ + GPtrArray *array; + guint i; + UpHistoryItem *item; + GtkWidget *widget; + gboolean checked; + gboolean points; + GpmPointObj *point; + GPtrArray *new; + gint32 offset = 0; + GTimeVal timeval; + + new = g_ptr_array_new_with_free_func ((GDestroyNotify) gpm_point_obj_free); + if (g_strcmp0 (history_type, GPM_HISTORY_CHARGE_VALUE) == 0) { + g_object_set (graph_history, + "type-x", GPM_GRAPH_WIDGET_TYPE_TIME, + "type-y", GPM_GRAPH_WIDGET_TYPE_PERCENTAGE, + "autorange-x", FALSE, + "start-x", -history_time, + "stop-x", 0, + "autorange-y", FALSE, + "start-y", 0, + "stop-y", 100, + NULL); + } else if (g_strcmp0 (history_type, GPM_HISTORY_RATE_VALUE) == 0) { + g_object_set (graph_history, + "type-x", GPM_GRAPH_WIDGET_TYPE_TIME, + "type-y", GPM_GRAPH_WIDGET_TYPE_POWER, + "autorange-x", FALSE, + "start-x", -history_time, + "stop-x", 0, + "autorange-y", TRUE, + NULL); + } else { + g_object_set (graph_history, + "type-x", GPM_GRAPH_WIDGET_TYPE_TIME, + "type-y", GPM_GRAPH_WIDGET_TYPE_TIME, + "autorange-x", FALSE, + "start-x", -history_time, + "stop-x", 0, + "autorange-y", TRUE, + NULL); + } + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_history_nodata")); + array = up_device_get_history_sync (device, history_type, history_time, 150, NULL, NULL); + if (array == NULL) { + /* show no data label and hide graph */ + gtk_widget_hide (graph_history); + gtk_widget_show (widget); + goto out; + } + + /* hide no data and show graph */ + gtk_widget_hide (widget); + gtk_widget_show (graph_history); + + g_get_current_time (&timeval); + offset = timeval.tv_sec; + + for (i=0; i<array->len; i++) { + item = (UpHistoryItem *) g_ptr_array_index (array, i); + + /* abandon this point */ + if (up_history_item_get_state (item) == UP_DEVICE_STATE_UNKNOWN) + continue; + + point = gpm_point_obj_new (); + point->x = (gint32) up_history_item_get_time (item) - offset; + point->y = up_history_item_get_value (item); + if (up_history_item_get_state (item) == UP_DEVICE_STATE_CHARGING) + point->color = egg_color_from_rgb (255, 0, 0); + else if (up_history_item_get_state (item) == UP_DEVICE_STATE_DISCHARGING) + point->color = egg_color_from_rgb (0, 0, 255); + else if (up_history_item_get_state (item) == UP_DEVICE_STATE_PENDING_CHARGE) + point->color = egg_color_from_rgb (200, 0, 0); + else if (up_history_item_get_state (item) == UP_DEVICE_STATE_PENDING_DISCHARGE) + point->color = egg_color_from_rgb (0, 0, 200); + else { + if (g_strcmp0 (history_type, GPM_HISTORY_RATE_VALUE) == 0) + point->color = egg_color_from_rgb (255, 255, 255); + else + point->color = egg_color_from_rgb (0, 255, 0); + } + g_ptr_array_add (new, point); + } + + /* render */ + sigma_smoothing = 2.0; + widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_smooth_history")); + checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_points_history")); + points = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + /* present data to graph */ + gpm_stats_set_graph_data (graph_history, new, checked, points); + + g_ptr_array_unref (array); + g_ptr_array_unref (new); +out: + return; +} + +/** + * gpm_stats_update_info_page_stats: + **/ +static void +gpm_stats_update_info_page_stats (UpDevice *device) +{ + GPtrArray *array; + guint i; + UpStatsItem *item; + GtkWidget *widget; + gboolean checked; + gboolean points; + GpmPointObj *point; + GPtrArray *new; + gboolean use_data = FALSE; + const gchar *type = NULL; + + new = g_ptr_array_new_with_free_func ((GDestroyNotify) gpm_point_obj_free); + if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0) { + type = "charging"; + use_data = TRUE; + } else if (g_strcmp0 (stats_type, GPM_STATS_DISCHARGE_DATA_VALUE) == 0) { + type = "discharging"; + use_data = TRUE; + } else if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_ACCURACY_VALUE) == 0) { + type = "charging"; + use_data = FALSE; + } else if (g_strcmp0 (stats_type, GPM_STATS_DISCHARGE_ACCURACY_VALUE) == 0) { + type = "discharging"; + use_data = FALSE; + } else { + g_assert_not_reached (); + } + + if (use_data) { + g_object_set (graph_statistics, + "type-x", GPM_GRAPH_WIDGET_TYPE_PERCENTAGE, + "type-y", GPM_GRAPH_WIDGET_TYPE_FACTOR, + "autorange-x", TRUE, + "autorange-y", TRUE, + NULL); + } else { + g_object_set (graph_statistics, + "type-x", GPM_GRAPH_WIDGET_TYPE_PERCENTAGE, + "type-y", GPM_GRAPH_WIDGET_TYPE_PERCENTAGE, + "autorange-x", TRUE, + "autorange-y", TRUE, + NULL); + } + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_stats_nodata")); + array = up_device_get_statistics_sync (device, type, NULL, NULL); + if (array == NULL) { + /* show no data label and hide graph */ + gtk_widget_hide (graph_statistics); + gtk_widget_show (widget); + goto out; + } + + /* hide no data and show graph */ + gtk_widget_hide (widget); + gtk_widget_show (graph_statistics); + + for (i=0; i<array->len; i++) { + item = (UpStatsItem *) g_ptr_array_index (array, i); + point = gpm_point_obj_new (); + point->x = i; + if (use_data) + point->y = up_stats_item_get_value (item); + else + point->y = up_stats_item_get_accuracy (item); + point->color = egg_color_from_rgb (255, 0, 0); + g_ptr_array_add (new, point); + } + + /* render */ + sigma_smoothing = 1.1; + widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_smooth_stats")); + checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_points_stats")); + points = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + /* present data to graph */ + gpm_stats_set_graph_data (graph_statistics, new, checked, points); + + g_ptr_array_unref (array); + g_ptr_array_unref (new); +out: + return; +} + +/** + * gpm_stats_update_info_data_page: + **/ +static void +gpm_stats_update_info_data_page (UpDevice *device, gint page) +{ + if (page == 0) + gpm_stats_update_info_page_details (device); + else if (page == 1) + gpm_stats_update_info_page_history (device); + else if (page == 2) + gpm_stats_update_info_page_stats (device); +} + +/** + * gpm_stats_update_info_data: + **/ +static void +gpm_stats_update_info_data (UpDevice *device) +{ + gint page; + GtkNotebook *notebook; + GtkWidget *page_widget; + gboolean has_history; + gboolean has_statistics; + + /* get properties */ + g_object_get (device, + "has-history", &has_history, + "has-statistics", &has_statistics, + NULL); + + + notebook = GTK_NOTEBOOK (gtk_builder_get_object (builder, "notebook1")); + + /* show info page */ + page_widget = gtk_notebook_get_nth_page (notebook, 0); + gtk_widget_show (page_widget); + + /* hide history if no support */ + page_widget = gtk_notebook_get_nth_page (notebook, 1); + if (has_history) + gtk_widget_show (page_widget); + else + gtk_widget_hide (page_widget); + + /* hide statistics if no support */ + page_widget = gtk_notebook_get_nth_page (notebook, 2); + if (has_statistics) + gtk_widget_show (page_widget); + else + gtk_widget_hide (page_widget); + + /* hide wakeups page */ + page_widget = gtk_notebook_get_nth_page (notebook, 3); + gtk_widget_hide (page_widget); + + page = gtk_notebook_get_current_page (notebook); + gpm_stats_update_info_data_page (device, page); + + return; +} + +/** + * gpm_stats_format_cmdline: + **/ +static gchar * +gpm_stats_format_cmdline (UpWakeupItem *item) +{ + gchar *found; + gchar *temp = NULL; + gchar *cmdline; + const gchar *temp_ptr; + + /* nothing */ + if (up_wakeup_item_get_cmdline (item) == NULL) { + /* TRANSLATORS: the command line was not provided */ + temp_ptr = _("No data"); + goto out; + } + + /* common kernel cmd names */ + if (g_strcmp0 (up_wakeup_item_get_cmdline (item), "insmod") == 0) { + /* TRANSLATORS: kernel module, usually a device driver */ + temp_ptr = _("Kernel module"); + goto out; + } + if (g_strcmp0 (up_wakeup_item_get_cmdline (item), "modprobe") == 0) { + /* TRANSLATORS: kernel module, usually a device driver */ + temp_ptr = _("Kernel module"); + goto out; + } + if (g_strcmp0 (up_wakeup_item_get_cmdline (item), "swapper") == 0) { + /* TRANSLATORS: kernel housekeeping */ + temp_ptr = _("Kernel core"); + goto out; + } + if (g_strcmp0 (up_wakeup_item_get_cmdline (item), "kernel-ipi") == 0) { + /* TRANSLATORS: interrupt between processors */ + temp_ptr = _("Interprocessor interrupt"); + goto out; + } + if (g_strcmp0 (up_wakeup_item_get_cmdline (item), "interrupt") == 0) { + /* TRANSLATORS: unknown interrupt */ + temp_ptr = _("Interrupt"); + goto out; + } + + /* truncate at first space or ':' */ + temp = g_strdup (up_wakeup_item_get_cmdline (item)); + found = strstr (temp, ":"); + if (found != NULL) + *found = '\0'; + found = strstr (temp, " "); + if (found != NULL) + *found = '\0'; + + /* remove path */ + found = g_strrstr (temp, "/"); + if (found != NULL && strncmp (temp, "event", 5) != 0) + temp_ptr = found + 1; + else + temp_ptr = temp; + +out: + /* format command line */ + if (up_wakeup_item_get_is_userspace (item)) + cmdline = g_markup_escape_text (temp_ptr, -1); + else + cmdline = g_markup_printf_escaped ("<i>%s</i>", temp_ptr); + g_free (temp); + + /* return */ + return cmdline; +} + +/** + * gpm_stats_format_details: + **/ +static gchar * +gpm_stats_format_details (UpWakeupItem *item) +{ + gchar *details; + const gchar *data; + + /* get this once to avoid a load of derefs */ + data = up_wakeup_item_get_details (item); + + /* replace common driver names */ + if (g_strcmp0 (data, "i8042") == 0) { + /* TRANSLATORS: the keyboard and mouse device event */ + details = g_strdup (_("PS/2 keyboard/mouse/touchpad")); + } else if (g_strcmp0 (data, "acpi") == 0) { + /* TRANSLATORS: ACPI, the Intel power standard on laptops and desktops */ + details = g_strdup (_("ACPI")); + } else if (g_strcmp0 (data, "ata_piix") == 0) { + /* TRANSLATORS: serial ATA is a new style of hard disk interface */ + details = g_strdup (_("Serial ATA")); + } else if (g_strcmp0 (data, "libata") == 0) { + /* TRANSLATORS: this is the old-style ATA interface */ + details = g_strdup (_("ATA host controller")); + } else if (g_strcmp0 (data, "iwl3945") == 0 || g_strcmp0 (data, "iwlagn") == 0) { + /* TRANSLATORS: 802.11 wireless adaptor */ + details = g_strdup (_("Intel wireless adaptor")); + + /* try to make the wakeup type nicer */ + } else if (g_str_has_prefix (data, "__mod_timer")) { + /* TRANSLATORS: a timer is something that fires periodically. + * The parameter is a process name, e.g. "firefox-bin". + * This is shown when the timer wakes up. */ + details = g_strdup_printf (_("Timer %s"), data+12); + } else if (g_str_has_prefix (data, "mod_timer")) { + /* TRANSLATORS: a timer is something that fires periodically. + * The parameter is a process name, e.g. "firefox-bin". + * This is shown when the timer wakes up. */ + details = g_strdup_printf (_("Timer %s"), data+10); + } else if (g_str_has_prefix (data, "hrtimer_start_expires")) { + /* TRANSLATORS: a timer is something that fires periodically. + * The parameter is a process name, e.g. "firefox-bin". + * This is shown when the timer wakes up. */ + details = g_strdup_printf (_("Timer %s"), data+22); + } else if (g_str_has_prefix (data, "hrtimer_start")) { + /* TRANSLATORS: a timer is something that fires periodically. + * The parameter is a process name, e.g. "firefox-bin". + * This is shown when the timer wakes up. */ + details = g_strdup_printf (_("Timer %s"), data+14); + } else if (g_str_has_prefix (data, "do_setitimer")) { + /* TRANSLATORS: a timer is something that fires periodically. + * The parameter is a process name, e.g. "firefox-bin". + * This is shown when the timer wakes up. */ + details = g_strdup_printf (_("Timer %s"), data+10); + } else if (g_str_has_prefix (data, "do_nanosleep")) { + /* TRANSLATORS: the parameter is the name of task that's woken up from sleeping. + * This is shown when the task wakes up. */ + details = g_strdup_printf (_("Sleep %s"), data+13); + } else if (g_str_has_prefix (data, "enqueue_task_rt")) { + /* TRANSLATORS: this is the name of a new realtime task. */ + details = g_strdup_printf (_("New task %s"), data+16); + } else if (g_str_has_prefix (data, "futex_wait")) { + /* TRANSLATORS: this is the name of a task that's woken to check state. + * This is shown when the task wakes up. */ + details = g_strdup_printf (_("Wait %s"), data+11); + } else if (g_str_has_prefix (data, "queue_delayed_work_on")) { + /* TRANSLATORS: this is the name of a work queue. + * A work queue is a list of work that has to be done. */ + details = g_strdup_printf (_("Work queue %s"), data+22); + } else if (g_str_has_prefix (data, "queue_delayed_work")) { + /* TRANSLATORS: this is the name of a work queue. + * A work queue is a list of work that has to be done. */ + details = g_strdup_printf (_("Work queue %s"), data+19); + } else if (g_str_has_prefix (data, "dst_run_gc")) { + /* TRANSLATORS: this is when the networking subsystem clears out old entries */ + details = g_strdup_printf (_("Network route flush %s"), data+11); + } else if (g_str_has_prefix (data, "usb_hcd_poll_rh_status")) { + /* TRANSLATORS: this is the name of an activity on the USB bus */ + details = g_strdup_printf (_("USB activity %s"), data+23); + } else if (g_str_has_prefix (data, "schedule_hrtimeout_range")) { + /* TRANSLATORS: we've timed out of an aligned timer, with the name */ + details = g_strdup_printf (_("Wakeup %s"), data+25); + } else if (g_str_has_prefix (data, "Local timer interrupts")) { + /* TRANSLATORS: interupts on the system required for basic operation */ + details = g_strdup (_("Local interrupts")); + } else if (g_str_has_prefix (data, "Rescheduling interrupts")) { + /* TRANSLATORS: interrupts when a task gets moved from one core to another */ + details = g_strdup (_("Rescheduling interrupts")); + } else + details = g_markup_escape_text (data, -1); + + return details; +} +/** + * gpm_stats_add_wakeups_item: + **/ +static void +gpm_stats_add_wakeups_item (UpWakeupItem *item) +{ + const gchar *icon; + gchar *value; + gchar *id; + gchar *details; + gchar *cmdline; + GtkTreeIter iter; + + if (up_wakeup_item_get_is_userspace (item)) { + icon = "application-x-executable"; + id = g_strdup_printf ("%i", up_wakeup_item_get_id (item)); + } else { + icon = "applications-system"; + if (up_wakeup_item_get_id (item) < 0xff0) + id = g_strdup_printf ("IRQ%i", up_wakeup_item_get_id (item)); + else + id = g_strdup ("IRQx"); + } + + /* formate value to one decimal place */ + value = g_strdup_printf ("%.1f", up_wakeup_item_get_value (item)); + + /* get formatted lines */ + cmdline = gpm_stats_format_cmdline (item); + details = gpm_stats_format_details (item); + + gtk_list_store_append (list_store_wakeups, &iter); + gtk_list_store_set (list_store_wakeups, &iter, + GPM_WAKEUPS_COLUMN_ID, id, + GPM_WAKEUPS_COLUMN_VALUE, value, + GPM_WAKEUPS_COLUMN_CMDLINE, cmdline, + GPM_WAKEUPS_COLUMN_DETAILS, details, + GPM_WAKEUPS_COLUMN_ICON, icon, -1); + g_free (cmdline); + g_free (details); + g_free (value); + g_free (id); +} + +/** + * gpm_stats_update_wakeups_data: + **/ +static void +gpm_stats_update_wakeups_data (void) +{ + GtkWidget *widget; + GtkWidget *page_widget; + guint total; + UpWakeupItem *item; + gchar *text; + guint i; + GError *error = NULL; + GPtrArray *array; + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "notebook1")); + + /* hide other pages */ + page_widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK(widget), 0); + gtk_widget_hide (page_widget); + page_widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK(widget), 1); + gtk_widget_hide (page_widget); + page_widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK(widget), 2); + gtk_widget_hide (page_widget); + + /* show wakeups page */ + page_widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK(widget), 3); + gtk_widget_show (page_widget); + + /* show total */ + total = up_wakeups_get_total_sync (wakeups, NULL, &error); + widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_total_wakeups")); + if (error == NULL) { + text = g_strdup_printf ("%i", total); + gtk_label_set_label (GTK_LABEL(widget), text); + g_free (text); + } else { + gtk_label_set_label (GTK_LABEL(widget), error->message); + g_error_free (error); + } + + /* get data */ + gtk_list_store_clear (list_store_wakeups); + array = up_wakeups_get_data_sync (wakeups, NULL, NULL); + if (array == NULL) + return; + for (i=0; i<array->len; i++) { + item = g_ptr_array_index (array, i); + gpm_stats_add_wakeups_item (item); + } + g_ptr_array_unref (array); +} + +static void +gpm_stats_set_title (GtkWindow *window, gint page_num) +{ + gchar *title; + const gchar * const page_titles[] = { + /* TRANSLATORS: shown on the titlebar */ + N_("Device Information"), + /* TRANSLATORS: shown on the titlebar */ + N_("Device History"), + /* TRANSLATORS: shown on the titlebar */ + N_("Device Profile"), + /* TRANSLATORS: shown on the titlebar */ + N_("Processor Wakeups") + }; + + /* TRANSLATORS: shown on the titlebar */ + title = g_strdup_printf ("%s - %s", _("Power Statistics"), _(page_titles[page_num])); + gtk_window_set_title (window, title); + g_free (title); +} + +/** + * gpm_stats_notebook_changed_cb: + **/ +static void +gpm_stats_notebook_changed_cb (GtkNotebook *notebook, gpointer page, gint page_num, gpointer user_data) +{ + UpDevice *device; + GtkWidget *widget; + + /* set the window title depending on the mode */ + widget = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_stats")); + gpm_stats_set_title (GTK_WINDOW (widget), page_num); + + /* save page in mateconf */ + mateconf_client_set_int (mateconf_client, GPM_CONF_INFO_PAGE_NUMBER, page_num, NULL); + + if (current_device == NULL) + return; + + if (g_strcmp0 (current_device, "wakeups") == 0) + return; + + device = up_device_new (); + up_device_set_object_path_sync (device, current_device, NULL, NULL); + gpm_stats_update_info_data_page (device, page_num); + gpm_stats_update_info_data (device); + g_object_unref (device); +} + +/** + * gpm_stats_button_update_ui: + **/ +static void +gpm_stats_button_update_ui (void) +{ + UpDevice *device; + device = up_device_new (); + up_device_set_object_path_sync (device, current_device, NULL, NULL); + gpm_stats_update_info_data (device); + g_object_unref (device); +} + +/** + * gpm_stats_devices_treeview_clicked_cb: + **/ +static void +gpm_stats_devices_treeview_clicked_cb (GtkTreeSelection *selection, gboolean data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + UpDevice *device; + + /* This will only work in single or browse selection mode! */ + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + g_free (current_device); + gtk_tree_model_get (model, &iter, GPM_DEVICES_COLUMN_ID, ¤t_device, -1); + + /* save device in mateconf */ + mateconf_client_set_string (mateconf_client, GPM_CONF_INFO_LAST_DEVICE, current_device, NULL); + + /* show transaction_id */ + egg_debug ("selected row is: %s", current_device); + + /* is special device */ + if (g_strcmp0 (current_device, "wakeups") == 0) { + gpm_stats_update_wakeups_data (); + } else { + device = up_device_new (); + up_device_set_object_path_sync (device, current_device, NULL, NULL); + gpm_stats_update_info_data (device); + g_object_unref (device); + } + + } else { + egg_debug ("no row selected"); + } +} + +/** + * gpm_stats_window_activated_cb + **/ +static void +gpm_stats_window_activated_cb (EggUnique *egg_unique, gpointer data) +{ + GtkWidget *widget; + widget = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_stats")); + gtk_window_present (GTK_WINDOW (widget)); +} + +/** + * gpm_stats_add_device: + **/ +static void +gpm_stats_add_device (UpDevice *device) +{ + const gchar *id; + GtkTreeIter iter; + const gchar *text; + const gchar *icon; + UpDeviceKind kind; + + /* get device properties */ + g_object_get (device, + "kind", &kind, + NULL); + + id = up_device_get_object_path (device); + text = gpm_device_kind_to_localised_text (kind, 1); + icon = gpm_upower_get_device_icon (device); + + gtk_list_store_append (list_store_devices, &iter); + gtk_list_store_set (list_store_devices, &iter, + GPM_DEVICES_COLUMN_ID, id, + GPM_DEVICES_COLUMN_TEXT, text, + GPM_DEVICES_COLUMN_ICON, icon, -1); +} + +/** + * gpm_stats_data_changed_cb: + **/ +static void +gpm_stats_data_changed_cb (UpClient *client, gpointer user_data) +{ + if (g_strcmp0 (current_device, "wakeups") == 0) + gpm_stats_update_wakeups_data (); +} + +/** + * gpm_stats_device_added_cb: + **/ +static void +gpm_stats_device_added_cb (UpClient *client, UpDevice *device, gpointer user_data) +{ + const gchar *object_path; + object_path = up_device_get_object_path (device); + egg_debug ("added: %s", object_path); + gpm_stats_add_device (device); +} + +/** + * gpm_stats_device_changed_cb: + **/ +static void +gpm_stats_device_changed_cb (UpClient *client, UpDevice *device, gpointer user_data) +{ + const gchar *object_path; + object_path = up_device_get_object_path (device); + if (object_path == NULL || current_device == NULL) + return; + egg_debug ("changed: %s", object_path); + if (g_strcmp0 (current_device, object_path) == 0) + gpm_stats_update_info_data (device); +} + +/** + * gpm_stats_device_removed_cb: + **/ +static void +gpm_stats_device_removed_cb (UpClient *client, UpDevice *device, gpointer user_data) +{ + const gchar *object_path; + GtkTreeIter iter; + gchar *id = NULL; + gboolean ret; + + object_path = up_device_get_object_path (device); + egg_debug ("removed: %s", object_path); + if (g_strcmp0 (current_device, object_path) == 0) { + gtk_list_store_clear (list_store_info); + } + + /* search the list and remove the object path entry */ + ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store_devices), &iter); + while (ret) { + gtk_tree_model_get (GTK_TREE_MODEL (list_store_devices), &iter, GPM_DEVICES_COLUMN_ID, &id, -1); + if (g_strcmp0 (id, object_path) == 0) { + gtk_list_store_remove (list_store_devices, &iter); + break; + } + g_free (id); + ret = gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store_devices), &iter); + }; +} + +/** + * gpm_stats_history_type_combo_changed_cb: + **/ +static void +gpm_stats_history_type_combo_changed_cb (GtkWidget *widget, gpointer data) +{ + gchar *value; + const gchar *axis_x = NULL; + const gchar *axis_y = NULL; + value = gtk_combo_box_get_active_text (GTK_COMBO_BOX (widget)); + if (g_strcmp0 (value, GPM_HISTORY_RATE_TEXT) == 0) { + history_type = GPM_HISTORY_RATE_VALUE; + /* TRANSLATORS: this is the X axis on the graph */ + axis_x = _("Time elapsed"); + /* TRANSLATORS: this is the Y axis on the graph */ + axis_y = _("Power"); + } else if (g_strcmp0 (value, GPM_HISTORY_CHARGE_TEXT) == 0) { + history_type = GPM_HISTORY_CHARGE_VALUE; + /* TRANSLATORS: this is the X axis on the graph */ + axis_x = _("Time elapsed"); + /* TRANSLATORS: this is the Y axis on the graph for the whole battery device */ + axis_y = _("Cell charge"); + } else if (g_strcmp0 (value, GPM_HISTORY_TIME_FULL_TEXT) == 0) { + history_type = GPM_HISTORY_TIME_FULL_VALUE; + /* TRANSLATORS: this is the X axis on the graph */ + axis_x = _("Time elapsed"); + /* TRANSLATORS: this is the Y axis on the graph */ + axis_y = _("Predicted time"); + } else if (g_strcmp0 (value, GPM_HISTORY_TIME_EMPTY_TEXT) == 0) { + history_type = GPM_HISTORY_TIME_EMPTY_VALUE; + /* TRANSLATORS: this is the X axis on the graph */ + axis_x = _("Time elapsed"); + /* TRANSLATORS: this is the Y axis on the graph */ + axis_y = _("Predicted time"); + } else { + g_assert (FALSE); + } + + /* set axis */ + widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_axis_history_x")); + gtk_label_set_label (GTK_LABEL(widget), axis_x); + widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_axis_history_y")); + gtk_label_set_label (GTK_LABEL(widget), axis_y); + + gpm_stats_button_update_ui (); + g_free (value); + + /* save to mateconf */ + mateconf_client_set_string (mateconf_client, GPM_CONF_INFO_HISTORY_TYPE, history_type, NULL); +} + +/** + * gpm_stats_type_combo_changed_cb: + **/ +static void +gpm_stats_type_combo_changed_cb (GtkWidget *widget, gpointer data) +{ + gchar *value; + const gchar *axis_x = NULL; + const gchar *axis_y = NULL; + value = gtk_combo_box_get_active_text (GTK_COMBO_BOX (widget)); + if (g_strcmp0 (value, GPM_STATS_CHARGE_DATA_TEXT) == 0) { + stats_type = GPM_STATS_CHARGE_DATA_VALUE; + /* TRANSLATORS: this is the X axis on the graph for the whole battery device */ + axis_x = _("Cell charge"); + /* TRANSLATORS: this is the Y axis on the graph */ + axis_y = _("Correction factor"); + } else if (g_strcmp0 (value, GPM_STATS_CHARGE_ACCURACY_TEXT) == 0) { + stats_type = GPM_STATS_CHARGE_ACCURACY_VALUE; + /* TRANSLATORS: this is the X axis on the graph for the whole battery device */ + axis_x = _("Cell charge"); + /* TRANSLATORS: this is the Y axis on the graph */ + axis_y = _("Prediction accuracy"); + } else if (g_strcmp0 (value, GPM_STATS_DISCHARGE_DATA_TEXT) == 0) { + stats_type = GPM_STATS_DISCHARGE_DATA_VALUE; + /* TRANSLATORS: this is the X axis on the graph for the whole battery device */ + axis_x = _("Cell charge"); + /* TRANSLATORS: this is the Y axis on the graph */ + axis_y = _("Correction factor"); + } else if (g_strcmp0 (value, GPM_STATS_DISCHARGE_ACCURACY_TEXT) == 0) { + stats_type = GPM_STATS_DISCHARGE_ACCURACY_VALUE; + /* TRANSLATORS: this is the X axis on the graph for the whole battery device */ + axis_x = _("Cell charge"); + /* TRANSLATORS: this is the Y axis on the graph */ + axis_y = _("Prediction accuracy"); + } else { + g_assert (FALSE); + } + + /* set axis */ + widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_axis_stats_x")); + gtk_label_set_label (GTK_LABEL(widget), axis_x); + widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_axis_stats_y")); + gtk_label_set_label (GTK_LABEL(widget), axis_y); + + gpm_stats_button_update_ui (); + g_free (value); + + /* save to mateconf */ + mateconf_client_set_string (mateconf_client, GPM_CONF_INFO_STATS_TYPE, stats_type, NULL); +} + +/** + * gpm_stats_range_combo_changed: + **/ +static void +gpm_stats_range_combo_changed (GtkWidget *widget, gpointer data) +{ + gchar *value; + value = gtk_combo_box_get_active_text (GTK_COMBO_BOX (widget)); + if (g_strcmp0 (value, GPM_HISTORY_MINUTE_TEXT) == 0) + history_time = GPM_HISTORY_MINUTE_VALUE; + else if (g_strcmp0 (value, GPM_HISTORY_HOUR_TEXT) == 0) + history_time = GPM_HISTORY_HOUR_VALUE; + else if (g_strcmp0 (value, GPM_HISTORY_HOURS_TEXT) == 0) + history_time = GPM_HISTORY_HOURS_VALUE; + else if (g_strcmp0 (value, GPM_HISTORY_DAY_TEXT) == 0) + history_time = GPM_HISTORY_DAY_VALUE; + else if (g_strcmp0 (value, GPM_HISTORY_WEEK_TEXT) == 0) + history_time = GPM_HISTORY_WEEK_VALUE; + else + g_assert (FALSE); + + /* save to mateconf */ + mateconf_client_set_int (mateconf_client, GPM_CONF_INFO_HISTORY_TIME, history_time, NULL); + + gpm_stats_button_update_ui (); + g_free (value); +} + +/** + * gpm_stats_smooth_checkbox_history_cb: + * @widget: The GtkWidget object + **/ +static void +gpm_stats_smooth_checkbox_history_cb (GtkWidget *widget, gpointer data) +{ + gboolean checked; + checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + mateconf_client_set_bool (mateconf_client, GPM_CONF_INFO_HISTORY_GRAPH_SMOOTH, checked, NULL); + gpm_stats_button_update_ui (); +} + +/** + * gpm_stats_smooth_checkbox_stats_cb: + * @widget: The GtkWidget object + **/ +static void +gpm_stats_smooth_checkbox_stats_cb (GtkWidget *widget, gpointer data) +{ + gboolean checked; + checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + mateconf_client_set_bool (mateconf_client, GPM_CONF_INFO_STATS_GRAPH_SMOOTH, checked, NULL); + gpm_stats_button_update_ui (); +} + +/** + * gpm_stats_points_checkbox_history_cb: + * @widget: The GtkWidget object + **/ +static void +gpm_stats_points_checkbox_history_cb (GtkWidget *widget, gpointer data) +{ + gboolean checked; + checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + mateconf_client_set_bool (mateconf_client, GPM_CONF_INFO_HISTORY_GRAPH_POINTS, checked, NULL); + gpm_stats_button_update_ui (); +} + +/** + * gpm_stats_points_checkbox_stats_cb: + * @widget: The GtkWidget object + **/ +static void +gpm_stats_points_checkbox_stats_cb (GtkWidget *widget, gpointer data) +{ + gboolean checked; + checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + mateconf_client_set_bool (mateconf_client, GPM_CONF_INFO_STATS_GRAPH_POINTS, checked, NULL); + gpm_stats_button_update_ui (); +} + +/** + * gpm_stats_set_combo_simple_text: + **/ +static void +gpm_stats_set_combo_simple_text (GtkWidget *combo_box) +{ + GtkCellRenderer *cell; + GtkListStore *store; + + store = gtk_list_store_new (1, G_TYPE_STRING); + gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store)); + g_object_unref (store); + + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell, + "text", 0, + NULL); +} + +/** + * gpm_stats_highlight_device: + **/ +static void +gpm_stats_highlight_device (const gchar *object_path) +{ + gboolean ret; + gchar *id = NULL; + gchar *path_str; + guint i; + GtkTreeIter iter; + GtkTreePath *path; + GtkWidget *widget; + + /* check valid */ + if (!g_str_has_prefix (object_path, "/")) + return; + + /* we have to reuse the treeview data as it may be sorted */ + ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store_devices), &iter); + for (i=0; ret; i++) { + gtk_tree_model_get (GTK_TREE_MODEL (list_store_devices), &iter, + GPM_DEVICES_COLUMN_ID, &id, + -1); + if (g_strcmp0 (id, object_path) == 0) { + path_str = g_strdup_printf ("%i", i); + path = gtk_tree_path_new_from_string (path_str); + widget = GTK_WIDGET (gtk_builder_get_object (builder, "treeview_devices")); + gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (widget), path, NULL, NULL, FALSE); + g_free (path_str); + gtk_tree_path_free (path); + } + g_free (id); + ret = gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store_devices), &iter); + } +} + +/** + * main: + **/ +int +main (int argc, char *argv[]) +{ + gboolean verbose = FALSE; + GOptionContext *context; + GtkBox *box; + GtkWidget *widget; + GtkTreeSelection *selection; + EggUnique *egg_unique; + gboolean ret; + UpClient *client; + GPtrArray *devices; + UpDevice *device; + UpDeviceKind kind; + guint i, j; + gint page; + gboolean checked; + gchar *last_device = NULL; + guint retval; + GError *error = NULL; + + const GOptionEntry options[] = { + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + /* TRANSLATORS: show verbose debugging */ + N_("Show extra debugging information"), NULL }, + { "device", '\0', 0, G_OPTION_ARG_STRING, &last_device, + /* TRANSLATORS: show a device by default */ + N_("Select this device at startup"), NULL }, + { NULL} + }; + + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + if (! g_thread_supported ()) + g_thread_init (NULL); + dbus_g_thread_init (); + g_type_init (); + + context = g_option_context_new (NULL); + /* TRANSLATORS: the program name */ + g_option_context_set_summary (context, _("Power Statistics")); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); + + egg_debug_init (verbose); + gtk_init (&argc, &argv); + + /* are we already activated? */ + egg_unique = egg_unique_new (); + ret = egg_unique_assign (egg_unique, "org.mate.PowerManager.Statistics"); + if (!ret) + goto unique_out; + g_signal_connect (egg_unique, "activated", + G_CALLBACK (gpm_stats_window_activated_cb), NULL); + + /* add application specific icons to search path */ + gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), + GPM_DATA G_DIR_SEPARATOR_S "icons"); + + /* get data from mateconf */ + mateconf_client = mateconf_client_get_default (); + + /* get UI */ + builder = gtk_builder_new (); + retval = gtk_builder_add_from_file (builder, GPM_DATA "/gpm-statistics.ui", &error); + if (retval == 0) { + egg_warning ("failed to load ui: %s", error->message); + g_error_free (error); + } + + /* add history graph */ + box = GTK_BOX (gtk_builder_get_object (builder, "hbox_history")); + graph_history = gpm_graph_widget_new (); + gtk_box_pack_start (box, graph_history, TRUE, TRUE, 0); + gtk_widget_set_size_request (graph_history, 400, 250); + gtk_widget_show (graph_history); + + /* add statistics graph */ + box = GTK_BOX (gtk_builder_get_object (builder, "hbox_statistics")); + graph_statistics = gpm_graph_widget_new (); + gtk_box_pack_start (box, graph_statistics, TRUE, TRUE, 0); + gtk_widget_set_size_request (graph_statistics, 400, 250); + gtk_widget_show (graph_statistics); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_stats")); + gtk_window_set_default_size (GTK_WINDOW(widget), 800, 500); + gtk_window_set_default_icon_name (GPM_STOCK_APP_ICON); + + /* Get the main window quit */ + g_signal_connect_swapped (widget, "delete_event", G_CALLBACK (gtk_main_quit), NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "button_close")); + g_signal_connect_swapped (widget, "clicked", G_CALLBACK (gtk_main_quit), NULL); + gtk_widget_grab_default (widget); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "button_help")); + g_signal_connect (widget, "clicked", + G_CALLBACK (gpm_stats_button_help_cb), NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_smooth_history")); + checked = mateconf_client_get_bool (mateconf_client, GPM_CONF_INFO_HISTORY_GRAPH_SMOOTH, NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), checked); + g_signal_connect (widget, "clicked", + G_CALLBACK (gpm_stats_smooth_checkbox_history_cb), NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_smooth_stats")); + checked = mateconf_client_get_bool (mateconf_client, GPM_CONF_INFO_STATS_GRAPH_SMOOTH, NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), checked); + g_signal_connect (widget, "clicked", + G_CALLBACK (gpm_stats_smooth_checkbox_stats_cb), NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_points_history")); + checked = mateconf_client_get_bool (mateconf_client, GPM_CONF_INFO_HISTORY_GRAPH_POINTS, NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), checked); + g_signal_connect (widget, "clicked", + G_CALLBACK (gpm_stats_points_checkbox_history_cb), NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_points_stats")); + checked = mateconf_client_get_bool (mateconf_client, GPM_CONF_INFO_STATS_GRAPH_POINTS, NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), checked); + g_signal_connect (widget, "clicked", + G_CALLBACK (gpm_stats_points_checkbox_stats_cb), NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "notebook1")); + page = mateconf_client_get_int (mateconf_client, GPM_CONF_INFO_PAGE_NUMBER, NULL); + gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), page); + g_signal_connect (widget, "switch-page", + G_CALLBACK (gpm_stats_notebook_changed_cb), NULL); + + /* create list stores */ + list_store_info = gtk_list_store_new (GPM_INFO_COLUMN_LAST, G_TYPE_STRING, G_TYPE_STRING); + list_store_devices = gtk_list_store_new (GPM_DEVICES_COLUMN_LAST, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING); + list_store_wakeups = gtk_list_store_new (GPM_WAKEUPS_COLUMN_LAST, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + /* create transaction_id tree view */ + widget = GTK_WIDGET (gtk_builder_get_object (builder, "treeview_info")); + gtk_tree_view_set_model (GTK_TREE_VIEW (widget), + GTK_TREE_MODEL (list_store_info)); + + /* add columns to the tree view */ + gpm_stats_add_info_columns (GTK_TREE_VIEW (widget)); + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (widget)); /* show */ + + /* create transaction_id tree view */ + widget = GTK_WIDGET (gtk_builder_get_object (builder, "treeview_devices")); + gtk_tree_view_set_model (GTK_TREE_VIEW (widget), + GTK_TREE_MODEL (list_store_devices)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); + g_signal_connect (selection, "changed", + G_CALLBACK (gpm_stats_devices_treeview_clicked_cb), NULL); + + /* add columns to the tree view */ + gpm_stats_add_devices_columns (GTK_TREE_VIEW (widget)); + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (widget)); /* show */ + + /* create wakeups tree view */ + widget = GTK_WIDGET (gtk_builder_get_object (builder, "treeview_wakeups")); + gtk_tree_view_set_model (GTK_TREE_VIEW (widget), + GTK_TREE_MODEL (list_store_wakeups)); + + /* add columns to the tree view */ + gpm_stats_add_wakeups_columns (GTK_TREE_VIEW (widget)); + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (widget)); /* show */ + + history_type = mateconf_client_get_string (mateconf_client, GPM_CONF_INFO_HISTORY_TYPE, NULL); + history_time = mateconf_client_get_int (mateconf_client, GPM_CONF_INFO_HISTORY_TIME, NULL); + if (history_type == NULL) + history_type = GPM_HISTORY_CHARGE_VALUE; + if (history_time == 0) + history_time = GPM_HISTORY_HOUR_VALUE; + + stats_type = mateconf_client_get_string (mateconf_client, GPM_CONF_INFO_STATS_TYPE, NULL); + if (stats_type == NULL) + stats_type = GPM_STATS_CHARGE_DATA_VALUE; + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_history_type")); + gpm_stats_set_combo_simple_text (widget); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_HISTORY_RATE_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_HISTORY_CHARGE_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_HISTORY_TIME_FULL_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_HISTORY_TIME_EMPTY_TEXT); + if (g_strcmp0 (history_type, GPM_HISTORY_RATE_VALUE) == 0) + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); + else + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1); + g_signal_connect (G_OBJECT (widget), "changed", + G_CALLBACK (gpm_stats_history_type_combo_changed_cb), NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_stats_type")); + gpm_stats_set_combo_simple_text (widget); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_STATS_CHARGE_DATA_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_STATS_CHARGE_ACCURACY_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_STATS_DISCHARGE_DATA_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_STATS_DISCHARGE_ACCURACY_TEXT); + if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0) + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); + else if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0) + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1); + else if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0) + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 2); + else if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0) + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); + else + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 3); + g_signal_connect (G_OBJECT (widget), "changed", + G_CALLBACK (gpm_stats_type_combo_changed_cb), NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_history_time")); + gpm_stats_set_combo_simple_text (widget); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_HISTORY_MINUTE_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_HISTORY_HOUR_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_HISTORY_HOURS_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_HISTORY_DAY_TEXT); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), GPM_HISTORY_WEEK_TEXT); + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1); + if (history_time == GPM_HISTORY_MINUTE_VALUE) + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); + else if (history_time == GPM_HISTORY_HOUR_VALUE) + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1); + else + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 2); + g_signal_connect (G_OBJECT (widget), "changed", + G_CALLBACK (gpm_stats_range_combo_changed), NULL); + + client = up_client_new (); + + wakeups = up_wakeups_new (); + g_signal_connect (wakeups, "data-changed", G_CALLBACK (gpm_stats_data_changed_cb), NULL); + + /* coldplug */ + ret = up_client_enumerate_devices_sync (client, NULL, NULL); + if (!ret) + goto out; + devices = up_client_get_devices (client); + + /* add devices in visually pleasing order */ + for (j=0; j<UP_DEVICE_KIND_LAST; j++) { + for (i=0; i < devices->len; i++) { + device = g_ptr_array_index (devices, i); + g_object_get (device, "kind", &kind, NULL); + if (kind == j) + gpm_stats_add_device (device); + } + } + + /* connect now the coldplug is done */ + g_signal_connect (client, "device-added", G_CALLBACK (gpm_stats_device_added_cb), NULL); + g_signal_connect (client, "device-removed", G_CALLBACK (gpm_stats_device_removed_cb), NULL); + g_signal_connect (client, "device-changed", G_CALLBACK (gpm_stats_device_changed_cb), NULL); + + /* set current device */ + if (devices->len > 0) { + device = g_ptr_array_index (devices, 0); + gpm_stats_update_info_data (device); + current_device = g_strdup (up_device_get_object_path (device)); + } + + if (last_device == NULL) + last_device = mateconf_client_get_string (mateconf_client, GPM_CONF_INFO_LAST_DEVICE, NULL); + + /* has capability to measure wakeups */ + ret = up_wakeups_get_has_capability (wakeups); + if (ret) { + GtkTreeIter iter; + gtk_list_store_append (list_store_devices, &iter); + gtk_list_store_set (list_store_devices, &iter, + GPM_DEVICES_COLUMN_ID, "wakeups", + /* TRANSLATORS: the icon for the CPU */ + GPM_DEVICES_COLUMN_TEXT, _("Processor"), + GPM_DEVICES_COLUMN_ICON, "computer", -1); + } + + /* set the correct focus on the last device */ + if (last_device != NULL) + gpm_stats_highlight_device (last_device); + + g_ptr_array_unref (devices); + + /* set axis */ + widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_history_type")); + gpm_stats_history_type_combo_changed_cb (widget, NULL); + widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_stats_type")); + gpm_stats_type_combo_changed_cb (widget, NULL); + + widget = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_stats")); + gtk_widget_show (widget); + + gtk_main (); + +out: + g_object_unref (mateconf_client); + g_object_unref (client); + g_object_unref (wakeups); + g_object_unref (builder); + g_object_unref (list_store_info); +unique_out: + g_object_unref (egg_unique); + g_free (last_device); + return 0; +} |