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, ¤t_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