[gnome-disk-utility/ata-smart-ui-rework: 17/17] Include a graph in the ATA SMART dialog
- From: David Zeuthen <davidz src gnome org>
- To: svn-commits-list gnome org
- Subject: [gnome-disk-utility/ata-smart-ui-rework: 17/17] Include a graph in the ATA SMART dialog
- Date: Tue, 23 Jun 2009 00:44:44 -0400 (EDT)
commit a578a4127fb882101fa2fe564a7dde6d734cdd22
Author: David Zeuthen <davidz redhat com>
Date: Tue Jun 23 00:39:55 2009 -0400
Include a graph in the ATA SMART dialog
This is still work in progress, see
http://people.freedesktop.org/~david/new-ata-smart-dialog-wip.png
One can observe that I hacked on the DriveSetSpindownTimeout()
yesterday using this disk as a guinea pig ;-)
src/gdu-gtk/Makefile.am | 2 +
src/gdu-gtk/gdu-ata-smart-dialog.c | 445 +++++++++++++++++++++----
src/gdu-gtk/gdu-graph.c | 647 ++++++++++++++++++++++++++++++++++++
src/gdu-gtk/gdu-graph.h | 93 +++++
src/gdu-gtk/gdu-gtk-types.h | 1 +
src/gdu-gtk/gdu-gtk.h | 1 +
src/gdu/gdu-device.c | 2 +-
7 files changed, 1120 insertions(+), 71 deletions(-)
---
diff --git a/src/gdu-gtk/Makefile.am b/src/gdu-gtk/Makefile.am
index 8e1aeaf..b122ac5 100644
--- a/src/gdu-gtk/Makefile.am
+++ b/src/gdu-gtk/Makefile.am
@@ -10,6 +10,7 @@ libgdu_gtkinclude_HEADERS = \
gdu-gtk-types.h \
gdu-time-label.h \
gdu-ata-smart-dialog.h \
+ gdu-graph.h \
gdu-ata-smart-attribute-dialog.h \
$(NULL)
@@ -18,6 +19,7 @@ libgdu_gtk_la_SOURCES = \
gdu-gtk-types.h \
gdu-time-label.h gdu-time-label.c \
gdu-ata-smart-dialog.h gdu-ata-smart-dialog.c \
+ gdu-graph.h gdu-graph.c \
gdu-ata-smart-attribute-dialog.h gdu-ata-smart-attribute-dialog.c \
$(NULL)
diff --git a/src/gdu-gtk/gdu-ata-smart-dialog.c b/src/gdu-gtk/gdu-ata-smart-dialog.c
index 8bcdfc0..e091f79 100644
--- a/src/gdu-gtk/gdu-ata-smart-dialog.c
+++ b/src/gdu-gtk/gdu-ata-smart-dialog.c
@@ -23,6 +23,7 @@
#include <glib/gi18n.h>
#include "gdu-time-label.h"
+#include "gdu-graph.h"
#include "gdu-ata-smart-dialog.h"
#include "gdu-ata-smart-attribute-dialog.h"
@@ -43,6 +44,10 @@ struct GduAtaSmartDialogPrivate
GtkWidget *tree_view;
GtkListStore *attr_list_store;
+
+ GtkWidget *graph;
+
+ GList *historical_data;
};
enum
@@ -75,6 +80,10 @@ G_DEFINE_TYPE (GduAtaSmartDialog, gdu_ata_smart_dialog, GTK_TYPE_DIALOG)
static void update_dialog (GduAtaSmartDialog *dialog);
static void device_changed (GduDevice *device, gpointer user_data);
+static gchar *pretty_to_string (guint64 pretty_value,
+ GduAtaSmartAttributeUnit pretty_unit,
+ gboolean long_string);
+
static void
gdu_ata_smart_dialog_finalize (GObject *object)
{
@@ -84,6 +93,11 @@ gdu_ata_smart_dialog_finalize (GObject *object)
g_object_unref (dialog->priv->device);
g_object_unref (dialog->priv->attr_list_store);
+ if (dialog->priv->historical_data != NULL) {
+ g_list_foreach (dialog->priv->historical_data, (GFunc) g_object_unref, NULL);
+ g_list_free (dialog->priv->historical_data);
+ }
+
if (G_OBJECT_CLASS (gdu_ata_smart_dialog_parent_class)->finalize != NULL)
G_OBJECT_CLASS (gdu_ata_smart_dialog_parent_class)->finalize (object);
}
@@ -125,43 +139,223 @@ gdu_ata_smart_dialog_set_property (GObject *object,
}
static void
-on_bar_clicked (GtkButton *button,
- gpointer user_data)
+selection_changed (GtkTreeSelection *tree_selection,
+ gpointer user_data)
{
GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
- GtkWidget *attr_dialog;
- GtkTreeSelection *tree_selection;
- GtkTreeModel *tree_model;
GtkTreeIter iter;
- gchar *selected_attr_name;
-
- selected_attr_name = NULL;
- tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->tree_view));
- if (gtk_tree_selection_get_selected (tree_selection, &tree_model, &iter)) {
- gtk_tree_model_get (tree_model, &iter,
- ATTR_NAME_COLUMN,
- &selected_attr_name,
- -1);
- }
+ gchar *attr_name;
+ guint n;
+
+ attr_name = NULL;
- if (selected_attr_name == NULL) {
- g_warning ("No attribute selected");
+ if (dialog->priv->historical_data == NULL)
goto out;
+
+ if (!gtk_tree_selection_get_selected (tree_selection,
+ NULL,
+ &iter))
+ goto out;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->attr_list_store),
+ &iter,
+ ATTR_NAME_COLUMN,
+ &attr_name,
+ -1);
+
+ g_debug ("selected %s", attr_name);
+
+ GArray *cur_points;
+ GArray *raw_points;
+ GArray *band_points;
+ GList *l;
+ guint64 now;
+ cur_points = g_array_new (FALSE, FALSE, sizeof (GduGraphPoint));
+ raw_points = g_array_new (FALSE, FALSE, sizeof (GduGraphPoint));
+ band_points = g_array_new (FALSE, FALSE, sizeof (GduGraphPoint));
+
+ guint64 raw_min;
+ guint64 raw_max;
+ GduAtaSmartAttributeUnit raw_unit;
+ raw_min = G_MAXUINT64;
+ raw_max = 0;
+ for (l = dialog->priv->historical_data; l != NULL; l = l->next) {
+ GduAtaSmartHistoricalData *data = GDU_ATA_SMART_HISTORICAL_DATA (l->data);
+ GduAtaSmartAttribute *attr;
+
+ attr = gdu_ata_smart_historical_data_get_attribute (data, attr_name);
+ if (attr != NULL) {
+ guint64 raw;
+
+ raw = gdu_ata_smart_attribute_get_pretty_value (attr);
+ raw_unit = gdu_ata_smart_attribute_get_pretty_unit (attr);
+ if (raw < raw_min)
+ raw_min = raw;
+ if (raw > raw_max)
+ raw_max = raw;
+ g_object_unref (attr);
+ }
+ }
+
+ guint64 time_factor;
+ switch (raw_unit) {
+ case GDU_ATA_SMART_ATTRIBUTE_UNIT_MSECONDS:
+ if (raw_min > 1000 * 60 * 60 * 24) {
+ time_factor = 1000 * 60 * 60 * 24;
+ } else if (raw_min > 1000 * 60 * 60) {
+ time_factor = 1000 * 60 * 60;
+ } else if (raw_min > 1000 * 60) {
+ time_factor = 1000 * 60;
+ } else if (raw_min > 1000) {
+ time_factor = 1000;
+ } else {
+ time_factor = 1;
+ }
+
+ if (raw_max - raw_min < 5 * time_factor) {
+ raw_min -= (raw_min % time_factor);
+ raw_max = raw_min + 5 * time_factor;
+ }
+ break;
+ case GDU_ATA_SMART_ATTRIBUTE_UNIT_MKELVIN:
+ if (raw_max - raw_min < 5000) {
+ raw_min -= (raw_min % 1000);
+ raw_max = raw_min + 5000;
+ }
+ break;
+ case GDU_ATA_SMART_ATTRIBUTE_UNIT_SECTORS:
+ case GDU_ATA_SMART_ATTRIBUTE_UNIT_NONE:
+ case GDU_ATA_SMART_ATTRIBUTE_UNIT_UNKNOWN:
+ if (raw_min - raw_max < 5) {
+ raw_max = raw_min + 5;
+ }
+ break;
+ }
+
+
+ gchar **y_axis_left;
+ y_axis_left = g_new0 (gchar *, 6);
+ for (n = 0; n < 5; n++) {
+ guint64 raw_marker_value;
+ gchar *s;
+
+ raw_marker_value = raw_min + n * ((gdouble) (raw_max - raw_min)) / (5 - 1);
+
+ s = pretty_to_string (raw_marker_value, raw_unit, FALSE);
+ y_axis_left[n] = s;
+ }
+ y_axis_left[n] = NULL;
+ gdu_graph_set_y_markers_left (GDU_GRAPH (dialog->priv->graph), (const gchar* const *) y_axis_left);
+ g_strfreev (y_axis_left);
+
+ guint64 tolerance;
+ guint64 timespan;
+
+ timespan = 5 * 24 * 60 * 60;
+ tolerance = 2 * 60 * 60;
+
+ guint64 last_age;
+ now = (guint64) time (NULL);
+ last_age = timespan;
+
+ /* oldest points first */
+ for (l = dialog->priv->historical_data; l != NULL; l = l->next) {
+ GduAtaSmartHistoricalData *data = GDU_ATA_SMART_HISTORICAL_DATA (l->data);
+ GduAtaSmartAttribute *attr;
+ guint64 time_collected;
+ GduGraphPoint point;
+ guint64 age;
+ gboolean use_point;
+
+ memset (&point, '\0', sizeof (GduGraphPoint));
+
+ time_collected = gdu_ata_smart_historical_data_get_time_collected (data);
+ age = now - time_collected;
+
+ g_debug ("age = %d", (gint) age);
+
+ /* skip old points, except if the following point is not too old */
+ use_point = FALSE;
+ if (age < timespan) {
+ use_point = TRUE;
+ } else {
+ if (l->next != NULL) {
+ GduAtaSmartHistoricalData *next_data = GDU_ATA_SMART_HISTORICAL_DATA (l->next->data);
+ guint64 next_age;
+ next_age = now - gdu_ata_smart_historical_data_get_time_collected (next_data);
+ if (next_age < timespan) {
+ use_point = TRUE;
+ }
+ }
+ }
+
+ if (use_point) {
+
+ point.x = 1.0f - ((gfloat) age) / ((gfloat) timespan);
+
+ attr = gdu_ata_smart_historical_data_get_attribute (data, attr_name);
+ if (attr != NULL) {
+ guint current;
+ guint64 raw;
+
+ current = gdu_ata_smart_attribute_get_current (attr);
+ raw = gdu_ata_smart_attribute_get_pretty_value (attr);
+
+ point.y = current / 255.0f;
+ g_array_append_val (cur_points, point);
+
+ point.y = ((gfloat) (raw - raw_min)) / ((gfloat) (raw_max - raw_min));
+ g_array_append_val (raw_points, point);
+
+ g_object_unref (attr);
+ }
+
+ /* draw a band if there's a discontinuity; e.g. no samples for an hour or more */
+ if (last_age - age >= tolerance) {
+ guint64 band_start;
+ guint64 band_end;
+
+ band_start = last_age - tolerance;
+ band_end = age + tolerance;
+
+ point.x = 1.0f - band_start / ((gfloat) timespan);
+ point.y = 0;
+ g_array_append_val (band_points, point);
+
+ point.x = 1.0f - band_end / ((gfloat) timespan);
+ point.y = 0;
+ g_array_append_val (band_points, point);
+ }
+ }
+
+ last_age = age;
}
- /* Make the attributes dialog transient for the same window as
- * this dialog - we do this so the user can open several attr
- * windows and keep them visible while allowing to close this
- * window (this is useful when monitoring a system)
- */
- attr_dialog = gdu_ata_smart_attribute_dialog_new (GTK_WINDOW (dialog), //NULL,//TODO:gtk_window_get_transient_for (GTK_WINDOW (dialog)),
- dialog->priv->device,
- selected_attr_name);
+ GdkColor cur_color = { 0, 0x8c00, 0xb000, 0xd700};
+ GdkColor raw_color = { 0, 0xfc00, 0xaf00, 0x3e00};
+ GdkColor band_color = { 0, 0x4000, 0x4000, 0x4000};
+
+ gdu_graph_set_curve (GDU_GRAPH (dialog->priv->graph),
+ "current",
+ &cur_color,
+ cur_points);
+
+ gdu_graph_set_curve (GDU_GRAPH (dialog->priv->graph),
+ "raw",
+ &raw_color,
+ raw_points);
- gtk_widget_show_all (attr_dialog);
+ gdu_graph_set_band (GDU_GRAPH (dialog->priv->graph),
+ "discontinuity",
+ &band_color,
+ band_points);
+
+ g_array_unref (cur_points);
+ g_array_unref (raw_points);
+ g_array_unref (band_points);
out:
- ;
+ g_free (attr_name);
}
static void
@@ -171,20 +365,38 @@ gdu_ata_smart_dialog_constructed (GObject *object)
GtkWidget *content_area;
GtkWidget *align;
GtkWidget *vbox;
+ GtkWidget *hbox;
GtkWidget *table;
GtkWidget *label;
GtkWidget *tree_view;
GtkWidget *scrolled_window;
+ GtkWidget *graph;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
gint row;
+ GduPresentable *drive;
+ GduPool *pool;
+ gchar *title;
+ gchar *drive_name;
+ GtkTreeSelection *selection;
gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ pool = gdu_device_get_pool (dialog->priv->device);
+ drive = gdu_pool_get_drive_by_device (pool, dialog->priv->device);
+ drive_name = gdu_presentable_get_name (drive);
+ /* Translators: %s is the drive name */
+ title = g_strdup_printf (_("ATA SMART data for %s"), drive_name);
+ gtk_window_set_title (GTK_WINDOW (dialog), title);
+ g_object_unref (pool);
+ g_object_unref (drive);
+ g_free (title);
+ g_free (drive_name);
+
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
- gtk_dialog_add_button (GTK_DIALOG (dialog),
- GTK_STOCK_CLOSE,
- GTK_RESPONSE_CLOSE);
+ //gtk_dialog_add_button (GTK_DIALOG (dialog),
+ // GTK_STOCK_CLOSE,
+ // GTK_RESPONSE_CLOSE);
align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
gtk_alignment_set_padding (GTK_ALIGNMENT (align), 12, 12, 12, 12);
@@ -193,10 +405,37 @@ gdu_ata_smart_dialog_constructed (GObject *object)
vbox = gtk_vbox_new (FALSE, 6);
gtk_container_add (GTK_CONTAINER (align), vbox);
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
table = gtk_table_new (4, 2, FALSE);
gtk_table_set_col_spacings (GTK_TABLE (table), 12);
gtk_table_set_row_spacings (GTK_TABLE (table), 4);
- gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
+
+ graph = gdu_graph_new ();
+ dialog->priv->graph = graph;
+ gtk_widget_set_size_request (graph, 480, 180);
+ gtk_box_pack_start (GTK_BOX (hbox), graph, TRUE, TRUE, 0);
+
+ const gchar *time_axis[7];
+ time_axis[0] = C_("ATA SMART graph label", "five days ago");
+ time_axis[1] = C_("ATA SMART graph label", "four days ago");
+ time_axis[2] = C_("ATA SMART graph label", "three days ago");
+ time_axis[3] = C_("ATA SMART graph label", "two days ago");
+ time_axis[4] = C_("ATA SMART graph label", "one day ago");
+ time_axis[5] = C_("ATA SMART graph label", "now");
+ time_axis[6] = NULL;
+ gdu_graph_set_x_markers (GDU_GRAPH (graph), time_axis);
+
+ const gchar *y_axis_right[6];
+ y_axis_right[0] = C_("ATA SMART graph label", "0");
+ y_axis_right[1] = C_("ATA SMART graph label", "64");
+ y_axis_right[2] = C_("ATA SMART graph label", "128");
+ y_axis_right[3] = C_("ATA SMART graph label", "192");
+ y_axis_right[4] = C_("ATA SMART graph label", "255");
+ y_axis_right[5] = NULL;
+ gdu_graph_set_y_markers_right (GDU_GRAPH (graph), y_axis_right);
row = 0;
@@ -205,14 +444,14 @@ gdu_ata_smart_dialog_constructed (GObject *object)
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Powered On:</b>"));
gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
dialog->priv->power_on_hours_label = label;
gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
row++;
@@ -221,14 +460,14 @@ gdu_ata_smart_dialog_constructed (GObject *object)
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Temperature:</b>"));
gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
dialog->priv->temperature_label = label;
gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
row++;
@@ -237,14 +476,14 @@ gdu_ata_smart_dialog_constructed (GObject *object)
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Last Test:</b>"));
gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
dialog->priv->last_self_test_result_label = label;
gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
row++;
@@ -253,14 +492,14 @@ gdu_ata_smart_dialog_constructed (GObject *object)
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Updated:</b>"));
gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
label = gdu_time_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
dialog->priv->updated_label = label;
gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
row++;
@@ -269,14 +508,14 @@ gdu_ata_smart_dialog_constructed (GObject *object)
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Sectors:</b>"));
gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
dialog->priv->sectors_label = label;
gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
row++;
@@ -285,14 +524,14 @@ gdu_ata_smart_dialog_constructed (GObject *object)
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Attributes:</b>"));
gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
dialog->priv->attributes_label = label;
gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
row++;
@@ -301,14 +540,14 @@ gdu_ata_smart_dialog_constructed (GObject *object)
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Assessment:</b>"));
gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
dialog->priv->assessment_label = label;
gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
- GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ GTK_FILL, GTK_FILL, 0, 0);
row++;
@@ -338,6 +577,12 @@ gdu_ata_smart_dialog_constructed (GObject *object)
GTK_SORT_ASCENDING);
dialog->priv->tree_view = tree_view;
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+ g_signal_connect (selection,
+ "changed",
+ G_CALLBACK (selection_changed),
+ dialog);
+
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, "ID");
renderer = gtk_cell_renderer_text_new ();
@@ -441,11 +686,6 @@ gdu_ata_smart_dialog_constructed (GObject *object)
gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
- GtkWidget *button;
- button = gtk_button_new_with_mnemonic ("_Bar");
- g_signal_connect (button, "clicked", G_CALLBACK (on_bar_clicked), dialog);
- gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
-
gtk_window_set_default_size (GTK_WINDOW (dialog), 500, 500);
update_dialog (dialog);
@@ -501,7 +741,9 @@ gdu_ata_smart_dialog_new (GtkWindow *parent,
/* ---------------------------------------------------------------------------------------------------- */
static gchar *
-pretty_to_string (guint64 pretty_value, GduAtaSmartAttributeUnit pretty_unit)
+pretty_to_string (guint64 pretty_value,
+ GduAtaSmartAttributeUnit pretty_unit,
+ gboolean long_string)
{
gchar *ret;
gdouble celcius;
@@ -510,30 +752,56 @@ pretty_to_string (guint64 pretty_value, GduAtaSmartAttributeUnit pretty_unit)
switch (pretty_unit) {
case GDU_ATA_SMART_ATTRIBUTE_UNIT_MSECONDS:
- if (pretty_value > 1000 * 60 * 60 * 24) {
- ret = g_strdup_printf (_("%.3g days"), pretty_value / 1000.0 / 60.0 / 60.0 / 24.0);
- } else if (pretty_value > 1000 * 60 * 60) {
- ret = g_strdup_printf (_("%.3g hours"), pretty_value / 1000.0 / 60.0 / 60.0);
- } else if (pretty_value > 1000 * 60) {
- ret = g_strdup_printf (_("%.3g mins"), pretty_value / 1000.0 / 60.0);
- } else if (pretty_value > 1000) {
- ret = g_strdup_printf (_("%.3g secs"), pretty_value / 1000.0);
+ if (long_string) {
+ if (pretty_value > 1000 * 60 * 60 * 24) {
+ ret = g_strdup_printf (_("%.3f days"), pretty_value / 1000.0 / 60.0 / 60.0 / 24.0);
+ } else if (pretty_value > 1000 * 60 * 60) {
+ ret = g_strdup_printf (_("%.3f hours"), pretty_value / 1000.0 / 60.0 / 60.0);
+ } else if (pretty_value > 1000 * 60) {
+ ret = g_strdup_printf (_("%.3f minutes"), pretty_value / 1000.0 / 60.0);
+ } else if (pretty_value > 1000) {
+ ret = g_strdup_printf (_("%.3f seconds"), pretty_value / 1000.0);
+ } else {
+ ret = g_strdup_printf (_("%" G_GUINT64_FORMAT " msec"), pretty_value);
+ }
} else {
- ret = g_strdup_printf (_("%" G_GUINT64_FORMAT " msec"), pretty_value);
+ if (pretty_value > 1000 * 60 * 60 * 24) {
+ ret = g_strdup_printf (_("%.0f d"), pretty_value / 1000.0 / 60.0 / 60.0 / 24.0);
+ } else if (pretty_value > 1000 * 60 * 60) {
+ ret = g_strdup_printf (_("%.0f h"), pretty_value / 1000.0 / 60.0 / 60.0);
+ } else if (pretty_value > 1000 * 60) {
+ ret = g_strdup_printf (_("%.0f m"), pretty_value / 1000.0 / 60.0);
+ } else if (pretty_value > 1000) {
+ ret = g_strdup_printf (_("%.0f s"), pretty_value / 1000.0);
+ } else {
+ ret = g_strdup_printf (_("%" G_GUINT64_FORMAT " msec"), pretty_value);
+ }
}
break;
case GDU_ATA_SMART_ATTRIBUTE_UNIT_SECTORS:
- if (pretty_value == 1)
- ret = g_strdup (_("1 Sector"));
- else
- ret = g_strdup_printf (_("%" G_GUINT64_FORMAT " Sectors"), pretty_value);
+ if (long_string) {
+ if (pretty_value == 1)
+ ret = g_strdup (_("1 Sector"));
+ else
+ ret = g_strdup_printf (_("%" G_GUINT64_FORMAT " Sectors"), pretty_value);
+ } else {
+ ret = g_strdup_printf (_("%" G_GUINT64_FORMAT), pretty_value);
+ }
break;
case GDU_ATA_SMART_ATTRIBUTE_UNIT_MKELVIN:
- celcius = pretty_value / 1000.0 - 273.15;
- fahrenheit = 9.0 * celcius / 5.0 + 32.0;
- ret = g_strdup_printf (_("%.3g\302\260 C / %.3g\302\260 F"), celcius, fahrenheit);
+ if (long_string) {
+ celcius = pretty_value / 1000.0 - 273.15;
+ fahrenheit = 9.0 * celcius / 5.0 + 32.0;
+ ret = g_strdup_printf (_("%.3f\302\260 C / %.3f\302\260 F"), celcius, fahrenheit);
+ } else {
+ /* We could choose kelvin here to treat C and F camps equally. But
+ * that would be lame.
+ */
+ celcius = pretty_value / 1000.0 - 273.15;
+ ret = g_strdup_printf (_("%.0f\302\260 C"), celcius);
+ }
break;
default:
@@ -549,6 +817,33 @@ pretty_to_string (guint64 pretty_value, GduAtaSmartAttributeUnit pretty_unit)
/* ---------------------------------------------------------------------------------------------------- */
static void
+get_historical_data_cb (GduDevice *device,
+ GList *smart_data,
+ GError *error,
+ gpointer user_data)
+{
+ GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
+
+ if (error != NULL) {
+ g_warning ("Error getting historical data: %s", error->message);
+ g_error_free (error);
+ } else {
+ if (dialog->priv->historical_data != NULL) {
+ g_list_foreach (dialog->priv->historical_data, (GFunc) g_object_unref, NULL);
+ g_list_free (dialog->priv->historical_data);
+ }
+ dialog->priv->historical_data = smart_data;
+
+ g_debug ("got historical data (%d elems)", g_list_length (smart_data));
+
+ update_dialog (dialog);
+ }
+
+ g_object_unref (dialog);
+}
+
+
+static void
update_dialog (GduAtaSmartDialog *dialog)
{
gchar *assessment_text;
@@ -627,13 +922,13 @@ update_dialog (GduAtaSmartDialog *dialog)
if (power_on_msec == 0) {
powered_on_text = g_strdup (_("Unknown"));
} else {
- powered_on_text = pretty_to_string (power_on_msec, GDU_ATA_SMART_ATTRIBUTE_UNIT_MSECONDS);
+ powered_on_text = pretty_to_string (power_on_msec, GDU_ATA_SMART_ATTRIBUTE_UNIT_MSECONDS, TRUE);
}
if (temperature_mkelvin == 0) {
temperature_text = g_strdup (_("Unknown"));
} else {
- temperature_text = pretty_to_string (temperature_mkelvin, GDU_ATA_SMART_ATTRIBUTE_UNIT_MKELVIN);
+ temperature_text = pretty_to_string (temperature_mkelvin, GDU_ATA_SMART_ATTRIBUTE_UNIT_MKELVIN, TRUE);
}
dialog->priv->last_updated = updated.tv_sec = gdu_device_drive_ata_smart_get_time_collected (dialog->priv->device);
@@ -757,7 +1052,7 @@ update_dialog (GduAtaSmartDialog *dialog)
pretty_value = gdu_ata_smart_attribute_get_pretty_value (a);
pretty_unit = gdu_ata_smart_attribute_get_pretty_unit (a);
- pretty_str = pretty_to_string (pretty_value, pretty_unit);
+ pretty_str = pretty_to_string (pretty_value, pretty_unit, TRUE);
is_good = gdu_ata_smart_attribute_get_good (a);
is_good_valid = gdu_ata_smart_attribute_get_good_valid (a);
@@ -823,6 +1118,16 @@ update_dialog (GduAtaSmartDialog *dialog)
g_free (powered_on_text);
g_free (temperature_text);
g_free (selftest_text);
+
+ /* TODO: also fetch new data if current data is out of date */
+ if (dialog->priv->historical_data == NULL) {
+ gdu_device_drive_ata_smart_get_historical_data (dialog->priv->device,
+ 0, /* since */
+ 0, /* until */
+ 0, /* spacing */
+ get_historical_data_cb,
+ g_object_ref (dialog));
+ }
}
static void
diff --git a/src/gdu-gtk/gdu-graph.c b/src/gdu-gtk/gdu-graph.c
new file mode 100644
index 0000000..f0b0cf9
--- /dev/null
+++ b/src/gdu-gtk/gdu-graph.c
@@ -0,0 +1,647 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-graph.c
+ *
+ * Copyright (C) 2009 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <math.h>
+
+#include "gdu-graph.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ gchar *id;
+ GdkColor *color;
+ GArray *points;
+} Curve;
+
+static Curve *
+curve_new (const gchar *curve_id,
+ GdkColor *color,
+ GArray *points)
+{
+ Curve *c;
+
+ c = g_new0 (Curve, 1);
+ c->id = g_strdup (curve_id);
+ c->color = gdk_color_copy (color);
+ c->points = g_array_ref (points);
+
+ return c;
+}
+
+static void
+curve_free (Curve *c)
+{
+ g_free (c->id);
+ gdk_color_free (c->color);
+ g_array_unref (c->points);
+ g_free (c);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+struct GduGraphPrivate
+{
+ guint foo;
+
+ gchar **x_markers;
+ gchar **y_markers_left;
+ gchar **y_markers_right;
+
+ GPtrArray *curves;
+ GPtrArray *bands;
+};
+
+G_DEFINE_TYPE (GduGraph, gdu_graph, GTK_TYPE_DRAWING_AREA)
+
+static gboolean gdu_graph_expose_event (GtkWidget *widget,
+ GdkEventExpose *event);
+
+enum
+{
+ PROP_0,
+ PROP_X_MARKERS,
+ PROP_Y_MARKERS_LEFT,
+ PROP_Y_MARKERS_RIGHT,
+};
+
+static void
+gdu_graph_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GduGraph *graph = GDU_GRAPH (object);
+
+ switch (prop_id) {
+ case PROP_X_MARKERS:
+ gdu_graph_set_x_markers (graph, g_value_get_boxed (value));
+ break;
+
+ case PROP_Y_MARKERS_LEFT:
+ gdu_graph_set_y_markers_left (graph, g_value_get_boxed (value));
+ break;
+
+ case PROP_Y_MARKERS_RIGHT:
+ gdu_graph_set_y_markers_right (graph, g_value_get_boxed (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdu_graph_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GduGraph *graph = GDU_GRAPH (object);
+
+ switch (prop_id) {
+ case PROP_X_MARKERS:
+ g_value_set_boxed (value, graph->priv->x_markers);
+ break;
+
+ case PROP_Y_MARKERS_LEFT:
+ g_value_set_boxed (value, graph->priv->y_markers_left);
+ break;
+
+ case PROP_Y_MARKERS_RIGHT:
+ g_value_set_boxed (value, graph->priv->y_markers_right);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdu_graph_finalize (GObject *object)
+{
+ GduGraph *graph = GDU_GRAPH (object);
+
+ g_strfreev (graph->priv->x_markers);
+ g_strfreev (graph->priv->y_markers_left);
+ g_strfreev (graph->priv->y_markers_right);
+
+ g_ptr_array_unref (graph->priv->curves);
+ g_ptr_array_unref (graph->priv->bands);
+
+ if (G_OBJECT_CLASS (gdu_graph_parent_class)->finalize != NULL)
+ G_OBJECT_CLASS (gdu_graph_parent_class)->finalize (object);
+}
+
+static void
+gdu_graph_class_init (GduGraphClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gobject_class->finalize = gdu_graph_finalize;
+ gobject_class->set_property = gdu_graph_set_property;
+ gobject_class->get_property = gdu_graph_get_property;
+
+ widget_class->expose_event = gdu_graph_expose_event;
+
+ g_type_class_add_private (klass, sizeof (GduGraphPrivate));
+
+ g_object_class_install_property (gobject_class,
+ PROP_X_MARKERS,
+ g_param_spec_boxed ("x-markers",
+ _("X Markers"),
+ _("Markers to print on the X axis"),
+ G_TYPE_STRV,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (gobject_class,
+ PROP_Y_MARKERS_LEFT,
+ g_param_spec_boxed ("y-markers-left",
+ _("Left Y Markers"),
+ _("Markers to print on the left Y axis"),
+ G_TYPE_STRV,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (gobject_class,
+ PROP_Y_MARKERS_RIGHT,
+ g_param_spec_boxed ("y-markers-right",
+ _("Right Y Markers"),
+ _("Markers to print on the right Y axis"),
+ G_TYPE_STRV,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gdu_graph_init (GduGraph *graph)
+{
+ graph->priv = G_TYPE_INSTANCE_GET_PRIVATE (graph, GDU_TYPE_GRAPH, GduGraphPrivate);
+ graph->priv->curves = g_ptr_array_new_with_free_func ((GDestroyNotify) curve_free);
+ graph->priv->bands = g_ptr_array_new_with_free_func ((GDestroyNotify) curve_free);
+}
+
+GtkWidget *
+gdu_graph_new (void)
+{
+ return GTK_WIDGET (g_object_new (GDU_TYPE_GRAPH, NULL));
+}
+
+gchar **
+gdu_graph_get_x_markers (GduGraph *graph)
+{
+ g_return_val_if_fail (GDU_IS_GRAPH (graph), NULL);
+ return g_strdupv (graph->priv->x_markers);
+}
+
+gchar **
+gdu_graph_get_y_markers_left (GduGraph *graph)
+{
+ g_return_val_if_fail (GDU_IS_GRAPH (graph), NULL);
+ return g_strdupv (graph->priv->y_markers_left);
+}
+
+gchar **
+gdu_graph_get_y_markers_right (GduGraph *graph)
+{
+ g_return_val_if_fail (GDU_IS_GRAPH (graph), NULL);
+ return g_strdupv (graph->priv->y_markers_right);
+}
+
+void
+gdu_graph_set_x_markers (GduGraph *graph,
+ const gchar* const *markers)
+{
+ g_return_if_fail (GDU_IS_GRAPH (graph));
+ g_strfreev (graph->priv->x_markers);
+ graph->priv->x_markers = g_strdupv ((gchar **) markers);
+ if (GTK_WIDGET (graph)->window != NULL)
+ gdk_window_invalidate_rect (GTK_WIDGET (graph)->window, NULL, TRUE);
+}
+
+void
+gdu_graph_set_y_markers_left (GduGraph *graph,
+ const gchar* const *markers)
+{
+ g_return_if_fail (GDU_IS_GRAPH (graph));
+ g_strfreev (graph->priv->y_markers_left);
+ graph->priv->y_markers_left = g_strdupv ((gchar **) markers);
+ if (GTK_WIDGET (graph)->window != NULL)
+ gdk_window_invalidate_rect (GTK_WIDGET (graph)->window, NULL, TRUE);
+}
+
+void
+gdu_graph_set_y_markers_right (GduGraph *graph,
+ const gchar* const *markers)
+{
+ g_return_if_fail (GDU_IS_GRAPH (graph));
+ g_strfreev (graph->priv->y_markers_right);
+ graph->priv->y_markers_right = g_strdupv ((gchar **) markers);
+ if (GTK_WIDGET (graph)->window != NULL)
+ gdk_window_invalidate_rect (GTK_WIDGET (graph)->window, NULL, TRUE);
+}
+
+static gdouble
+measure_width (cairo_t *cr,
+ const gchar *s)
+{
+ cairo_text_extents_t te;
+ cairo_select_font_face (cr,
+ "sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 8.0);
+ cairo_text_extents (cr, s, &te);
+ return te.width;
+}
+
+static gboolean
+gdu_graph_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GduGraph *graph = GDU_GRAPH (widget);
+ guint n_x_markers;
+ guint n_y_markers_left;
+ guint n_y_markers_right;
+ cairo_t *cr;
+ gdouble width, height;
+ guint n;
+
+ n_x_markers = graph->priv->x_markers != NULL ? g_strv_length (graph->priv->x_markers) : 0;
+ n_y_markers_left = graph->priv->y_markers_left != NULL ? g_strv_length (graph->priv->y_markers_left) : 0;
+ n_y_markers_right = graph->priv->y_markers_right != NULL ? g_strv_length (graph->priv->y_markers_right) : 0;
+
+ width = widget->allocation.width;
+ height = widget->allocation.height;
+
+ cr = gdk_cairo_create (widget->window);
+ cairo_rectangle (cr,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ cairo_clip (cr);
+
+ double gx, gy, gw, gh;
+ gx = 0;
+ gy = 10;
+ gw = width - 10;
+ gh = height - gy - 30;
+
+ guint twidth;
+
+ /* measure text on the left y-axis */
+ guint y_left_max_width;
+ y_left_max_width = 0;
+ for (n = 0; n < n_y_markers_left; n++) {
+ twidth = ceil (measure_width (cr, graph->priv->y_markers_left[n]));
+ if (twidth > y_left_max_width)
+ y_left_max_width = twidth;
+ }
+ /* include half width of first xmarker label */
+ if (n_x_markers > 0) {
+ twidth = ceil (measure_width (cr, graph->priv->x_markers[0]));
+ if (twidth/2 > y_left_max_width)
+ y_left_max_width = twidth/2;
+ }
+ y_left_max_width += 6; /* padding */
+ gx += y_left_max_width;
+ gw -= y_left_max_width;
+
+ /* measure text on the right y-axis */
+ guint y_right_max_width;
+ y_right_max_width = 0;
+ for (n = 0; n < n_y_markers_right; n++) {
+ twidth = ceil (measure_width (cr, graph->priv->y_markers_right[n]));
+ if (twidth/2 > y_right_max_width)
+ y_right_max_width = twidth/2;
+ }
+ /* include half width of last xmarker label */
+ if (n_x_markers > 0) {
+ twidth = ceil (measure_width (cr, graph->priv->x_markers[n_x_markers - 1]));
+ y_right_max_width += twidth/2;
+ }
+ y_right_max_width += 6; /* padding */
+ gw -= y_right_max_width;
+
+ /* draw the box to draw in */
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, gx, gy, gw, gh);
+ cairo_set_line_width (cr, 0.0);
+ cairo_fill (cr);
+
+ /* draw markers on the left y-axis */
+ for (n = 0; n < n_y_markers_left; n++) {
+ double pos;
+
+ pos = ceil (gy + gh / (n_y_markers_left - 1) * n);
+
+ const char *s;
+ s = graph->priv->y_markers_left[n_y_markers_left - 1 - n];
+
+ cairo_text_extents_t te;
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_select_font_face (cr, "sans",
+ CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 8.0);
+ cairo_text_extents (cr, s, &te);
+ cairo_move_to (cr,
+ gx/2.0 - 3 - te.width/2 - te.x_bearing,
+ pos - te.height/2 - te.y_bearing);
+
+ cairo_show_text (cr, s);
+
+ cairo_set_line_width (cr, 1.0);
+ double dashes[1] = {2.0};
+ cairo_set_dash (cr, dashes, 1, 0.0);
+ cairo_move_to (cr,
+ gx - 0.5,
+ pos - 0.5);
+ cairo_line_to (cr,
+ gx - 0.5 + gw,
+ pos - 0.5);
+ cairo_stroke (cr);
+ }
+
+ /* draw markers on the right y-axis */
+ for (n = 0; n < n_y_markers_right; n++) {
+ double pos;
+
+ pos = ceil (gy + gh / (n_y_markers_right - 1) * n);
+
+ const char *s;
+ s = graph->priv->y_markers_right[n_y_markers_right - 1 - n];
+
+ cairo_text_extents_t te;
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_select_font_face (cr, "sans",
+ CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 8.0);
+ cairo_text_extents (cr, s, &te);
+ cairo_move_to (cr,
+ gx + gw + y_right_max_width/2.0 + 3 - te.width/2 - te.x_bearing,
+ pos - te.height/2 - te.y_bearing);
+
+ cairo_show_text (cr, s);
+ }
+
+ guint64 t_left;
+ guint64 t_right;
+ GTimeVal now;
+
+ g_get_current_time (&now);
+ /*
+ switch (0) {
+ default:
+ case 0:
+ t_left = now.tv_sec - 6 * 60 * 60;
+ break;
+ case 1:
+ t_left = now.tv_sec - 24 * 60 * 60;
+ break;
+ case 2:
+ t_left = now.tv_sec - 3 * 24 * 60 * 60;
+ break;
+ case 3:
+ t_left = now.tv_sec - 12 * 24 * 60 * 60;
+ break;
+ case 4:
+ t_left = now.tv_sec - 36 * 24 * 60 * 60;
+ break;
+ case 5:
+ t_left = now.tv_sec - 96 * 24 * 60 * 60;
+ break;
+ }
+ t_right = now.tv_sec;
+ */
+ t_left = now.tv_sec - 6 * 24 * 60 * 60;
+ t_right = now.tv_sec;
+
+ /* draw time markers on x-axis */
+ for (n = 0; n < n_x_markers; n++) {
+ double pos;
+ guint64 val;
+ const gchar *s;
+ cairo_text_extents_t te;
+ double dashes[1] = {2.0};
+
+ s = graph->priv->x_markers[n];
+
+ pos = ceil (gx + gw / (n_x_markers - 1) * n);
+ val = t_left + (t_right - t_left) * n / (n_x_markers - 1);
+
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_select_font_face (cr, "sans",
+ CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 8.0);
+ cairo_text_extents (cr, s, &te);
+ cairo_move_to (cr,
+ pos - te.width/2 - te.x_bearing,
+ height - 30.0/2 - te.height/2 - te.y_bearing); /* TODO */
+
+ cairo_show_text (cr, s);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_dash (cr, dashes, 1, 0.0);
+ cairo_move_to (cr,
+ pos - 0.5,
+ gy - 0.5);
+ cairo_line_to (cr,
+ pos - 0.5,
+ gy - 0.5 + gh);
+ cairo_stroke (cr);
+ }
+
+ /* clip to the graph area */
+ cairo_rectangle (cr, gx, gy, gw, gh);
+ cairo_clip (cr);
+
+ /* draw all bands */
+ for (n = 0; n < graph->priv->bands->len; n++) {
+ Curve *c = (Curve *) graph->priv->bands->pdata[n];
+ guint m;
+
+ cairo_new_path (cr);
+ cairo_set_line_width (cr, 0.0);
+ cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.5);
+
+ for (m = 0; m < c->points->len; m+= 2) {
+ GduGraphPoint *point;
+ gdouble x0, x1;
+ gdouble x, width;
+ cairo_pattern_t *pat;
+
+ point = &g_array_index (c->points, GduGraphPoint, m);
+ x0 = gx + gw * point->x;
+
+ point = &g_array_index (c->points, GduGraphPoint, m + 1);
+ x1 = gx + gw * point->x;
+
+ x = x0;
+ if (x1 < x0)
+ x = x1;
+ width = fabs (x1 - x0);
+
+ g_debug ("band: %f to %f", x0, x1);
+
+ pat = cairo_pattern_create_linear (x, gy,
+ x + width, gy);
+ cairo_pattern_add_color_stop_rgba (pat, 0.00, 1.00, 1.00, 1.00, 0.00);
+ cairo_pattern_add_color_stop_rgba (pat, 0.35, 0.85, 0.85, 0.85, 0.50);
+ cairo_pattern_add_color_stop_rgba (pat, 0.65, 0.85, 0.85, 0.85, 0.50);
+ cairo_pattern_add_color_stop_rgba (pat, 1.00, 1.00, 1.00, 1.00, 0.00);
+ cairo_set_source (cr, pat);
+ cairo_pattern_destroy (pat);
+
+ cairo_rectangle (cr, x, gy, width, gh);
+ cairo_fill (cr);
+ }
+ }
+
+ /* draw all curves */
+ for (n = 0; n < graph->priv->curves->len; n++) {
+ Curve *c = (Curve *) graph->priv->curves->pdata[n];
+ guint m;
+
+ cairo_new_path (cr);
+ cairo_set_dash (cr, NULL, 0, 0.0);
+ cairo_set_line_width (cr, 1.0);
+ gdk_cairo_set_source_color (cr, c->color);
+
+ for (m = 0; m < c->points->len; m++) {
+ GduGraphPoint *point;
+ gdouble x, y;
+
+ point = &g_array_index (c->points, GduGraphPoint, m);
+
+ x = gx + gw * point->x;
+ y = gy + gh * (1.0f - point->y);
+
+ if (y < gy + 1.0)
+ y = gy;
+
+ if (y > gy + gh - 1.0)
+ y = gy + gh - 1.0;
+
+ cairo_line_to (cr, x, y);
+ }
+ cairo_stroke (cr);
+ }
+
+ /* propagate event further */
+ return FALSE;
+}
+
+gboolean
+gdu_graph_remove_curve (GduGraph *graph,
+ const gchar *curve_id)
+{
+ guint n;
+ gboolean found;
+
+ found = FALSE;
+ for (n = 0; n < graph->priv->curves->len; n++) {
+ Curve *c = (Curve *) graph->priv->curves->pdata[n];
+ if (g_strcmp0 (curve_id, c->id) == 0) {
+ g_ptr_array_remove_index (graph->priv->curves, n);
+ found = TRUE;
+ break;
+ }
+ }
+
+ return found;
+}
+
+void
+gdu_graph_set_curve (GduGraph *graph,
+ const gchar *curve_id,
+ GdkColor *color,
+ GArray *points)
+{
+ g_return_if_fail (GDU_IS_GRAPH (graph));
+ g_return_if_fail (curve_id != NULL);
+ g_return_if_fail (color != NULL);
+
+ if (points == NULL) {
+ gdu_graph_remove_curve (graph, curve_id);
+ } else {
+ gdu_graph_remove_curve (graph, curve_id);
+ g_ptr_array_add (graph->priv->curves,
+ curve_new (curve_id,
+ color,
+ points));
+ }
+
+ if (GTK_WIDGET (graph)->window != NULL)
+ gdk_window_invalidate_rect (GTK_WIDGET (graph)->window, NULL, TRUE);
+}
+
+gboolean
+gdu_graph_remove_band (GduGraph *graph,
+ const gchar *band_id)
+{
+ guint n;
+ gboolean found;
+
+ found = FALSE;
+ for (n = 0; n < graph->priv->bands->len; n++) {
+ Curve *c = (Curve *) graph->priv->bands->pdata[n];
+ if (g_strcmp0 (band_id, c->id) == 0) {
+ g_ptr_array_remove_index (graph->priv->bands, n);
+ found = TRUE;
+ break;
+ }
+ }
+
+ return found;
+}
+
+void
+gdu_graph_set_band (GduGraph *graph,
+ const gchar *band_id,
+ GdkColor *color,
+ GArray *points)
+{
+ g_return_if_fail (GDU_IS_GRAPH (graph));
+ g_return_if_fail (band_id != NULL);
+ g_return_if_fail (color != NULL);
+
+ if (points == NULL) {
+ gdu_graph_remove_band (graph, band_id);
+ } else {
+ gdu_graph_remove_band (graph, band_id);
+ g_ptr_array_add (graph->priv->bands,
+ curve_new (band_id,
+ color,
+ points));
+ }
+
+ if (GTK_WIDGET (graph)->window != NULL)
+ gdk_window_invalidate_rect (GTK_WIDGET (graph)->window, NULL, TRUE);
+}
diff --git a/src/gdu-gtk/gdu-graph.h b/src/gdu-gtk/gdu-graph.h
new file mode 100644
index 0000000..92fe0f0
--- /dev/null
+++ b/src/gdu-gtk/gdu-graph.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-graph.h
+ *
+ * Copyright (C) 2007 David Zeuthen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#if !defined (__GDU_GTK_INSIDE_GDU_GTK_H) && !defined (GDU_GTK_COMPILATION)
+#error "Only <gdu-gtk/gdu-gtk.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef GDU_GRAPH_H
+#define GDU_GRAPH_H
+
+#include <gdu-gtk/gdu-gtk-types.h>
+
+#define GDU_TYPE_GRAPH (gdu_graph_get_type ())
+#define GDU_GRAPH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDU_TYPE_GRAPH, GduGraph))
+#define GDU_GRAPH_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), GDU_GRAPH, GduGraphClass))
+#define GDU_IS_GRAPH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDU_TYPE_GRAPH))
+#define GDU_IS_GRAPH_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), GDU_TYPE_GRAPH))
+#define GDU_GRAPH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDU_TYPE_GRAPH, GduGraphClass))
+
+typedef struct GduGraphClass GduGraphClass;
+typedef struct GduGraphPrivate GduGraphPrivate;
+
+struct GduGraph
+{
+ GtkDrawingArea parent;
+
+ /* private */
+ GduGraphPrivate *priv;
+};
+
+struct GduGraphClass
+{
+ GtkDrawingAreaClass parent_class;
+};
+
+
+GType gdu_graph_get_type (void);
+GtkWidget *gdu_graph_new (void);
+gchar **gdu_graph_get_x_markers (GduGraph *graph);
+gchar **gdu_graph_get_y_markers_left (GduGraph *graph);
+gchar **gdu_graph_get_y_markers_right (GduGraph *graph);
+void gdu_graph_set_x_markers (GduGraph *graph,
+ const gchar* const *markers);
+void gdu_graph_set_y_markers_left (GduGraph *graph,
+ const gchar* const *markers);
+void gdu_graph_set_y_markers_right (GduGraph *graph,
+ const gchar* const *markers);
+
+typedef struct GduGraphPoint GduGraphPoint;
+
+struct GduGraphPoint
+{
+ gfloat x;
+ gfloat y;
+ gpointer data;
+};
+
+gboolean gdu_graph_remove_curve (GduGraph *graph,
+ const gchar *curve_id);
+
+void gdu_graph_set_curve (GduGraph *graph,
+ const gchar *curve_id,
+ GdkColor *color,
+ GArray *points);
+
+gboolean gdu_graph_remove_band (GduGraph *graph,
+ const gchar *band_id);
+
+void gdu_graph_set_band (GduGraph *graph,
+ const gchar *band_id,
+ GdkColor *color,
+ GArray *points);
+
+
+#endif /* GDU_GRAPH_H */
diff --git a/src/gdu-gtk/gdu-gtk-types.h b/src/gdu-gtk/gdu-gtk-types.h
index 658d312..efab698 100644
--- a/src/gdu-gtk/gdu-gtk-types.h
+++ b/src/gdu-gtk/gdu-gtk-types.h
@@ -32,6 +32,7 @@
G_BEGIN_DECLS
+typedef struct GduGraph GduGraph;
typedef struct GduTimeLabel GduTimeLabel;
typedef struct GduAtaSmartDialog GduAtaSmartDialog;
typedef struct GduAtaSmartAttributeDialog GduAtaSmartAttributeDialog;
diff --git a/src/gdu-gtk/gdu-gtk.h b/src/gdu-gtk/gdu-gtk.h
index bab7ad3..b530209 100644
--- a/src/gdu-gtk/gdu-gtk.h
+++ b/src/gdu-gtk/gdu-gtk.h
@@ -28,6 +28,7 @@
#define __GDU_GTK_INSIDE_GDU_GTK_H
#include <gdu-gtk/gdu-gtk-types.h>
+#include <gdu-gtk/gdu-graph.h>
#include <gdu-gtk/gdu-time-label.h>
#include <gdu-gtk/gdu-ata-smart-dialog.h>
#include <gdu-gtk/gdu-ata-smart-attribute-dialog.h>
diff --git a/src/gdu/gdu-device.c b/src/gdu/gdu-device.c
index 82eeec0..b1794b9 100644
--- a/src/gdu/gdu-device.c
+++ b/src/gdu/gdu-device.c
@@ -2406,7 +2406,7 @@ op_ata_smart_historical_data_cb (DBusGProxy *proxy, GPtrArray *historical_data,
if (historical_data != NULL && error == NULL)
ret = op_ata_smart_historical_data_compute_ret (historical_data);
- if (data->callback == NULL)
+ if (data->callback != NULL)
data->callback (data->device, ret, error, data->user_data);
g_object_unref (data->device);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]