1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
4  * Copyright (C) 2012-2021 MATE Developers
5  *
6  * Licensed under the GNU General Public License Version 2
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #include "config.h"
24 
25 #include <locale.h>
26 
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 
30 #include <gtk/gtk.h>
31 #include <dbus/dbus-glib.h>
32 #include <libupower-glib/upower.h>
33 
34 #include "egg-color.h"
35 #include "egg-array-float.h"
36 
37 #include "gpm-common.h"
38 #include "gpm-icon-names.h"
39 #include "gpm-upower.h"
40 #include "gpm-graph-widget.h"
41 
42 static GtkBuilder *builder = NULL;
43 static GtkListStore *list_store_info = NULL;
44 static GtkListStore *list_store_devices = NULL;
45 gchar *current_device = NULL;
46 static const gchar *history_type;
47 static const gchar *stats_type;
48 static guint history_time;
49 static GSettings *settings;
50 static gfloat sigma_smoothing = 0.0f;
51 static GtkWidget *graph_history = NULL;
52 static GtkWidget *graph_statistics = NULL;
53 
54 enum {
55 	GPM_INFO_COLUMN_TEXT,
56 	GPM_INFO_COLUMN_VALUE,
57 	GPM_INFO_COLUMN_LAST
58 };
59 
60 enum {
61 	GPM_DEVICES_COLUMN_ICON,
62 	GPM_DEVICES_COLUMN_TEXT,
63 	GPM_DEVICES_COLUMN_ID,
64 	GPM_DEVICES_COLUMN_LAST
65 };
66 
67 #define GPM_HISTORY_RATE_TEXT			_("Rate")
68 #define GPM_HISTORY_CHARGE_TEXT			_("Charge")
69 #define GPM_HISTORY_TIME_FULL_TEXT		_("Time to full")
70 #define GPM_HISTORY_TIME_EMPTY_TEXT		_("Time to empty")
71 
72 #define GPM_HISTORY_RATE_VALUE			"rate"
73 #define GPM_HISTORY_CHARGE_VALUE		"charge"
74 #define GPM_HISTORY_TIME_FULL_VALUE		"time-full"
75 #define GPM_HISTORY_TIME_EMPTY_VALUE		"time-empty"
76 
77 #define GPM_HISTORY_MINUTE_TEXT			_("10 minutes")
78 #define GPM_HISTORY_HOUR_TEXT			_("2 hours")
79 #define GPM_HISTORY_HOURS_TEXT			_("6 hours")
80 #define GPM_HISTORY_DAY_TEXT			_("1 day")
81 #define GPM_HISTORY_WEEK_TEXT			_("1 week")
82 
83 #define GPM_HISTORY_MINUTE_VALUE		10*60
84 #define GPM_HISTORY_HOUR_VALUE			2*60*60
85 #define GPM_HISTORY_HOURS_VALUE			6*60*60
86 #define GPM_HISTORY_DAY_VALUE			24*60*60
87 #define GPM_HISTORY_WEEK_VALUE			7*24*60*60
88 
89 /* TRANSLATORS: what we've observed about the device */
90 #define GPM_STATS_CHARGE_DATA_TEXT		_("Charge profile")
91 #define GPM_STATS_DISCHARGE_DATA_TEXT		_("Discharge profile")
92 /* TRANSLATORS: how accurately we can predict the time remaining of the battery */
93 #define GPM_STATS_CHARGE_ACCURACY_TEXT		_("Charge accuracy")
94 #define GPM_STATS_DISCHARGE_ACCURACY_TEXT	_("Discharge accuracy")
95 
96 #define GPM_STATS_CHARGE_DATA_VALUE		"charge-data"
97 #define GPM_STATS_CHARGE_ACCURACY_VALUE		"charge-accuracy"
98 #define GPM_STATS_DISCHARGE_DATA_VALUE		"discharge-data"
99 #define GPM_STATS_DISCHARGE_ACCURACY_VALUE	"discharge-accuracy"
100 
101 /**
102  * gpm_stats_button_help_cb:
103  **/
104 static void
gpm_stats_button_help_cb(GtkWidget * widget,gboolean data)105 gpm_stats_button_help_cb (GtkWidget *widget, gboolean data)
106 {
107 	gpm_help_display ("statistics");
108 }
109 
110 /**
111  * gpm_stats_add_info_columns:
112  **/
113 static void
gpm_stats_add_info_columns(GtkTreeView * treeview)114 gpm_stats_add_info_columns (GtkTreeView *treeview)
115 {
116 	GtkCellRenderer *renderer;
117 	GtkTreeViewColumn *column;
118 
119 	/* image */
120 	renderer = gtk_cell_renderer_text_new ();
121 	column = gtk_tree_view_column_new_with_attributes (_("Attribute"), renderer,
122 							   "markup", GPM_INFO_COLUMN_TEXT, NULL);
123 	gtk_tree_view_column_set_sort_column_id (column, GPM_INFO_COLUMN_TEXT);
124 	gtk_tree_view_append_column (treeview, column);
125 
126 	/* column for text */
127 	renderer = gtk_cell_renderer_text_new ();
128 	column = gtk_tree_view_column_new_with_attributes (_("Value"), renderer,
129 							   "markup", GPM_INFO_COLUMN_VALUE, NULL);
130 	gtk_tree_view_append_column (treeview, column);
131 }
132 
133 /**
134  * gpm_stats_add_devices_columns:
135  **/
136 static void
gpm_stats_add_devices_columns(GtkTreeView * treeview)137 gpm_stats_add_devices_columns (GtkTreeView *treeview)
138 {
139 	GtkCellRenderer *renderer;
140 	GtkTreeViewColumn *column;
141 
142 	/* image */
143 	renderer = gtk_cell_renderer_pixbuf_new ();
144 	g_object_set (renderer, "stock-size", GTK_ICON_SIZE_DIALOG, NULL);
145 	column = gtk_tree_view_column_new_with_attributes (_("Image"), renderer,
146 							   "icon-name", GPM_DEVICES_COLUMN_ICON, NULL);
147 	gtk_tree_view_append_column (treeview, column);
148 
149 	/* column for text */
150 	renderer = gtk_cell_renderer_text_new ();
151 	column = gtk_tree_view_column_new_with_attributes (_("Description"), renderer,
152 							   "markup", GPM_DEVICES_COLUMN_TEXT, NULL);
153 	gtk_tree_view_column_set_sort_column_id (column, GPM_INFO_COLUMN_TEXT);
154 	gtk_tree_view_append_column (treeview, column);
155 	gtk_tree_view_column_set_expand (column, TRUE);
156 }
157 
158 /**
159  * gpm_stats_add_info_data:
160  **/
161 static void
gpm_stats_add_info_data(const gchar * attr,const gchar * text)162 gpm_stats_add_info_data (const gchar *attr, const gchar *text)
163 {
164 	GtkTreeIter iter;
165 	gtk_list_store_append (list_store_info, &iter);
166 	gtk_list_store_set (list_store_info, &iter,
167 			    GPM_INFO_COLUMN_TEXT, attr,
168 			    GPM_INFO_COLUMN_VALUE, text, -1);
169 }
170 
171 /**
172  * gpm_stats_update_smooth_data:
173  **/
174 static GPtrArray *
gpm_stats_update_smooth_data(GPtrArray * list)175 gpm_stats_update_smooth_data (GPtrArray *list)
176 {
177 	guint i;
178 	GpmPointObj *point;
179 	GpmPointObj *point_new;
180 	GPtrArray *new;
181 	EggArrayFloat *raw;
182 	EggArrayFloat *convolved;
183 	EggArrayFloat *outliers;
184 	EggArrayFloat *gaussian = NULL;
185 
186 	/* convert the y data to a EggArrayFloat array */
187 	raw = egg_array_float_new (list->len);
188 	for (i=0; i<list->len; i++) {
189 		point = (GpmPointObj *) g_ptr_array_index (list, i);
190 		egg_array_float_set (raw, i, point->y);
191 	}
192 
193 	/* remove any outliers */
194 	outliers = egg_array_float_remove_outliers (raw, 3, 0.1);
195 
196 	/* convolve with gaussian */
197 	gaussian = egg_array_float_compute_gaussian (15, sigma_smoothing);
198 	convolved = egg_array_float_convolve (outliers, gaussian);
199 
200 	/* add the smoothed data back into a new array */
201 	new = g_ptr_array_new_with_free_func ((GDestroyNotify) gpm_point_obj_free);
202 	for (i=0; i<list->len; i++) {
203 		point = (GpmPointObj *) g_ptr_array_index (list, i);
204 		point_new = g_new0 (GpmPointObj, 1);
205 		point_new->color = point->color;
206 		point_new->x = point->x;
207 		point_new->y = egg_array_float_get (convolved, i);
208 		g_ptr_array_add (new, point_new);
209 	}
210 
211 	/* free data */
212 	egg_array_float_free (gaussian);
213 	egg_array_float_free (raw);
214 	egg_array_float_free (convolved);
215 	egg_array_float_free (outliers);
216 
217 	return new;
218 }
219 
220 /**
221  * gpm_stats_time_to_string:
222  **/
223 static gchar *
gpm_stats_time_to_string(gint seconds)224 gpm_stats_time_to_string (gint seconds)
225 {
226 	gfloat value = seconds;
227 
228 	if (value < 0) {
229 		/* TRANSLATORS: this is when the stats time is not known */
230 		return g_strdup (_("Unknown"));
231 	}
232 	if (value < 60) {
233 		/* TRANSLATORS: this is a time value, usually to show on a graph */
234 		return g_strdup_printf (ngettext ("%.0f second", "%.0f seconds", value), value);
235 	}
236 	value /= 60.0;
237 	if (value < 60) {
238 		/* TRANSLATORS: this is a time value, usually to show on a graph */
239 		return g_strdup_printf (ngettext ("%.1f minute", "%.1f minutes", value), value);
240 	}
241 	value /= 60.0;
242 	if (value < 60) {
243 		/* TRANSLATORS: this is a time value, usually to show on a graph */
244 		return g_strdup_printf (ngettext ("%.1f hour", "%.1f hours", value), value);
245 	}
246 	value /= 24.0;
247 	/* TRANSLATORS: this is a time value, usually to show on a graph */
248 	return g_strdup_printf (ngettext ("%.1f day", "%.1f days", value), value);
249 }
250 
251 /**
252  * gpm_stats_bool_to_string:
253  **/
254 static const gchar *
gpm_stats_bool_to_string(gboolean ret)255 gpm_stats_bool_to_string (gboolean ret)
256 {
257 	return ret ? _("Yes") : _("No");
258 }
259 
260 /**
261  * gpm_stats_get_printable_device_path:
262  **/
263 static gchar *
gpm_stats_get_printable_device_path(UpDevice * device)264 gpm_stats_get_printable_device_path (UpDevice *device)
265 {
266 	const gchar *object_path;
267 	gchar *device_path = NULL;
268 
269 	/* get object path */
270 	object_path = up_device_get_object_path (device);
271 	if (object_path != NULL)
272 		device_path = g_filename_display_basename (object_path);
273 
274 	return device_path;
275 }
276 
277 /**
278  * gpm_stats_update_info_page_details:
279  **/
280 static void
gpm_stats_update_info_page_details(UpDevice * device)281 gpm_stats_update_info_page_details (UpDevice *device)
282 {
283 	struct tm *time_tm;
284 	time_t t;
285 	gchar time_buf[256];
286 	gchar *text;
287 	guint refreshed;
288 	UpDeviceKind kind;
289 	UpDeviceState state;
290 	UpDeviceTechnology technology;
291 	gdouble percentage;
292 	gdouble capacity;
293 	gdouble energy;
294 	gdouble energy_empty;
295 	gdouble energy_full;
296 	gdouble energy_full_design;
297 	gdouble energy_rate;
298 	gdouble voltage;
299 	gboolean online;
300 	gboolean is_present;
301 	gboolean power_supply;
302 	gboolean is_rechargeable;
303 	guint64 update_time;
304 	gint64 time_to_full;
305 	gint64 time_to_empty;
306 	gchar *vendor = NULL;
307 	gchar *serial = NULL;
308 	gchar *model = NULL;
309 	gchar *device_path = NULL;
310 
311 	gtk_list_store_clear (list_store_info);
312 
313 	/* get device properties */
314 	g_object_get (device,
315 		      "kind", &kind,
316 		      "state", &state,
317 		      "percentage", &percentage,
318 		      "online", &online,
319 		      "update_time", &update_time,
320 		      "power_supply", &power_supply,
321 		      "is_rechargeable", &is_rechargeable,
322 		      "is-present", &is_present,
323 		      "time-to-full", &time_to_full,
324 		      "time-to-empty", &time_to_empty,
325 		      "technology", &technology,
326 		      "capacity", &capacity,
327 		      "energy", &energy,
328 		      "energy-empty", &energy_empty,
329 		      "energy-full", &energy_full,
330 		      "energy-full-design", &energy_full_design,
331 		      "energy-rate", &energy_rate,
332 		      "voltage", &voltage,
333 		      "vendor", &vendor,
334 		      "serial", &serial,
335 		      "model", &model,
336 		      NULL);
337 
338 	/* get a human readable time */
339 	t = (time_t) update_time;
340 	time_tm = localtime (&t);
341 	strftime (time_buf, sizeof time_buf, "%c", time_tm);
342 
343 	/* remove prefix */
344 	device_path = gpm_stats_get_printable_device_path (device);
345 	/* TRANSLATORS: the device ID of the current device, e.g. "battery0" */
346 	gpm_stats_add_info_data (_("Device"), device_path);
347 	g_free (device_path);
348 
349 	gpm_stats_add_info_data (_("Type"), gpm_device_kind_to_localised_string (kind, 1));
350 	if (vendor != NULL && vendor[0] != '\0')
351 		gpm_stats_add_info_data (_("Vendor"), vendor);
352 	if (model != NULL && model[0] != '\0')
353 		gpm_stats_add_info_data (_("Model"), model);
354 	if (serial != NULL && serial[0] != '\0')
355 		gpm_stats_add_info_data (_("Serial number"), serial);
356 
357 	/* TRANSLATORS: a boolean attribute that means if the device is supplying the
358 	 * main power for the computer. For instance, an AC adapter or laptop battery
359 	 * would be TRUE,  but a mobile phone or mouse taking power is FALSE */
360 	gpm_stats_add_info_data (_("Supply"), gpm_stats_bool_to_string (power_supply));
361 
362 	refreshed = (int) (time (NULL) - update_time);
363 	text = g_strdup_printf (ngettext ("%d second", "%d seconds", refreshed), refreshed);
364 
365 	/* TRANSLATORS: when the device was last updated with new data. It's
366 	* usually a few seconds when a device is discharging or charging. */
367 	gpm_stats_add_info_data (_("Refreshed"), text);
368 	g_free (text);
369 
370 	if (kind == UP_DEVICE_KIND_BATTERY ||
371 	    kind == UP_DEVICE_KIND_MOUSE ||
372 	    kind == UP_DEVICE_KIND_KEYBOARD ||
373 	    kind == UP_DEVICE_KIND_UPS) {
374 		/* TRANSLATORS: Present is whether the device is currently attached
375 		 * to the computer, as some devices (e.g. laptop batteries) can
376 		 * be removed, but still observed as devices on the system */
377 		gpm_stats_add_info_data (_("Present"), gpm_stats_bool_to_string (is_present));
378 	}
379 	if (kind == UP_DEVICE_KIND_BATTERY ||
380 	    kind == UP_DEVICE_KIND_MOUSE ||
381 	    kind == UP_DEVICE_KIND_KEYBOARD) {
382 		/* TRANSLATORS: If the device can be recharged, e.g. lithium
383 		 * batteries rather than alkaline ones */
384 		gpm_stats_add_info_data (_("Rechargeable"), gpm_stats_bool_to_string (is_rechargeable));
385 	}
386 	if (kind == UP_DEVICE_KIND_BATTERY ||
387 	    kind == UP_DEVICE_KIND_MOUSE ||
388 	    kind == UP_DEVICE_KIND_KEYBOARD) {
389 		/* TRANSLATORS: The state of the device, e.g. "Changing" or "Fully charged" */
390 		gpm_stats_add_info_data (_("State"), gpm_device_state_to_localised_string (state));
391 	}
392 	if (kind == UP_DEVICE_KIND_BATTERY) {
393 		text = g_strdup_printf ("%.1f Wh", energy);
394 		gpm_stats_add_info_data (_("Energy"), text);
395 		g_free (text);
396 		text = g_strdup_printf ("%.1f Wh", energy_empty);
397 		gpm_stats_add_info_data (_("Energy when empty"), text);
398 		g_free (text);
399 		text = g_strdup_printf ("%.1f Wh", energy_full);
400 		gpm_stats_add_info_data (_("Energy when full"), text);
401 		g_free (text);
402 		text = g_strdup_printf ("%.1f Wh", energy_full_design);
403 		gpm_stats_add_info_data (_("Energy (design)"), text);
404 		g_free (text);
405 	}
406 	if (kind == UP_DEVICE_KIND_BATTERY ||
407 	    kind == UP_DEVICE_KIND_MONITOR) {
408 		text = g_strdup_printf ("%.1f W", energy_rate);
409 		/* TRANSLATORS: the rate of discharge for the device */
410 		gpm_stats_add_info_data (_("Rate"), text);
411 		g_free (text);
412 	}
413 	if (kind == UP_DEVICE_KIND_UPS ||
414 	    kind == UP_DEVICE_KIND_BATTERY ||
415 	    kind == UP_DEVICE_KIND_MONITOR) {
416 		text = g_strdup_printf ("%.1f V", voltage);
417 		gpm_stats_add_info_data (_("Voltage"), text);
418 		g_free (text);
419 	}
420 	if (kind == UP_DEVICE_KIND_BATTERY ||
421 	    kind == UP_DEVICE_KIND_UPS) {
422 		if (time_to_full >= 0) {
423 			text = gpm_stats_time_to_string (time_to_full);
424 			gpm_stats_add_info_data (_("Time to full"), text);
425 			g_free (text);
426 		}
427 		if (time_to_empty >= 0) {
428 			text = gpm_stats_time_to_string (time_to_empty);
429 			gpm_stats_add_info_data (_("Time to empty"), text);
430 			g_free (text);
431 		}
432 	}
433 	if (kind == UP_DEVICE_KIND_BATTERY ||
434 	    kind == UP_DEVICE_KIND_MOUSE ||
435 	    kind == UP_DEVICE_KIND_KEYBOARD ||
436 	    kind == UP_DEVICE_KIND_UPS) {
437 		text = g_strdup_printf ("%.1f%%", percentage);
438 		/* TRANSLATORS: the amount of charge the cell contains */
439 		gpm_stats_add_info_data (_("Percentage"), text);
440 		g_free (text);
441 	}
442 	if (kind == UP_DEVICE_KIND_BATTERY) {
443 		text = g_strdup_printf ("%.1f%%", capacity);
444 		/* TRANSLATORS: the capacity of the device, which is basically a measure
445 		 * of how full it can get, relative to the design capacity */
446 		gpm_stats_add_info_data (_("Capacity"), text);
447 		g_free (text);
448 	}
449 	if (kind == UP_DEVICE_KIND_BATTERY) {
450 		/* TRANSLATORS: the type of battery, e.g. lithium or nikel metal hydroxide */
451 		gpm_stats_add_info_data (_("Technology"), gpm_device_technology_to_localised_string (technology));
452 	}
453 	if (kind == UP_DEVICE_KIND_LINE_POWER) {
454 		/* TRANSLATORS: this is when the device is plugged in, typically
455 		 * only shown for the ac adaptor device */
456 		gpm_stats_add_info_data (_("Online"), gpm_stats_bool_to_string (online));
457 	}
458 
459 	g_free (vendor);
460 	g_free (serial);
461 	g_free (model);
462 }
463 
464 /**
465  * gpm_stats_set_graph_data:
466  **/
467 static void
gpm_stats_set_graph_data(GtkWidget * widget,GPtrArray * data,gboolean use_smoothed,gboolean use_points)468 gpm_stats_set_graph_data (GtkWidget *widget, GPtrArray *data, gboolean use_smoothed, gboolean use_points)
469 {
470 	GPtrArray *smoothed;
471 
472 	gpm_graph_widget_data_clear (GPM_GRAPH_WIDGET (widget));
473 
474 	/* add correct data */
475 	if (!use_smoothed) {
476 		if (use_points)
477 			gpm_graph_widget_data_assign (GPM_GRAPH_WIDGET (widget), GPM_GRAPH_WIDGET_PLOT_BOTH, data);
478 		else
479 			gpm_graph_widget_data_assign (GPM_GRAPH_WIDGET (widget), GPM_GRAPH_WIDGET_PLOT_LINE, data);
480 	} else {
481 		smoothed = gpm_stats_update_smooth_data (data);
482 		if (use_points)
483 			gpm_graph_widget_data_assign (GPM_GRAPH_WIDGET (widget), GPM_GRAPH_WIDGET_PLOT_POINTS, data);
484 		gpm_graph_widget_data_assign (GPM_GRAPH_WIDGET (widget), GPM_GRAPH_WIDGET_PLOT_LINE, smoothed);
485 		g_ptr_array_unref (smoothed);
486 	}
487 
488 	/* show */
489 	gtk_widget_show (widget);
490 }
491 
492 /**
493  * gpm_stats_update_info_page_history:
494  **/
495 static void
gpm_stats_update_info_page_history(UpDevice * device)496 gpm_stats_update_info_page_history (UpDevice *device)
497 {
498 	GPtrArray *array;
499 	guint i;
500 	UpHistoryItem *item;
501 	GtkWidget *widget;
502 	gboolean checked;
503 	gboolean points;
504 	GpmPointObj *point;
505 	GPtrArray *new;
506 	gint32 offset;
507 
508 	new = g_ptr_array_new_with_free_func ((GDestroyNotify) gpm_point_obj_free);
509 	if (g_strcmp0 (history_type, GPM_HISTORY_CHARGE_VALUE) == 0) {
510 		g_object_set (graph_history,
511 			      "type-x", GPM_GRAPH_WIDGET_TYPE_TIME,
512 			      "type-y", GPM_GRAPH_WIDGET_TYPE_PERCENTAGE,
513 			      "autorange-x", FALSE,
514 			      "start-x", -history_time,
515 			      "stop-x", 0,
516 			      "autorange-y", FALSE,
517 			      "start-y", 0,
518 			      "stop-y", 100,
519 			      NULL);
520 	} else if (g_strcmp0 (history_type, GPM_HISTORY_RATE_VALUE) == 0) {
521 		g_object_set (graph_history,
522 			      "type-x", GPM_GRAPH_WIDGET_TYPE_TIME,
523 			      "type-y", GPM_GRAPH_WIDGET_TYPE_POWER,
524 			      "autorange-x", FALSE,
525 			      "start-x", -history_time,
526 			      "stop-x", 0,
527 			      "autorange-y", TRUE,
528 			      NULL);
529 	} else {
530 		g_object_set (graph_history,
531 			      "type-x", GPM_GRAPH_WIDGET_TYPE_TIME,
532 			      "type-y", GPM_GRAPH_WIDGET_TYPE_TIME,
533 			      "autorange-x", FALSE,
534 			      "start-x", -history_time,
535 			      "stop-x", 0,
536 			      "autorange-y", TRUE,
537 			      NULL);
538 	}
539 
540 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_history_nodata"));
541 	array = up_device_get_history_sync (device, history_type, history_time, 150, NULL, NULL);
542 	if (array == NULL) {
543 		/* show no data label and hide graph */
544 		gtk_widget_hide (graph_history);
545 		gtk_widget_show (widget);
546 		goto out;
547 	}
548 
549 	/* hide no data and show graph */
550 	gtk_widget_hide (widget);
551 	gtk_widget_show (graph_history);
552 
553 	offset = (gint32) (g_get_real_time () / G_USEC_PER_SEC);
554 
555 	for (i=0; i<array->len; i++) {
556 		item = (UpHistoryItem *) g_ptr_array_index (array, i);
557 
558 		/* abandon this point */
559 		if (up_history_item_get_state (item) == UP_DEVICE_STATE_UNKNOWN)
560 			continue;
561 
562 		point = gpm_point_obj_new ();
563 		point->x = ((gint32) up_history_item_get_time (item)) - offset;
564 		point->y = up_history_item_get_value (item);
565 		if (up_history_item_get_state (item) == UP_DEVICE_STATE_CHARGING)
566 			point->color = egg_color_from_rgb (255, 0, 0);
567 		else if (up_history_item_get_state (item) == UP_DEVICE_STATE_DISCHARGING)
568 			point->color = egg_color_from_rgb (0, 0, 255);
569 		else if (up_history_item_get_state (item) == UP_DEVICE_STATE_PENDING_CHARGE)
570 			point->color = egg_color_from_rgb (200, 0, 0);
571 		else if (up_history_item_get_state (item) == UP_DEVICE_STATE_PENDING_DISCHARGE)
572 			point->color = egg_color_from_rgb (0, 0, 200);
573 		else {
574 			if (g_strcmp0 (history_type, GPM_HISTORY_RATE_VALUE) == 0)
575 				point->color = egg_color_from_rgb (255, 255, 255);
576 			else
577 				point->color = egg_color_from_rgb (0, 255, 0);
578 		}
579 		g_ptr_array_add (new, point);
580 	}
581 
582 	/* render */
583 	sigma_smoothing = 2.0;
584 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_smooth_history"));
585 	checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
586 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_points_history"));
587 	points = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
588 
589 	/* present data to graph */
590 	gpm_stats_set_graph_data (graph_history, new, checked, points);
591 
592 	g_ptr_array_unref (array);
593 	g_ptr_array_unref (new);
594 out:
595 	return;
596 }
597 
598 /**
599  * gpm_stats_update_info_page_stats:
600  **/
601 static void
gpm_stats_update_info_page_stats(UpDevice * device)602 gpm_stats_update_info_page_stats (UpDevice *device)
603 {
604 	GPtrArray *array;
605 	guint i;
606 	UpStatsItem *item;
607 	GtkWidget *widget;
608 	gboolean checked;
609 	gboolean points;
610 	GpmPointObj *point;
611 	GPtrArray *new;
612 	gboolean use_data = FALSE;
613 	const gchar *type = NULL;
614 
615 	new = g_ptr_array_new_with_free_func ((GDestroyNotify) gpm_point_obj_free);
616 	if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0) {
617 		type = "charging";
618 		use_data = TRUE;
619 	} else if (g_strcmp0 (stats_type, GPM_STATS_DISCHARGE_DATA_VALUE) == 0) {
620 		type = "discharging";
621 		use_data = TRUE;
622 	} else if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_ACCURACY_VALUE) == 0) {
623 		type = "charging";
624 		use_data = FALSE;
625 	} else if (g_strcmp0 (stats_type, GPM_STATS_DISCHARGE_ACCURACY_VALUE) == 0) {
626 		type = "discharging";
627 		use_data = FALSE;
628 	} else {
629 		g_assert_not_reached ();
630 	}
631 
632 	if (use_data) {
633 		g_object_set (graph_statistics,
634 			      "type-x", GPM_GRAPH_WIDGET_TYPE_PERCENTAGE,
635 			      "type-y", GPM_GRAPH_WIDGET_TYPE_FACTOR,
636 			      "autorange-x", TRUE,
637 			      "autorange-y", TRUE,
638 			      NULL);
639 	} else {
640 		g_object_set (graph_statistics,
641 			      "type-x", GPM_GRAPH_WIDGET_TYPE_PERCENTAGE,
642 			      "type-y", GPM_GRAPH_WIDGET_TYPE_PERCENTAGE,
643 			      "autorange-x", TRUE,
644 			      "autorange-y", TRUE,
645 			      NULL);
646 	}
647 
648 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_stats_nodata"));
649 	array = up_device_get_statistics_sync (device, type, NULL, NULL);
650 	if (array == NULL) {
651 		/* show no data label and hide graph */
652 		gtk_widget_hide (graph_statistics);
653 		gtk_widget_show (widget);
654 		goto out;
655 	}
656 
657 	/* hide no data and show graph */
658 	gtk_widget_hide (widget);
659 	gtk_widget_show (graph_statistics);
660 
661 	for (i=0; i<array->len; i++) {
662 		item = (UpStatsItem *) g_ptr_array_index (array, i);
663 		point = gpm_point_obj_new ();
664 		point->x = i;
665 		if (use_data)
666 			point->y = up_stats_item_get_value (item);
667 		else
668 			point->y = up_stats_item_get_accuracy (item);
669 		point->color = egg_color_from_rgb (255, 0, 0);
670 		g_ptr_array_add (new, point);
671 	}
672 
673 	/* render */
674 	sigma_smoothing = 1.1;
675 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_smooth_stats"));
676 	checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
677 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_points_stats"));
678 	points = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
679 
680 	/* present data to graph */
681 	gpm_stats_set_graph_data (graph_statistics, new, checked, points);
682 
683 	g_ptr_array_unref (array);
684 	g_ptr_array_unref (new);
685 out:
686 	return;
687 }
688 
689 /**
690  * gpm_stats_update_info_data_page:
691  **/
692 static void
gpm_stats_update_info_data_page(UpDevice * device,gint page)693 gpm_stats_update_info_data_page (UpDevice *device, gint page)
694 {
695 	if (page == 0)
696 		gpm_stats_update_info_page_details (device);
697 	else if (page == 1)
698 		gpm_stats_update_info_page_history (device);
699 	else if (page == 2)
700 		gpm_stats_update_info_page_stats (device);
701 }
702 
703 /**
704  * gpm_stats_update_info_data:
705  **/
706 static void
gpm_stats_update_info_data(UpDevice * device)707 gpm_stats_update_info_data (UpDevice *device)
708 {
709 	gint page;
710 	GtkNotebook *notebook;
711 	GtkWidget *page_widget;
712 	gboolean has_history;
713 	gboolean has_statistics;
714 
715 	/* get properties */
716 	g_object_get (device,
717 		      "has-history", &has_history,
718 		      "has-statistics", &has_statistics,
719 		      NULL);
720 
721 
722 	notebook = GTK_NOTEBOOK (gtk_builder_get_object (builder, "notebook1"));
723 
724 	/* show info page */
725 	page_widget = gtk_notebook_get_nth_page (notebook, 0);
726 	gtk_widget_show (page_widget);
727 
728 	/* hide history if no support */
729 	page_widget = gtk_notebook_get_nth_page (notebook, 1);
730 	if (has_history)
731 		gtk_widget_show (page_widget);
732 	else
733 		gtk_widget_hide (page_widget);
734 
735 	/* hide statistics if no support */
736 	page_widget = gtk_notebook_get_nth_page (notebook, 2);
737 	if (has_statistics)
738 		gtk_widget_show (page_widget);
739 	else
740 		gtk_widget_hide (page_widget);
741 
742 	page = gtk_notebook_get_current_page (notebook);
743 	gpm_stats_update_info_data_page (device, page);
744 
745 	return;
746 }
747 
748 static void
gpm_stats_set_title(GtkWindow * window,gint page_num)749 gpm_stats_set_title (GtkWindow *window, gint page_num)
750 {
751 	gchar *title;
752 	const gchar * const page_titles[] = {
753 		/* TRANSLATORS: shown on the titlebar */
754 		N_("Device Information"),
755 		/* TRANSLATORS: shown on the titlebar */
756 		N_("Device History"),
757 		/* TRANSLATORS: shown on the titlebar */
758 		N_("Device Profile"),
759 	};
760 
761 	/* TRANSLATORS: shown on the titlebar */
762 	title = g_strdup_printf ("%s - %s", _("Power Statistics"), _(page_titles[page_num]));
763 	gtk_window_set_title (window, title);
764 	g_free (title);
765 }
766 
767 /**
768  * gpm_stats_notebook_changed_cb:
769  **/
770 static void
gpm_stats_notebook_changed_cb(GtkNotebook * notebook,gpointer page,gint page_num,gpointer user_data)771 gpm_stats_notebook_changed_cb (GtkNotebook *notebook, gpointer page, gint page_num, gpointer user_data)
772 {
773 	UpDevice *device;
774 	GtkWidget *widget;
775 
776 	/* set the window title depending on the mode */
777 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_stats"));
778 	gpm_stats_set_title (GTK_WINDOW (widget), page_num);
779 
780 	/* save page in gsettings */
781 	g_settings_set_int (settings, GPM_SETTINGS_INFO_PAGE_NUMBER, page_num);
782 
783 	if (current_device == NULL)
784 		return;
785 
786 	device = up_device_new ();
787 	up_device_set_object_path_sync (device, current_device, NULL, NULL);
788 	gpm_stats_update_info_data_page (device, page_num);
789 	gpm_stats_update_info_data (device);
790 	g_object_unref (device);
791 }
792 
793 /**
794  * gpm_stats_button_update_ui:
795  **/
796 static void
gpm_stats_button_update_ui(void)797 gpm_stats_button_update_ui (void)
798 {
799 	UpDevice *device;
800 
801 	if (current_device == NULL)
802 		return;
803 
804 	device = up_device_new ();
805 	up_device_set_object_path_sync (device, current_device, NULL, NULL);
806 	gpm_stats_update_info_data (device);
807 	g_object_unref (device);
808 }
809 
810 /**
811  * gpm_stats_devices_treeview_clicked_cb:
812  **/
813 static void
gpm_stats_devices_treeview_clicked_cb(GtkTreeSelection * selection,gboolean data)814 gpm_stats_devices_treeview_clicked_cb (GtkTreeSelection *selection, gboolean data)
815 {
816 	GtkTreeModel *model;
817 	GtkTreeIter iter;
818 	UpDevice *device;
819 
820 	/* This will only work in single or browse selection mode! */
821 	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
822 		g_free (current_device);
823 		gtk_tree_model_get (model, &iter, GPM_DEVICES_COLUMN_ID, &current_device, -1);
824 
825 		/* save device in gsettings */
826 		g_settings_set_string (settings, GPM_SETTINGS_INFO_LAST_DEVICE, current_device);
827 
828 		/* show transaction_id */
829 		g_debug ("selected row is: %s", current_device);
830 
831 		/* is special device */
832 		device = up_device_new ();
833 		up_device_set_object_path_sync (device, current_device, NULL, NULL);
834 		gpm_stats_update_info_data (device);
835 		g_object_unref (device);
836 
837 	} else {
838 		g_debug ("no row selected");
839 	}
840 }
841 
842 /**
843  * gpm_stats_window_activated_cb
844  **/
845 static void
gpm_stats_window_activated_cb(GtkApplication * app,gpointer data)846 gpm_stats_window_activated_cb (GtkApplication *app, gpointer data)
847 {
848 	GtkWidget *widget;
849 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_stats"));
850 	gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (widget));
851 	gtk_window_present (GTK_WINDOW (widget));
852 }
853 
854 /**
855  * gpm_stats_device_changed_cb:
856  **/
857 static void
gpm_stats_device_changed_cb(UpDevice * device,GParamSpec * pspec,gpointer user_data)858 gpm_stats_device_changed_cb (UpDevice *device, GParamSpec *pspec, gpointer user_data)
859 {
860 	const gchar *object_path;
861 	object_path = up_device_get_object_path (device);
862 	if (object_path == NULL || current_device == NULL)
863 		return;
864 	g_debug ("changed:   %s", object_path);
865 	if (g_strcmp0 (current_device, object_path) == 0)
866 		gpm_stats_update_info_data (device);
867 }
868 
869 /**
870  * gpm_stats_add_device:
871  **/
872 static void
gpm_stats_add_device(UpDevice * device,GPtrArray * devices)873 gpm_stats_add_device (UpDevice *device, GPtrArray *devices)
874 {
875 	const gchar *id;
876 	GtkTreeIter iter;
877 	const gchar *icon;
878 	UpDeviceKind kind;
879 	gchar *label, *vendor, *model;
880 
881 	if (devices != NULL)
882 		g_ptr_array_add (devices, device);
883 
884 	g_signal_connect (device, "notify",
885 	                  G_CALLBACK (gpm_stats_device_changed_cb), NULL);
886 
887 	/* get device properties */
888 	g_object_get (device,
889 		      "kind", &kind,
890 		      "vendor", &vendor,
891 		      "model", &model,
892 		      NULL);
893 
894 	id = up_device_get_object_path (device);
895 	if ((vendor != NULL && strlen(vendor) != 0) && (model != NULL && strlen(model) != 0)) {
896 		label = g_strdup_printf ("%s %s", vendor, model);
897 	}
898 	else {
899 		label = g_strdup_printf ("%s", gpm_device_kind_to_localised_string (kind, 1));
900 	}
901 	icon = gpm_upower_get_device_icon (device);
902 
903 	gtk_list_store_append (list_store_devices, &iter);
904 	gtk_list_store_set (list_store_devices, &iter,
905 			    GPM_DEVICES_COLUMN_ID, id,
906 			    GPM_DEVICES_COLUMN_TEXT, label,
907 			    GPM_DEVICES_COLUMN_ICON, icon, -1);
908 	g_free (label);
909 	g_free (vendor);
910 	g_free (model);
911 }
912 
913 /**
914  * gpm_stats_device_added_cb:
915  **/
916 static void
gpm_stats_device_added_cb(UpClient * client,UpDevice * device,GPtrArray * devices)917 gpm_stats_device_added_cb (UpClient *client, UpDevice *device, GPtrArray *devices)
918 {
919 	const gchar *object_path;
920 	object_path = up_device_get_object_path (device);
921 	g_debug ("added:     %s", object_path);
922 
923 	gpm_stats_add_device (device, devices);
924 }
925 
926 /**
927  * gpm_stats_device_removed_cb:
928  **/
929 static void
gpm_stats_device_removed_cb(UpClient * client,const gchar * object_path,GPtrArray * devices)930 gpm_stats_device_removed_cb (UpClient *client, const gchar *object_path, GPtrArray *devices)
931 {
932 	GtkTreeIter iter;
933 	gchar *id = NULL;
934 	gboolean ret;
935 
936 	UpDevice *device_tmp;
937 	guint i;
938 
939 	for (i = 0; i < devices->len; i++) {
940 		device_tmp = g_ptr_array_index (devices, i);
941 		if (g_strcmp0 (up_device_get_object_path (device_tmp), object_path) == 0) {
942 			g_ptr_array_remove_index_fast (devices, i);
943 			break;
944 		}
945 	}
946 	g_debug ("removed:   %s", object_path);
947 	if (g_strcmp0 (current_device, object_path) == 0) {
948 		gtk_list_store_clear (list_store_info);
949 	}
950 
951 	/* search the list and remove the object path entry */
952 	ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store_devices), &iter);
953 	while (ret) {
954 		gtk_tree_model_get (GTK_TREE_MODEL (list_store_devices), &iter, GPM_DEVICES_COLUMN_ID, &id, -1);
955 		if (g_strcmp0 (id, object_path) == 0) {
956 			gtk_list_store_remove (list_store_devices, &iter);
957 			break;
958 		}
959 		g_free (id);
960 		ret = gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store_devices), &iter);
961 	};
962 }
963 
964 /**
965  * gpm_stats_history_type_combo_changed_cb:
966  **/
967 static void
gpm_stats_history_type_combo_changed_cb(GtkWidget * widget,gpointer data)968 gpm_stats_history_type_combo_changed_cb (GtkWidget *widget, gpointer data)
969 {
970 	guint active;
971 	const gchar *axis_x = NULL;
972 	const gchar *axis_y = NULL;
973 
974 	active = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
975 
976 	if (active == 0) {
977 		history_type = GPM_HISTORY_RATE_VALUE;
978 		/* TRANSLATORS: this is the X axis on the graph */
979 		axis_x = _("Time elapsed");
980 		/* TRANSLATORS: this is the Y axis on the graph */
981 		axis_y = _("Power");
982 	} else if (active == 1) {
983 		history_type = GPM_HISTORY_CHARGE_VALUE;
984 		/* TRANSLATORS: this is the X axis on the graph */
985 		axis_x = _("Time elapsed");
986 		/* TRANSLATORS: this is the Y axis on the graph for the whole battery device */
987 		axis_y = _("Cell charge");
988 	} else if (active == 2) {
989 		history_type = GPM_HISTORY_TIME_FULL_VALUE;
990 		/* TRANSLATORS: this is the X axis on the graph */
991 		axis_x = _("Time elapsed");
992 		/* TRANSLATORS: this is the Y axis on the graph */
993 		axis_y = _("Predicted time");
994 	} else if (active == 3) {
995 		history_type = GPM_HISTORY_TIME_EMPTY_VALUE;
996 		/* TRANSLATORS: this is the X axis on the graph */
997 		axis_x = _("Time elapsed");
998 		/* TRANSLATORS: this is the Y axis on the graph */
999 		axis_y = _("Predicted time");
1000 	} else {
1001 		g_assert (FALSE);
1002 	}
1003 
1004 	/* set axis */
1005 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_axis_history_x"));
1006 	gtk_label_set_label (GTK_LABEL(widget), axis_x);
1007 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_axis_history_y"));
1008 	gtk_label_set_label (GTK_LABEL(widget), axis_y);
1009 
1010 	gpm_stats_button_update_ui ();
1011 
1012 	/* save to gsettings */
1013 	g_settings_set_string (settings, GPM_SETTINGS_INFO_HISTORY_TYPE, history_type);
1014 }
1015 
1016 /**
1017  * gpm_stats_type_combo_changed_cb:
1018  **/
1019 static void
gpm_stats_type_combo_changed_cb(GtkWidget * widget,gpointer data)1020 gpm_stats_type_combo_changed_cb (GtkWidget *widget, gpointer data)
1021 {
1022 	guint active;
1023 	const gchar *axis_x = NULL;
1024 	const gchar *axis_y = NULL;
1025 
1026 	active = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
1027 
1028 	if (active == 0) {
1029 		stats_type = GPM_STATS_CHARGE_DATA_VALUE;
1030 		/* TRANSLATORS: this is the X axis on the graph for the whole battery device */
1031 		axis_x = _("Cell charge");
1032 		/* TRANSLATORS: this is the Y axis on the graph */
1033 		axis_y = _("Correction factor");
1034 	} else if (active == 1) {
1035 		stats_type = GPM_STATS_CHARGE_ACCURACY_VALUE;
1036 		/* TRANSLATORS: this is the X axis on the graph for the whole battery device */
1037 		axis_x = _("Cell charge");
1038 		/* TRANSLATORS: this is the Y axis on the graph */
1039 		axis_y = _("Prediction accuracy");
1040 	} else if (active == 2) {
1041 		stats_type = GPM_STATS_DISCHARGE_DATA_VALUE;
1042 		/* TRANSLATORS: this is the X axis on the graph for the whole battery device */
1043 		axis_x = _("Cell charge");
1044 		/* TRANSLATORS: this is the Y axis on the graph */
1045 		axis_y = _("Correction factor");
1046 	} else if (active == 3) {
1047 		stats_type = GPM_STATS_DISCHARGE_ACCURACY_VALUE;
1048 		/* TRANSLATORS: this is the X axis on the graph for the whole battery device */
1049 		axis_x = _("Cell charge");
1050 		/* TRANSLATORS: this is the Y axis on the graph */
1051 		axis_y = _("Prediction accuracy");
1052 	} else {
1053 		g_assert (FALSE);
1054 	}
1055 
1056 	/* set axis */
1057 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_axis_stats_x"));
1058 	gtk_label_set_label (GTK_LABEL(widget), axis_x);
1059 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "label_axis_stats_y"));
1060 	gtk_label_set_label (GTK_LABEL(widget), axis_y);
1061 
1062 	gpm_stats_button_update_ui ();
1063 
1064 	/* save to gsettings */
1065 	g_settings_set_string (settings, GPM_SETTINGS_INFO_STATS_TYPE, stats_type);
1066 }
1067 
1068 /**
1069  * gpm_stats_range_combo_changed:
1070  **/
1071 static void
gpm_stats_range_combo_changed(GtkWidget * widget,gpointer data)1072 gpm_stats_range_combo_changed (GtkWidget *widget, gpointer data)
1073 {
1074 	guint active;
1075 
1076 	active = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
1077 
1078 	if (active == 0)
1079 		history_time = GPM_HISTORY_MINUTE_VALUE;
1080 	else if (active == 1)
1081 		history_time = GPM_HISTORY_HOUR_VALUE;
1082 	else if (active == 2)
1083 		history_time = GPM_HISTORY_HOURS_VALUE;
1084 	else if (active == 3)
1085 		history_time = GPM_HISTORY_DAY_VALUE;
1086 	else if (active == 4)
1087 		history_time = GPM_HISTORY_WEEK_VALUE;
1088 	else
1089 		g_assert (FALSE);
1090 
1091 	/* save to gsettings */
1092 	g_settings_set_int (settings, GPM_SETTINGS_INFO_HISTORY_TIME, history_time);
1093 
1094 	gpm_stats_button_update_ui ();
1095 }
1096 
1097 /**
1098  * gpm_stats_smooth_checkbox_history_cb:
1099  * @widget: The GtkWidget object
1100  **/
1101 static void
gpm_stats_smooth_checkbox_history_cb(GtkWidget * widget,gpointer data)1102 gpm_stats_smooth_checkbox_history_cb (GtkWidget *widget, gpointer data)
1103 {
1104 	gboolean checked;
1105 	checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
1106 	g_settings_set_boolean (settings, GPM_SETTINGS_INFO_HISTORY_GRAPH_SMOOTH, checked);
1107 	gpm_stats_button_update_ui ();
1108 }
1109 
1110 /**
1111  * gpm_stats_smooth_checkbox_stats_cb:
1112  * @widget: The GtkWidget object
1113  **/
1114 static void
gpm_stats_smooth_checkbox_stats_cb(GtkWidget * widget,gpointer data)1115 gpm_stats_smooth_checkbox_stats_cb (GtkWidget *widget, gpointer data)
1116 {
1117 	gboolean checked;
1118 	checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
1119 	g_settings_set_boolean (settings, GPM_SETTINGS_INFO_STATS_GRAPH_SMOOTH, checked);
1120 	gpm_stats_button_update_ui ();
1121 }
1122 
1123 /**
1124  * gpm_stats_points_checkbox_history_cb:
1125  * @widget: The GtkWidget object
1126  **/
1127 static void
gpm_stats_points_checkbox_history_cb(GtkWidget * widget,gpointer data)1128 gpm_stats_points_checkbox_history_cb (GtkWidget *widget, gpointer data)
1129 {
1130 	gboolean checked;
1131 	checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
1132 	g_settings_set_boolean (settings, GPM_SETTINGS_INFO_HISTORY_GRAPH_POINTS, checked);
1133 	gpm_stats_button_update_ui ();
1134 }
1135 
1136 /**
1137  * gpm_stats_points_checkbox_stats_cb:
1138  * @widget: The GtkWidget object
1139  **/
1140 static void
gpm_stats_points_checkbox_stats_cb(GtkWidget * widget,gpointer data)1141 gpm_stats_points_checkbox_stats_cb (GtkWidget *widget, gpointer data)
1142 {
1143 	gboolean checked;
1144 	checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
1145 	g_settings_set_boolean (settings, GPM_SETTINGS_INFO_STATS_GRAPH_POINTS, checked);
1146 	gpm_stats_button_update_ui ();
1147 }
1148 
1149 /**
1150  * gpm_stats_highlight_device:
1151  **/
1152 static void
gpm_stats_highlight_device(const gchar * object_path)1153 gpm_stats_highlight_device (const gchar *object_path)
1154 {
1155 	gboolean ret;
1156 	gchar *id = NULL;
1157 	gchar *path_str;
1158 	guint i;
1159 	GtkTreeIter iter;
1160 	GtkTreePath *path;
1161 	GtkWidget *widget;
1162 
1163 	/* check valid */
1164 	if (!g_str_has_prefix (object_path, "/"))
1165 		return;
1166 
1167 	/* we have to reuse the treeview data as it may be sorted */
1168 	ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store_devices), &iter);
1169 	for (i=0; ret; i++) {
1170 		gtk_tree_model_get (GTK_TREE_MODEL (list_store_devices), &iter,
1171 				    GPM_DEVICES_COLUMN_ID, &id,
1172 				    -1);
1173 		if (g_strcmp0 (id, object_path) == 0) {
1174 			path_str = g_strdup_printf ("%i", i);
1175 			path = gtk_tree_path_new_from_string (path_str);
1176 			widget = GTK_WIDGET (gtk_builder_get_object (builder, "treeview_devices"));
1177 			gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (widget), path, NULL, NULL, FALSE);
1178 			g_free (path_str);
1179 			gtk_tree_path_free (path);
1180 		}
1181 		g_free (id);
1182 		ret = gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store_devices), &iter);
1183 	}
1184 }
1185 
1186 /**
1187  * main:
1188  **/
1189 int
main(int argc,char * argv[])1190 main (int argc, char *argv[])
1191 {
1192 	GOptionContext *context;
1193 	GtkBox *box;
1194 	GtkWidget *widget, *window;
1195 	GtkTreeSelection *selection;
1196 	GtkApplication *app;
1197 	gint status;
1198 	UpClient *client;
1199 	GPtrArray *devices = NULL;
1200 	UpDevice *device;
1201 	UpDeviceKind kind;
1202 	guint i, j;
1203 	gint page;
1204 	gboolean checked;
1205 	gchar *last_device = NULL;
1206 
1207 	const GOptionEntry options[] = {
1208 		{ "device", '\0', 0, G_OPTION_ARG_STRING, &last_device,
1209 		  /* TRANSLATORS: show a device by default */
1210 		  N_("Select this device at startup"), NULL },
1211 		{ NULL}
1212 	};
1213 
1214 	setlocale (LC_ALL, "");
1215 
1216 	bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
1217 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1218 	textdomain (GETTEXT_PACKAGE);
1219 
1220 	dbus_g_thread_init ();
1221 
1222 	context = g_option_context_new (NULL);
1223 	/* TRANSLATORS: the program name */
1224 	g_option_context_set_summary (context, _("Power Statistics"));
1225 	g_option_context_add_main_entries (context, options, NULL);
1226 	g_option_context_parse (context, &argc, &argv, NULL);
1227 	g_option_context_free (context);
1228 
1229 	gtk_init (&argc, &argv);
1230 
1231 	app = gtk_application_new ("org.mate.PowerManager.Statistics", 0);
1232 
1233 	g_signal_connect (app, "activate",
1234 			  G_CALLBACK (gpm_stats_window_activated_cb), NULL);
1235 
1236 	/* add application specific icons to search path */
1237 	gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
1238                                            GPM_ICONS_DATA);
1239 
1240 	/* get data from the settings */
1241 	settings = g_settings_new (GPM_SETTINGS_SCHEMA);
1242 
1243 	/* get UI */
1244 	builder = gtk_builder_new_from_resource ("/org/mate/powermanager/statistics/gpm-statistics.ui");
1245 
1246 	/* add history graph */
1247 	box = GTK_BOX (gtk_builder_get_object (builder, "hbox_history"));
1248 	graph_history = gpm_graph_widget_new ();
1249 	gtk_box_pack_start (box, graph_history, TRUE, TRUE, 0);
1250 	gtk_widget_set_size_request (graph_history, 400, 250);
1251 	gtk_widget_show (graph_history);
1252 
1253 	/* add statistics graph */
1254 	box = GTK_BOX (gtk_builder_get_object (builder, "hbox_statistics"));
1255 	graph_statistics = gpm_graph_widget_new ();
1256 	gtk_box_pack_start (box, graph_statistics, TRUE, TRUE, 0);
1257 	gtk_widget_set_size_request (graph_statistics, 400, 250);
1258 	gtk_widget_show (graph_statistics);
1259 
1260 	window = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_stats"));
1261 	gtk_window_set_default_size (GTK_WINDOW(window), 800, 500);
1262 	gtk_window_set_default_icon_name (GPM_ICON_APP_ICON);
1263 
1264 	/* Get the main window quit */
1265 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "button_close"));
1266 	g_signal_connect_swapped (window, "delete_event", G_CALLBACK (gtk_widget_destroy), window);
1267 	g_signal_connect_swapped (widget, "clicked", G_CALLBACK (gtk_widget_destroy), window);
1268 	gtk_widget_grab_default (widget);
1269 
1270 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "button_help"));
1271 	g_signal_connect (widget, "clicked",
1272 			  G_CALLBACK (gpm_stats_button_help_cb), NULL);
1273 
1274 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_smooth_history"));
1275 	checked = g_settings_get_boolean (settings, GPM_SETTINGS_INFO_HISTORY_GRAPH_SMOOTH);
1276 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), checked);
1277 	g_signal_connect (widget, "clicked",
1278 			  G_CALLBACK (gpm_stats_smooth_checkbox_history_cb), NULL);
1279 
1280 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_smooth_stats"));
1281 	checked = g_settings_get_boolean (settings, GPM_SETTINGS_INFO_STATS_GRAPH_SMOOTH);
1282 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), checked);
1283 	g_signal_connect (widget, "clicked",
1284 			  G_CALLBACK (gpm_stats_smooth_checkbox_stats_cb), NULL);
1285 
1286 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_points_history"));
1287 	checked = g_settings_get_boolean (settings, GPM_SETTINGS_INFO_HISTORY_GRAPH_POINTS);
1288 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), checked);
1289 	g_signal_connect (widget, "clicked",
1290 			  G_CALLBACK (gpm_stats_points_checkbox_history_cb), NULL);
1291 
1292 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "checkbutton_points_stats"));
1293 	checked = g_settings_get_boolean (settings, GPM_SETTINGS_INFO_STATS_GRAPH_POINTS);
1294 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), checked);
1295 	g_signal_connect (widget, "clicked",
1296 			  G_CALLBACK (gpm_stats_points_checkbox_stats_cb), NULL);
1297 
1298 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "notebook1"));
1299 
1300 	gtk_widget_add_events (widget, GDK_SCROLL_MASK);
1301 	g_signal_connect (widget,
1302 	                  "scroll-event",
1303 	                  G_CALLBACK (gpm_notebook_scroll_event_cb),
1304 	                  NULL);
1305 
1306 	page = g_settings_get_int (settings, GPM_SETTINGS_INFO_PAGE_NUMBER);
1307 	gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), page);
1308 	g_signal_connect (widget, "switch-page",
1309 			  G_CALLBACK (gpm_stats_notebook_changed_cb), NULL);
1310 
1311 	/* create list stores */
1312 	list_store_info = gtk_list_store_new (GPM_INFO_COLUMN_LAST, G_TYPE_STRING, G_TYPE_STRING);
1313 	list_store_devices = gtk_list_store_new (GPM_DEVICES_COLUMN_LAST, G_TYPE_STRING,
1314 						 G_TYPE_STRING, G_TYPE_STRING);
1315 
1316 	/* create transaction_id tree view */
1317 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "treeview_info"));
1318 	gtk_tree_view_set_model (GTK_TREE_VIEW (widget),
1319 				 GTK_TREE_MODEL (list_store_info));
1320 
1321 	/* add columns to the tree view */
1322 	gpm_stats_add_info_columns (GTK_TREE_VIEW (widget));
1323 	gtk_tree_view_columns_autosize (GTK_TREE_VIEW (widget)); /* show */
1324 
1325 	/* create transaction_id tree view */
1326 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "treeview_devices"));
1327 	gtk_tree_view_set_model (GTK_TREE_VIEW (widget),
1328 				 GTK_TREE_MODEL (list_store_devices));
1329 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1330 	g_signal_connect (selection, "changed",
1331 			  G_CALLBACK (gpm_stats_devices_treeview_clicked_cb), NULL);
1332 
1333 	/* add columns to the tree view */
1334 	gpm_stats_add_devices_columns (GTK_TREE_VIEW (widget));
1335 	gtk_tree_view_columns_autosize (GTK_TREE_VIEW (widget)); /* show */
1336 
1337 	history_type = g_settings_get_string (settings, GPM_SETTINGS_INFO_HISTORY_TYPE);
1338 	history_time = g_settings_get_int (settings, GPM_SETTINGS_INFO_HISTORY_TIME);
1339 	if (history_type == NULL)
1340 		history_type = GPM_HISTORY_CHARGE_VALUE;
1341 	if (history_time == 0)
1342 		history_time = GPM_HISTORY_HOUR_VALUE;
1343 
1344 	stats_type = g_settings_get_string (settings, GPM_SETTINGS_INFO_STATS_TYPE);
1345 	if (stats_type == NULL)
1346 		stats_type = GPM_STATS_CHARGE_DATA_VALUE;
1347 
1348 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_history_type"));
1349 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_HISTORY_RATE_TEXT);
1350 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_HISTORY_CHARGE_TEXT);
1351 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_HISTORY_TIME_FULL_TEXT);
1352 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_HISTORY_TIME_EMPTY_TEXT);
1353 	if (g_strcmp0 (history_type, GPM_HISTORY_RATE_VALUE) == 0)
1354 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
1355 	else
1356 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1);
1357 	g_signal_connect (G_OBJECT (widget), "changed",
1358 			  G_CALLBACK (gpm_stats_history_type_combo_changed_cb), NULL);
1359 
1360 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_stats_type"));
1361 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_STATS_CHARGE_DATA_TEXT);
1362 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_STATS_CHARGE_ACCURACY_TEXT);
1363 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_STATS_DISCHARGE_DATA_TEXT);
1364 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_STATS_DISCHARGE_ACCURACY_TEXT);
1365 	if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0)
1366 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
1367 	else if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0)
1368 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1);
1369 	else if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0)
1370 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 2);
1371 	else if (g_strcmp0 (stats_type, GPM_STATS_CHARGE_DATA_VALUE) == 0)
1372 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
1373 	else
1374 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 3);
1375 	g_signal_connect (G_OBJECT (widget), "changed",
1376 			  G_CALLBACK (gpm_stats_type_combo_changed_cb), NULL);
1377 
1378 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_history_time"));
1379 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_HISTORY_MINUTE_TEXT);
1380 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_HISTORY_HOUR_TEXT);
1381 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_HISTORY_HOURS_TEXT);
1382 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_HISTORY_DAY_TEXT);
1383 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), GPM_HISTORY_WEEK_TEXT);
1384 	gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1);
1385 	if (history_time == GPM_HISTORY_MINUTE_VALUE)
1386 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
1387 	else if (history_time == GPM_HISTORY_HOUR_VALUE)
1388 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 1);
1389 	else
1390 		gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 2);
1391 	g_signal_connect (G_OBJECT (widget), "changed",
1392 			  G_CALLBACK (gpm_stats_range_combo_changed), NULL);
1393 
1394 	client = up_client_new ();
1395 
1396 	devices = up_client_get_devices2 (client);
1397 
1398 	/* add devices in visually pleasing order */
1399 	for (j=0; j<UP_DEVICE_KIND_LAST; j++) {
1400 		for (i=0; i < devices->len; i++) {
1401 			device = g_ptr_array_index (devices, i);
1402 			g_object_get (device, "kind", &kind, NULL);
1403 			if (kind == j)
1404 				/* NULL == do not add it to ptr array */
1405 				gpm_stats_add_device (device, NULL);
1406 		}
1407 	}
1408 
1409 	/* connect now the coldplug is done */
1410 	g_signal_connect (client, "device-added", G_CALLBACK (gpm_stats_device_added_cb), devices);
1411 	g_signal_connect (client, "device-removed", G_CALLBACK (gpm_stats_device_removed_cb), devices);
1412 
1413 	/* set current device */
1414 	if (devices->len > 0) {
1415 		device = g_ptr_array_index (devices, 0);
1416 		gpm_stats_update_info_data (device);
1417 		current_device = g_strdup (up_device_get_object_path (device));
1418 	}
1419 
1420 	if (last_device == NULL)
1421 		last_device = g_settings_get_string (settings, GPM_SETTINGS_INFO_LAST_DEVICE);
1422 
1423 	/* set the correct focus on the last device */
1424 	if (last_device != NULL)
1425 		gpm_stats_highlight_device (last_device);
1426 
1427 	/* set axis */
1428 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_history_type"));
1429 	gpm_stats_history_type_combo_changed_cb (widget, NULL);
1430 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "combobox_stats_type"));
1431 	gpm_stats_type_combo_changed_cb (widget, NULL);
1432 
1433 	widget = GTK_WIDGET (gtk_builder_get_object (builder, "dialog_stats"));
1434 
1435 	status = g_application_run (G_APPLICATION (app), argc, argv);
1436 	if (devices != NULL)
1437 		g_ptr_array_unref (devices);
1438 
1439 	g_object_unref (settings);
1440 	g_object_unref (client);
1441 	g_object_unref (builder);
1442 	g_object_unref (list_store_info);
1443 	g_object_unref (app);
1444 	g_free (last_device);
1445 	return status;
1446 }
1447