[gnome-disk-utility/ata-smart-ui-rework] Iterate a bit more over the ATA SMART graphs



commit 5b128f0393941ff7ddd0220a4f4a5c808232f4b3
Author: David Zeuthen <davidz redhat com>
Date:   Wed Jun 24 18:06:08 2009 -0400

    Iterate a bit more over the ATA SMART graphs
    
    ZOMG, we have legend text
    
    http://people.freedesktop.org/~david/ata-smart-rework-wip3.png

 src/gdu-gtk/Makefile.am                      |   28 ++-
 src/gdu-gtk/gdu-ata-smart-attribute-dialog.c |  243 -----------
 src/gdu-gtk/gdu-ata-smart-attribute-dialog.h |   63 ---
 src/gdu-gtk/gdu-ata-smart-dialog.c           |  143 +++++--
 src/gdu-gtk/gdu-curve.c                      |  463 +++++++++++++++++++++
 src/gdu-gtk/gdu-curve.h                      |  107 +++++
 src/gdu-gtk/gdu-graph.c                      |  572 +++++++++++++++-----------
 src/gdu-gtk/gdu-graph.h                      |   26 +-
 src/gdu-gtk/gdu-gtk-enums.h                  |   38 ++
 src/gdu-gtk/gdu-gtk-enumtypes.c.template     |   39 ++
 src/gdu-gtk/gdu-gtk-enumtypes.h.template     |   24 ++
 src/gdu-gtk/gdu-gtk-types.h                  |    5 +-
 src/gdu-gtk/gdu-gtk.h                        |    3 +-
 13 files changed, 1160 insertions(+), 594 deletions(-)
---
diff --git a/src/gdu-gtk/Makefile.am b/src/gdu-gtk/Makefile.am
index b122ac5..d2a018c 100644
--- a/src/gdu-gtk/Makefile.am
+++ b/src/gdu-gtk/Makefile.am
@@ -1,26 +1,40 @@
 
 NULL =
 
+gdu-gtk-enumtypes.h: gdu-gtk-enums.h gdu-gtk-enumtypes.h.template
+	( top_builddir=`cd $(top_builddir) && pwd`; \
+	  cd $(srcdir) && glib-mkenums --template gdu-gtk-enumtypes.h.template gdu-gtk-enums.h ) > \
+	    gdu-gtk-enumtypes.h.tmp && mv gdu-gtk-enumtypes.h.tmp gdu-gtk-enumtypes.h
+
+gdu-gtk-enumtypes.c: gdu-gtk-enums.h gdu-gtk-enumtypes.c.template
+	( top_builddir=`cd $(top_builddir) && pwd`; \
+	  cd $(srcdir) && glib-mkenums --template gdu-gtk-enumtypes.c.template gdu-gtk-enums.h ) > \
+	    gdu-gtk-enumtypes.c.tmp && mv gdu-gtk-enumtypes.c.tmp gdu-gtk-enumtypes.c
+
 lib_LTLIBRARIES=libgdu-gtk.la
 
 libgdu_gtkincludedir=$(includedir)/gnome-disk-utility/gdu-gtk
 
 libgdu_gtkinclude_HEADERS =              						\
 	gdu-gtk.h									\
+	gdu-gtk-enums.h									\
+	gdu-gtk-enumtypes.h								\
 	gdu-gtk-types.h									\
 	gdu-time-label.h								\
 	gdu-ata-smart-dialog.h								\
+	gdu-curve.h									\
 	gdu-graph.h									\
-	gdu-ata-smart-attribute-dialog.h						\
 	$(NULL)
 
 libgdu_gtk_la_SOURCES =                 	        	       			\
 	gdu-gtk.h				gdu-gtk.c				\
 	gdu-gtk-types.h									\
+	gdu-gtk-enums.h									\
+	gdu-gtk-enumtypes.h			gdu-gtk-enumtypes.c			\
 	gdu-time-label.h			gdu-time-label.c			\
 	gdu-ata-smart-dialog.h			gdu-ata-smart-dialog.c			\
+	gdu-curve.h				gdu-curve.c				\
 	gdu-graph.h				gdu-graph.c				\
-	gdu-ata-smart-attribute-dialog.h	gdu-ata-smart-attribute-dialog.c	\
 	$(NULL)
 
 libgdu_gtk_la_CPPFLAGS = 				\
@@ -60,8 +74,18 @@ libgdu_gtk_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = gdu-gtk.pc
 
+BUILT_SOURCES =			\
+	gdu-gtk-enumtypes.c	\
+	gdu-gtk-enumtypes.h	\
+	$(NULL)
+
 CLEANFILES = $(BUILT_SOURCES) $(pkgconfig_DATA)
 
+EXTRADIST =				\
+	gdu-gtk-enumtypes.h.template	\
+	gdu-gtk-enumtypes.c.template	\
+	$(NULL)
+
 clean-local :
 	rm -f *~ $(BUILT_SOURCES)
 
diff --git a/src/gdu-gtk/gdu-ata-smart-dialog.c b/src/gdu-gtk/gdu-ata-smart-dialog.c
index e091f79..50339cb 100644
--- a/src/gdu-gtk/gdu-ata-smart-dialog.c
+++ b/src/gdu-gtk/gdu-ata-smart-dialog.c
@@ -23,9 +23,9 @@
 #include <glib/gi18n.h>
 
 #include "gdu-time-label.h"
+#include "gdu-curve.h"
 #include "gdu-graph.h"
 #include "gdu-ata-smart-dialog.h"
-#include "gdu-ata-smart-attribute-dialog.h"
 
 struct GduAtaSmartDialogPrivate
 {
@@ -166,13 +166,17 @@ selection_changed (GtkTreeSelection *tree_selection,
         g_debug ("selected %s", attr_name);
 
         GArray *cur_points;
+        GArray *worst_points;
+        GArray *threshold_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));
+        cur_points = g_array_new (FALSE, FALSE, sizeof (GduPoint));
+        worst_points = g_array_new (FALSE, FALSE, sizeof (GduPoint));
+        threshold_points = g_array_new (FALSE, FALSE, sizeof (GduPoint));
+        raw_points = g_array_new (FALSE, FALSE, sizeof (GduPoint));
+        band_points = g_array_new (FALSE, FALSE, sizeof (GduPoint));
 
         guint64 raw_min;
         guint64 raw_max;
@@ -263,17 +267,15 @@ selection_changed (GtkTreeSelection *tree_selection,
                 GduAtaSmartHistoricalData *data = GDU_ATA_SMART_HISTORICAL_DATA (l->data);
                 GduAtaSmartAttribute *attr;
                 guint64 time_collected;
-                GduGraphPoint point;
+                GduPoint point;
                 guint64 age;
                 gboolean use_point;
 
-                memset (&point, '\0', sizeof (GduGraphPoint));
+                memset (&point, '\0', sizeof (GduPoint));
 
                 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) {
@@ -291,20 +293,30 @@ selection_changed (GtkTreeSelection *tree_selection,
 
                 if (use_point) {
 
-                        point.x = 1.0f - ((gfloat) age) / ((gfloat) timespan);
+                        point.x = 1.0 - ((gdouble) age) / ((gdouble) timespan);
 
                         attr = gdu_ata_smart_historical_data_get_attribute (data, attr_name);
                         if (attr != NULL) {
                                 guint current;
+                                guint worst;
+                                guint threshold;
                                 guint64 raw;
 
                                 current = gdu_ata_smart_attribute_get_current (attr);
+                                worst = gdu_ata_smart_attribute_get_worst (attr);
+                                threshold = gdu_ata_smart_attribute_get_threshold (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));
+                                point.y = worst / 255.0f;
+                                g_array_append_val (worst_points, point);
+
+                                point.y = threshold / 255.0f;
+                                g_array_append_val (threshold_points, point);
+
+                                point.y = ((gdouble) (raw - raw_min)) / ((gdouble) (raw_max - raw_min));
                                 g_array_append_val (raw_points, point);
 
                                 g_object_unref (attr);
@@ -318,12 +330,17 @@ selection_changed (GtkTreeSelection *tree_selection,
                                 band_start = last_age - tolerance;
                                 band_end = age + tolerance;
 
-                                point.x = 1.0f - band_start / ((gfloat) timespan);
-                                point.y = 0;
+                                point.x = 1.0 - band_start / ((gdouble) timespan);
+                                point.y = 1.0;
+                                g_array_append_val (band_points, point);
+
+                                point.x = 1.0 - band_end / ((gdouble) timespan);
+                                point.y = 1.0;
                                 g_array_append_val (band_points, point);
 
-                                point.x = 1.0f - band_end / ((gfloat) timespan);
-                                point.y = 0;
+                                /* close the segment */
+                                point.x = G_MAXDOUBLE;
+                                point.y = G_MAXDOUBLE;
                                 g_array_append_val (band_points, point);
                         }
                 }
@@ -331,26 +348,92 @@ selection_changed (GtkTreeSelection *tree_selection,
                 last_age = age;
         }
 
-        GdkColor cur_color = { 0, 0x8c00, 0xb000, 0xd700};
-        GdkColor raw_color = { 0, 0xfc00, 0xaf00, 0x3e00};
-        GdkColor band_color = { 0, 0x4000, 0x4000, 0x4000};
+#define GDU_COLOR_FROM_HEX(argb_hex, alpha)             \
+        {                                               \
+                (((argb_hex) >> 16)&0xff) / 255.0,      \
+                (((argb_hex) >>  8)&0xff) / 255.0,      \
+                (((argb_hex) >>  0)&0xff) / 255.0,      \
+                alpha                                   \
+        }
 
-        gdu_graph_set_curve (GDU_GRAPH (dialog->priv->graph),
+        /* see http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines for colors
+         */
+        GduColor raw_color       = GDU_COLOR_FROM_HEX (0xfcaf3e, 255.0); /* orange */
+        GduColor cur_color       = GDU_COLOR_FROM_HEX (0x729fcf, 255.0); /* sky blue */
+        GduColor worst_color     = GDU_COLOR_FROM_HEX (0xad7fa8, 255.0); /* plum */
+        GduColor threshold_color = GDU_COLOR_FROM_HEX (0xef2929, 255.0); /* scarlet red */
+        GduColor band_color      = { 0.00, 0.00, 0.00, 0.0 };
+        GduColor band_fill_color = { 0.85, 0.85, 0.85, 0.5 };
+        GduCurve *c;
+
+        /* add graphs in order */
+        gint z_order = 0;
+
+        /* first the bands representing no data */
+        c = gdu_curve_new ();
+        gdu_curve_set_legend (c, _("No data"));
+        gdu_curve_set_z_order (c, z_order++);
+        gdu_curve_set_points (c, band_points);
+        gdu_curve_set_color (c, &band_color);
+        gdu_curve_set_fill_color (c, &band_fill_color);
+        gdu_curve_set_flags (c, GDU_CURVE_FLAGS_FILLED | GDU_CURVE_FLAGS_FADE_EDGES);
+        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
+                             "band",
+                             c);
+        g_object_unref (c);
+
+        /* then worst */
+        c = gdu_curve_new ();
+        gdu_curve_set_legend (c, _("Worst"));
+        gdu_curve_set_z_order (c, z_order++);
+        gdu_curve_set_points (c, worst_points);
+        gdu_curve_set_color (c, &worst_color);
+        gdu_curve_set_width (c, 1.0);
+        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
+                             "worst",
+                             c);
+        g_object_unref (c);
+
+        /* then threshold */
+        c = gdu_curve_new ();
+        gdu_curve_set_legend (c, _("Treshold"));
+        gdu_curve_set_z_order (c, z_order++);
+        gdu_curve_set_points (c, threshold_points);
+        gdu_curve_set_color (c, &threshold_color);
+        gdu_curve_set_width (c, 1.0);
+        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
+                             "threshold",
+                             c);
+        g_object_unref (c);
+
+        /* then current */
+        c = gdu_curve_new ();
+        gdu_curve_set_legend (c, _("Current"));
+        gdu_curve_set_z_order (c, z_order++);
+        gdu_curve_set_points (c, cur_points);
+        gdu_curve_set_color (c, &cur_color);
+        gdu_curve_set_width (c, 2.0);
+        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
                              "current",
-                             &cur_color,
-                             cur_points);
-
-        gdu_graph_set_curve (GDU_GRAPH (dialog->priv->graph),
+                             c);
+        g_object_unref (c);
+
+
+        /* then raw */
+        c = gdu_curve_new ();
+        gdu_curve_set_legend (c, _("Raw")); /* TODO: units? */
+        gdu_curve_set_z_order (c, z_order++);
+        gdu_curve_set_points (c, raw_points);
+        gdu_curve_set_color (c, &raw_color);
+        gdu_curve_set_width (c, 2.0);
+        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
                              "raw",
-                             &raw_color,
-                             raw_points);
-
-        gdu_graph_set_band (GDU_GRAPH (dialog->priv->graph),
-                            "discontinuity",
-                            &band_color,
-                            band_points);
+                             c);
+        g_object_unref (c);
 
         g_array_unref (cur_points);
+        g_array_unref (worst_points);
+        g_array_unref (threshold_points);
         g_array_unref (raw_points);
         g_array_unref (band_points);
 
@@ -415,7 +498,7 @@ gdu_ata_smart_dialog_constructed (GObject *object)
 
         graph = gdu_graph_new ();
         dialog->priv->graph = graph;
-        gtk_widget_set_size_request (graph, 480, 180);
+        gtk_widget_set_size_request (graph, 480, 250);
         gtk_box_pack_start (GTK_BOX (hbox), graph, TRUE, TRUE, 0);
 
         const gchar *time_axis[7];
diff --git a/src/gdu-gtk/gdu-curve.c b/src/gdu-gtk/gdu-curve.c
new file mode 100644
index 0000000..cc7a5a0
--- /dev/null
+++ b/src/gdu-gtk/gdu-curve.c
@@ -0,0 +1,463 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-curve.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-gtk/gdu-gtk-enumtypes.h>
+
+#include "gdu-curve.h"
+
+struct GduCurvePrivate
+{
+        GduCurveFlags flags;
+        GArray *points;
+        gint z_order;
+        GduColor *color;
+        GduColor *fill_color;
+        gdouble width;
+        gchar *legend;
+};
+
+G_DEFINE_TYPE (GduCurve, gdu_curve, G_TYPE_OBJECT)
+
+enum
+{
+        PROP_0,
+        PROP_FLAGS,
+        PROP_POINTS,
+        PROP_Z_ORDER,
+        PROP_COLOR,
+        PROP_FILL_COLOR,
+        PROP_WIDTH,
+        PROP_LEGEND,
+};
+
+static void
+gdu_curve_set_property (GObject      *object,
+                        guint         prop_id,
+                        const GValue *value,
+                        GParamSpec   *pspec)
+{
+        GduCurve *curve = GDU_CURVE (object);
+
+        switch (prop_id) {
+        case PROP_FLAGS:
+                gdu_curve_set_flags (curve, g_value_get_flags (value));
+                break;
+
+        case PROP_POINTS:
+                gdu_curve_set_points (curve, g_value_get_boxed (value));
+                break;
+
+        case PROP_Z_ORDER:
+                gdu_curve_set_z_order (curve, g_value_get_int (value));
+                break;
+
+        case PROP_COLOR:
+                gdu_curve_set_color (curve, g_value_get_boxed (value));
+                break;
+
+        case PROP_FILL_COLOR:
+                gdu_curve_set_fill_color (curve, g_value_get_boxed (value));
+                break;
+
+        case PROP_WIDTH:
+                gdu_curve_set_width (curve, g_value_get_double (value));
+                break;
+
+        case PROP_LEGEND:
+                gdu_curve_set_legend (curve, g_value_get_string (value));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gdu_curve_get_property (GObject     *object,
+                        guint        prop_id,
+                        GValue      *value,
+                        GParamSpec  *pspec)
+{
+        GduCurve *curve = GDU_CURVE (object);
+
+        switch (prop_id) {
+        case PROP_FLAGS:
+                g_value_set_flags (value, gdu_curve_get_flags (curve));
+                break;
+
+        case PROP_POINTS:
+                g_value_set_boxed (value, gdu_curve_get_points (curve));
+                break;
+
+        case PROP_Z_ORDER:
+                g_value_set_int (value, gdu_curve_get_z_order (curve));
+                break;
+
+        case PROP_COLOR:
+                g_value_set_boxed (value, gdu_curve_get_color (curve));
+                break;
+
+        case PROP_FILL_COLOR:
+                g_value_set_boxed (value, gdu_curve_get_fill_color (curve));
+                break;
+
+        case PROP_WIDTH:
+                g_value_set_double (value, gdu_curve_get_width (curve));
+                break;
+
+        case PROP_LEGEND:
+                g_value_set_string (value, gdu_curve_get_legend (curve));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+    }
+}
+
+static void
+gdu_curve_finalize (GObject *object)
+{
+        GduCurve *curve = GDU_CURVE (object);
+
+        if (curve->priv->color != NULL)
+                gdu_color_free (curve->priv->color);
+        if (curve->priv->fill_color != NULL)
+                gdu_color_free (curve->priv->fill_color);
+        if (curve->priv->points != NULL)
+                g_array_unref (curve->priv->points);
+
+        g_free (curve->priv->legend);
+
+        if (G_OBJECT_CLASS (gdu_curve_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (gdu_curve_parent_class)->finalize (object);
+}
+
+static void
+gdu_curve_constructed (GObject *object)
+{
+        GduCurve *curve = GDU_CURVE (object);
+
+        /* default to an orange color if not set */
+        if (curve->priv->color == NULL) {
+                GduColor orange = { 0.5, 0.7, 0.85, 1.0 };
+                curve->priv->color = gdu_color_dup (&orange);
+        }
+
+        if (G_OBJECT_CLASS (gdu_curve_parent_class)->constructed != NULL)
+                G_OBJECT_CLASS (gdu_curve_parent_class)->constructed (object);
+}
+
+static void
+gdu_curve_class_init (GduCurveClass *klass)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+        gobject_class->finalize     = gdu_curve_finalize;
+        gobject_class->constructed  = gdu_curve_constructed;
+        gobject_class->set_property = gdu_curve_set_property;
+        gobject_class->get_property = gdu_curve_get_property;
+
+        g_type_class_add_private (klass, sizeof (GduCurvePrivate));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_FLAGS,
+                                         g_param_spec_flags ("flags",
+                                                             _("Flags"),
+                                                             _("The flags for the curve"),
+                                                             GDU_TYPE_CURVE_FLAGS,
+                                                             GDU_CURVE_FLAGS_NONE,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE |
+                                                             G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_POINTS,
+                                         g_param_spec_boxed ("points",
+                                                             _("Points"),
+                                                             _("The points of the curve"),
+                                                             G_TYPE_ARRAY,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_Z_ORDER,
+                                         g_param_spec_int ("z-order",
+                                                           _("Z order"),
+                                                           _("Z order of the curve"),
+                                                           G_MININT,
+                                                           G_MAXINT,
+                                                           0,
+                                                           G_PARAM_READABLE |
+                                                           G_PARAM_WRITABLE |
+                                                           G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_COLOR,
+                                         g_param_spec_boxed ("color",
+                                                             _("Color"),
+                                                             _("The color of the curve"),
+                                                             GDU_TYPE_COLOR,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_FILL_COLOR,
+                                         g_param_spec_boxed ("fill-color",
+                                                             _("Fill Color"),
+                                                             _("The color of the interior of the curve"),
+                                                             GDU_TYPE_COLOR,
+                                                             G_PARAM_READABLE |
+                                                             G_PARAM_WRITABLE));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_WIDTH,
+                                         g_param_spec_double ("width",
+                                                              _("Width"),
+                                                              _("The width of the line for the curve"),
+                                                              0.0,
+                                                              G_MAXDOUBLE,
+                                                              1.0,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_LEGEND,
+                                         g_param_spec_string ("legend",
+                                                              _("Legend"),
+                                                              _("The text to show in the legend"),
+                                                              NULL,
+                                                              G_PARAM_READABLE |
+                                                              G_PARAM_WRITABLE |
+                                                              G_PARAM_CONSTRUCT));
+
+}
+
+static void
+gdu_curve_init (GduCurve *curve)
+{
+        curve->priv = G_TYPE_INSTANCE_GET_PRIVATE (curve, GDU_TYPE_CURVE, GduCurvePrivate);
+}
+
+GduCurve *
+gdu_curve_new (void)
+{
+        return GDU_CURVE (g_object_new (GDU_TYPE_CURVE, NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GduCurveFlags
+gdu_curve_get_flags (GduCurve *curve)
+{
+        g_return_val_if_fail (GDU_IS_CURVE (curve), G_MAXINT);
+        return curve->priv->flags;
+}
+
+GArray *
+gdu_curve_get_points (GduCurve *curve)
+{
+        g_return_val_if_fail (GDU_IS_CURVE (curve), NULL);
+        return curve->priv->points;
+}
+
+gint
+gdu_curve_get_z_order (GduCurve *curve)
+{
+        g_return_val_if_fail (GDU_IS_CURVE (curve), G_MAXINT);
+        return curve->priv->z_order;
+}
+
+GduColor *
+gdu_curve_get_color (GduCurve *curve)
+{
+        g_return_val_if_fail (GDU_IS_CURVE (curve), NULL);
+        return curve->priv->color;
+}
+
+GduColor *
+gdu_curve_get_fill_color (GduCurve *curve)
+{
+        g_return_val_if_fail (GDU_IS_CURVE (curve), NULL);
+        return curve->priv->fill_color;
+}
+
+gdouble
+gdu_curve_get_width (GduCurve *curve)
+{
+        g_return_val_if_fail (GDU_IS_CURVE (curve), -G_MAXDOUBLE);
+        return curve->priv->width;
+}
+
+const gchar *
+gdu_curve_get_legend (GduCurve *curve)
+{
+        g_return_val_if_fail (GDU_IS_CURVE (curve), NULL);
+        return curve->priv->legend;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+gdu_curve_set_flags (GduCurve     *curve,
+                    GduCurveFlags  flags)
+{
+        g_return_if_fail (GDU_IS_CURVE (curve));
+        curve->priv->flags = flags;
+}
+
+void
+gdu_curve_set_points (GduCurve *curve,
+                      GArray   *points)
+{
+        g_return_if_fail (GDU_IS_CURVE (curve));
+
+        if (curve->priv->points != NULL)
+                g_array_unref (curve->priv->points);
+        curve->priv->points = points != NULL ? g_array_ref (points) : NULL;
+}
+
+void
+gdu_curve_set_z_order (GduCurve *curve,
+                       gint      z_order)
+{
+        g_return_if_fail (GDU_IS_CURVE (curve));
+
+        curve->priv->z_order = z_order;
+}
+
+void
+gdu_curve_set_color (GduCurve *curve,
+                     GduColor *color)
+{
+        g_return_if_fail (GDU_IS_CURVE (curve));
+        g_return_if_fail (color != NULL);
+
+        if (curve->priv->color != NULL)
+                gdu_color_free (curve->priv->color);
+        curve->priv->color = gdu_color_dup (color);
+}
+
+void
+gdu_curve_set_fill_color (GduCurve *curve,
+                          GduColor *color)
+{
+        g_return_if_fail (GDU_IS_CURVE (curve));
+        g_return_if_fail (color != NULL);
+
+        if (curve->priv->fill_color != NULL)
+                gdu_color_free (curve->priv->fill_color);
+        curve->priv->fill_color = gdu_color_dup (color);
+}
+
+void
+gdu_curve_set_width (GduCurve *curve,
+                          gdouble   width)
+{
+        g_return_if_fail (GDU_IS_CURVE (curve));
+        g_return_if_fail (width >= 0);
+
+        curve->priv->width = width;
+}
+
+void
+gdu_curve_set_legend (GduCurve    *curve,
+                      const gchar *text)
+{
+        g_return_if_fail (GDU_IS_CURVE (curve));
+
+        g_free (curve->priv->legend);
+        curve->priv->legend = g_strdup (text);
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GduColor *
+gdu_color_dup (GduColor *color)
+{
+        GduColor *c;
+        c = g_memdup (color, sizeof (GduColor));
+        return c;
+}
+
+void
+gdu_color_free (GduColor *color)
+{
+        g_free (color);
+}
+
+GType
+gdu_color_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      GType g_define_type_id =
+        g_boxed_type_register_static ("GduColor",
+                                      (GBoxedCopyFunc) gdu_color_dup,
+                                      (GBoxedFreeFunc) gdu_color_free);
+
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+GduPoint *
+gdu_point_dup (GduPoint *point)
+{
+        GduPoint *p;
+        p = g_memdup (point, sizeof (GduPoint));
+        return p;
+}
+
+void
+gdu_point_free (GduPoint *point)
+{
+        g_free (point);
+}
+
+GType
+gdu_point_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      GType g_define_type_id =
+        g_boxed_type_register_static ("GduPoint",
+                                      (GBoxedCopyFunc) gdu_point_dup,
+                                      (GBoxedFreeFunc) gdu_point_free);
+
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
diff --git a/src/gdu-gtk/gdu-curve.h b/src/gdu-gtk/gdu-curve.h
new file mode 100644
index 0000000..294695d
--- /dev/null
+++ b/src/gdu-gtk/gdu-curve.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-curve.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_CURVE_H
+#define GDU_CURVE_H
+
+#include <gdu-gtk/gdu-gtk-types.h>
+
+#define GDU_TYPE_CURVE             (gdu_curve_get_type ())
+#define GDU_CURVE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDU_TYPE_CURVE, GduCurve))
+#define GDU_CURVE_CLASS(obj)       (G_TYPE_CHECK_CLASS_CAST ((obj), GDU_CURVE,  GduCurveClass))
+#define GDU_IS_CURVE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDU_TYPE_CURVE))
+#define GDU_IS_CURVE_CLASS(obj)    (G_TYPE_CHECK_CLASS_TYPE ((obj), GDU_TYPE_CURVE))
+#define GDU_CURVE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GDU_TYPE_CURVE, GduCurveClass))
+
+typedef struct GduCurveClass       GduCurveClass;
+typedef struct GduCurvePrivate     GduCurvePrivate;
+
+struct GduCurve
+{
+        GObject parent;
+
+        /* private */
+        GduCurvePrivate *priv;
+};
+
+struct GduCurveClass
+{
+        GObjectClass parent_class;
+};
+
+GType          gdu_curve_get_type            (void) G_GNUC_CONST;
+GduCurve      *gdu_curve_new                 (void);
+
+GduCurveFlags  gdu_curve_get_flags           (GduCurve      *curve);
+GArray        *gdu_curve_get_points          (GduCurve      *curve);
+gint           gdu_curve_get_z_order         (GduCurve      *curve);
+GduColor      *gdu_curve_get_color           (GduCurve      *curve);
+GduColor      *gdu_curve_get_fill_color      (GduCurve      *curve);
+gdouble        gdu_curve_get_width           (GduCurve      *curve);
+const gchar   *gdu_curve_get_legend          (GduCurve      *curve);
+
+void           gdu_curve_set_flags           (GduCurve      *curve,
+                                              GduCurveFlags  flags);
+void           gdu_curve_set_points          (GduCurve      *curve,
+                                              GArray        *points);
+void           gdu_curve_set_z_order         (GduCurve      *curve,
+                                              gint           z_order);
+void           gdu_curve_set_color           (GduCurve      *curve,
+                                              GduColor      *color);
+void           gdu_curve_set_fill_color      (GduCurve      *curve,
+                                              GduColor      *color);
+void           gdu_curve_set_width           (GduCurve      *curve,
+                                              gdouble        line_width);
+void           gdu_curve_set_legend          (GduCurve      *curve,
+                                              const gchar   *text);
+
+/* ----------------------------------------------------------------------------------------------------  */
+
+struct GduPoint
+{
+        gdouble x;
+        gdouble y;
+};
+
+#define GDU_TYPE_POINT (gdu_point_get_type ())
+GType     gdu_point_get_type (void) G_GNUC_CONST;
+GduPoint *gdu_point_dup      (GduPoint *point);
+void      gdu_point_free     (GduPoint *point);
+
+struct GduColor
+{
+        gdouble red;
+        gdouble green;
+        gdouble blue;
+        gdouble alpha;
+};
+
+#define GDU_TYPE_COLOR (gdu_color_get_type ())
+GType     gdu_color_get_type (void) G_GNUC_CONST;
+GduColor *gdu_color_dup      (GduColor *color);
+void      gdu_color_free     (GduColor *color);
+
+
+#endif /* GDU_CURVE_H */
diff --git a/src/gdu-gtk/gdu-graph.c b/src/gdu-gtk/gdu-graph.c
index f0b0cf9..d04f969 100644
--- a/src/gdu-gtk/gdu-graph.c
+++ b/src/gdu-gtk/gdu-graph.c
@@ -24,43 +24,11 @@
 #include <string.h>
 #include <math.h>
 
+#include "gdu-curve.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;
@@ -69,8 +37,7 @@ struct GduGraphPrivate
         gchar **y_markers_left;
         gchar **y_markers_right;
 
-        GPtrArray *curves;
-        GPtrArray *bands;
+        GHashTable *curves;
 };
 
 G_DEFINE_TYPE (GduGraph, gdu_graph, GTK_TYPE_DRAWING_AREA)
@@ -149,8 +116,7 @@ gdu_graph_finalize (GObject *object)
         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);
+        g_hash_table_unref (graph->priv->curves);
 
         if (G_OBJECT_CLASS (gdu_graph_parent_class)->finalize != NULL)
                 G_OBJECT_CLASS (gdu_graph_parent_class)->finalize (object);
@@ -205,8 +171,7 @@ 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);
+        graph->priv->curves = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 }
 
 GtkWidget *
@@ -283,6 +248,91 @@ measure_width (cairo_t     *cr,
         return te.width;
 }
 
+static gdouble
+measure_height (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.height;
+}
+
+static void
+set_fade_edges_pattern (cairo_t   *cr,
+                        gdouble    x0,
+                        gdouble    x1,
+                        GduColor  *color)
+{
+        cairo_pattern_t *pattern;
+
+        pattern = cairo_pattern_create_linear (x0, 0,
+                                               x1, 0);
+        cairo_pattern_add_color_stop_rgba (pattern, 0.00,
+                                           1.00,
+                                           1.00,
+                                           1.00,
+                                           0.00);
+        cairo_pattern_add_color_stop_rgba (pattern, 0.35,
+                                           color->red,
+                                           color->green,
+                                           color->blue,
+                                           color->alpha);
+        cairo_pattern_add_color_stop_rgba (pattern, 0.65,
+                                           color->red,
+                                           color->green,
+                                           color->blue,
+                                           color->alpha);
+        cairo_pattern_add_color_stop_rgba (pattern, 1.00,
+                                           1.00,
+                                           1.00,
+                                           1.00,
+                                           0.00);
+        cairo_set_source (cr, pattern);
+        cairo_pattern_destroy (pattern);
+}
+
+static gint
+compute_all_legends_width (cairo_t *cr,
+                           gdouble  lb_width,
+                           GList   *curve_list)
+{
+        GList *l;
+        gint width;
+
+        width = 0;
+        for (l = curve_list; l != NULL; l = l->next) {
+                GduCurve *c = GDU_CURVE (l->data);
+                cairo_text_extents_t te;
+                const gchar *text;
+
+                text = gdu_curve_get_legend (c);
+
+                if (text == NULL)
+                        continue;
+
+                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, text, &te);
+
+                width += lb_width + 3 + ceil (te.width) + 12;
+        }
+
+        return width;
+}
+
+static gint
+curve_z_order_sort (GduCurve *a,
+                    GduCurve *b)
+{
+        return gdu_curve_get_z_order (a) - gdu_curve_get_z_order (b);
+}
+
 static gboolean
 gdu_graph_expose_event (GtkWidget      *widget,
                         GdkEventExpose *event)
@@ -294,6 +344,29 @@ gdu_graph_expose_event (GtkWidget      *widget,
         cairo_t *cr;
         gdouble width, height;
         guint n;
+        gdouble x, y;
+        guint twidth;
+        guint theight;
+        guint left_margin;
+        guint right_margin;
+        guint top_margin;
+        guint bottom_margin;
+        guint x_markers_height;
+        double gx, gy, gw, gh;
+        guint64 t_left;
+        guint64 t_right;
+        GTimeVal now;
+        GList *curve_list;
+        GList *l;
+        gdouble lb_width;
+        gdouble lb_height;
+        gdouble lb_padding;
+        gdouble lb_xpos;
+        gdouble lb_ypos;
+
+        curve_list = g_hash_table_get_values (graph->priv->curves);
+        curve_list = g_list_sort (curve_list,
+                                  (GCompareFunc) curve_z_order_sort);
 
         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;
@@ -308,47 +381,152 @@ gdu_graph_expose_event (GtkWidget      *widget,
                          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;
+        gy = 0;
+        gw = width;
+        gh = height;
 
-        guint twidth;
+        top_margin = 10;
+
+        /* measure text of all x markers */
+        bottom_margin = 0;
+        for (n = 0; n < n_x_markers; n++) {
+                theight = ceil (measure_height (cr, graph->priv->x_markers[n]));
+                if (theight > bottom_margin)
+                        bottom_margin = theight;
+        }
+        bottom_margin += 12; /* padding */
+        x_markers_height = bottom_margin;
+
+        /* compute how much size we need for legends */
+        bottom_margin += 6; /* padding */
+        lb_height = 14;
+        lb_width = 23; /* golden ratio */
+        lb_padding = 6;
+        bottom_margin += lb_height + lb_padding;
+
+        /* adjust drawing area */
+        gy += top_margin;
+        gh -= top_margin;
+        gh -= bottom_margin;
+
+        gint all_legends_width;
+        all_legends_width = compute_all_legends_width (cr,
+                                                       lb_width,
+                                                       curve_list);
+
+        /* draw legends */
+        lb_ypos = gy + gh + x_markers_height + 6; /* padding */
+        lb_xpos = 10 + ceil (((width - 20) - all_legends_width) / 2);
+        for (l = curve_list; l != NULL; l = l->next) {
+                GduCurve *c = GDU_CURVE (l->data);
+                GduColor *color;
+                GduColor *fill_color;
+                gdouble width;
+                GduCurveFlags flags;
+                const gchar *text;
+                gdouble x, y;
+                cairo_text_extents_t te;
+
+                color = gdu_curve_get_color (c);
+                fill_color = gdu_curve_get_fill_color (c);
+                if (fill_color == NULL)
+                        fill_color = color;
+                width = gdu_curve_get_width (c);
+                flags = gdu_curve_get_flags (c);
+                text = gdu_curve_get_legend (c);
+
+                if (text == NULL)
+                        continue;
+
+                x = lb_xpos + 0.5;
+                y = lb_ypos + 0.5;
+
+                cairo_new_path (cr);
+                cairo_set_dash (cr, NULL, 0, 0.0);
+                cairo_set_line_width (cr, 1.0);
+                cairo_rectangle (cr, x, y, lb_width, lb_height);
+                cairo_close_path (cr);
+                cairo_set_source_rgba (cr, 1, 1, 1, 1);
+                cairo_fill_preserve (cr);
+                cairo_set_source_rgba (cr, 0, 0, 0, 1);
+                cairo_stroke (cr);
+
+                cairo_new_path (cr);
+
+                if (flags * GDU_CURVE_FLAGS_FILLED) {
+                        if (flags & GDU_CURVE_FLAGS_FADE_EDGES) {
+                                set_fade_edges_pattern (cr, x, x + lb_width, fill_color);
+                        } else {
+                                cairo_set_source_rgba (cr,
+                                                       fill_color->red,
+                                                       fill_color->green,
+                                                       fill_color->blue,
+                                                       fill_color->alpha);
+                        }
+                        cairo_rectangle (cr, x + 1, y + 1, lb_width - 2, lb_height - 2);
+                        cairo_fill (cr);
+                } else {
+                        cairo_move_to (cr, x, y + lb_height/2.0);
+                        cairo_line_to (cr, x + lb_width / 2.0, y + lb_height/3.0);
+                        cairo_line_to (cr, x + lb_width, y + 2.0 * lb_height/3.0);
+
+                        cairo_set_line_width (cr, width);
+                        cairo_set_source_rgba (cr,
+                                               color->red,
+                                               color->green,
+                                               color->blue,
+                                               color->alpha);
+                        cairo_stroke (cr);
+                }
+
+                /* and now show the text */
+
+                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, text, &te);
+                cairo_move_to (cr,
+                               x + lb_width + 3 - te.x_bearing,
+                               y + lb_height/2.0 - te.height/2 - te.y_bearing);
+
+                cairo_show_text (cr, text);
+
+                lb_xpos += lb_width + 3 + ceil (te.width) + 12;
+        }
 
         /* measure text on the left y-axis */
-        guint y_left_max_width;
-        y_left_max_width = 0;
+        left_margin = 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;
+                if (twidth > left_margin)
+                        left_margin = 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;
+                if (twidth/2 > left_margin)
+                        left_margin = twidth/2;
         }
-        y_left_max_width += 6; /* padding */
-        gx += y_left_max_width;
-        gw -= y_left_max_width;
+        left_margin += 6; /* padding */
+        gx += left_margin;
+        gw -= left_margin;
 
         /* measure text on the right y-axis */
-        guint y_right_max_width;
-        y_right_max_width = 0;
+        right_margin = 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;
+                if (twidth/2 > right_margin)
+                        right_margin = 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;
+                right_margin += twidth/2;
         }
-        y_right_max_width += 6; /* padding */
-        gw -= y_right_max_width;
+        right_margin += 6; /* padding */
+        gw -= right_margin;
 
         /* draw the box to draw in */
         cairo_set_source_rgb (cr, 1, 1, 1);
@@ -358,14 +536,15 @@ gdu_graph_expose_event (GtkWidget      *widget,
 
         /* draw markers on the left y-axis */
         for (n = 0; n < n_y_markers_left; n++) {
-                double pos;
+                gdouble pos;
+                gdouble dashes[1] = {2.0};
+                const gchar *s;
+                cairo_text_extents_t te;
 
                 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);
@@ -378,7 +557,6 @@ gdu_graph_expose_event (GtkWidget      *widget,
                 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,
@@ -391,55 +569,27 @@ gdu_graph_expose_event (GtkWidget      *widget,
 
         /* draw markers on the right y-axis */
         for (n = 0; n < n_y_markers_right; n++) {
-                double pos;
+                gdouble pos;
+                const gchar *s;
+                cairo_text_extents_t te;
 
                 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,
+                               gx + gw + right_margin/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;
 
@@ -463,7 +613,7 @@ gdu_graph_expose_event (GtkWidget      *widget,
                 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 */
+                               gy + gh + x_markers_height/2.0 - te.y_bearing);
 
                 cairo_show_text (cr, s);
 
@@ -482,77 +632,92 @@ gdu_graph_expose_event (GtkWidget      *widget,
         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];
+        for (l = curve_list; l != NULL; l = l->next) {
+                GduCurve *c = GDU_CURVE (l->data);
+                GduColor *color;
+                GduColor *fill_color;
+                gdouble width;
+                GduCurveFlags flags;
+                GArray *points;
                 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);
-                }
+                color = gdu_curve_get_color (c);
+                fill_color = gdu_curve_get_fill_color (c);
+                if (fill_color == NULL)
+                        fill_color = color;
+                width = gdu_curve_get_width (c);
+                flags = gdu_curve_get_flags (c);
+                points = gdu_curve_get_points (c);
+
+                m = 0;
+                while (m < points->len) {
+                        guint first_point_index;
+
+                        cairo_new_path (cr);
+
+                        first_point_index = m;
+                        for (; m < points->len; m++) {
+                                GduPoint *point;
+
+                                point = &g_array_index (points, GduPoint, m);
+
+                                if (point->x == G_MAXDOUBLE &&
+                                    point->y == G_MAXDOUBLE) {
+                                        m++;
+                                        break;
+                                }
+
+                                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);
+                        }
+
+                        /* fill if requested */
+                        if (flags & GDU_CURVE_FLAGS_FILLED) {
+                                GduPoint *point;
+                                gdouble first_x;
+
+                                /* first, close the path */
+                                cairo_line_to (cr, x, gy + gh);
+                                point = &g_array_index (points, GduPoint, first_point_index);
+                                first_x = gx + gw * point->x;
+                                cairo_line_to (cr, first_x, gy + gh);
+                                cairo_close_path (cr);
+
+                                if (flags & GDU_CURVE_FLAGS_FADE_EDGES) {
+                                        set_fade_edges_pattern (cr, first_x, x, fill_color);
+                                } else {
+                                        cairo_set_source_rgba (cr,
+                                                               fill_color->red,
+                                                               fill_color->green,
+                                                               fill_color->blue,
+                                                               fill_color->alpha);
+                                }
+                                cairo_fill_preserve (cr);
+                        }
+
+                        /* then draw the curve */
+                        cairo_set_dash (cr, NULL, 0, 0.0);
+                        cairo_set_line_width (cr, width);
+                        cairo_set_source_rgba (cr,
+                                               color->red,
+                                               color->green,
+                                               color->blue,
+                                               color->alpha);
+
+                        cairo_stroke (cr);
+
+                } /* process more points */
         }
 
-        /* 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);
-        }
+        g_list_free (curve_list);
 
         /* propagate event further */
         return FALSE;
@@ -562,85 +727,26 @@ 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;
-}
+        gboolean ret;
 
-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));
-        }
+        ret = g_hash_table_remove (graph->priv->curves, curve_id);
 
         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;
+        return ret;
 }
 
 void
-gdu_graph_set_band (GduGraph           *graph,
-                    const gchar        *band_id,
-                    GdkColor           *color,
-                    GArray             *points)
+gdu_graph_add_curve (GduGraph           *graph,
+                     const gchar        *curve_id,
+                     GduCurve           *curve)
 {
         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));
-        }
+        g_return_if_fail (curve_id != NULL);
+        g_return_if_fail (curve != NULL);
+
+        g_hash_table_insert (graph->priv->curves, g_strdup (curve_id), g_object_ref (curve));
 
         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
index 92fe0f0..5db40e7 100644
--- a/src/gdu-gtk/gdu-graph.h
+++ b/src/gdu-gtk/gdu-graph.h
@@ -64,30 +64,14 @@ void        gdu_graph_set_y_markers_left  (GduGraph           *graph,
 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,
+GduCurve   *gdu_graph_lookup_curve        (GduGraph           *graph,
                                            const gchar        *curve_id);
 
-void        gdu_graph_set_curve           (GduGraph           *graph,
+void        gdu_graph_add_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);
+                                           GduCurve           *curve);
 
+gboolean    gdu_graph_remove_curve        (GduGraph           *graph,
+                                           const gchar        *curve_id);
 
 #endif /* GDU_GRAPH_H */
diff --git a/src/gdu-gtk/gdu-gtk-enums.h b/src/gdu-gtk/gdu-gtk-enums.h
new file mode 100644
index 0000000..9c1b49c
--- /dev/null
+++ b/src/gdu-gtk/gdu-gtk-enums.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/* gdu-gtk-enums.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_GTK_ENUMS_H
+#define GDU_GTK_ENUMS_H
+
+#include <glib-object.h>
+
+typedef enum
+{
+        GDU_CURVE_FLAGS_NONE       = 0,
+        GDU_CURVE_FLAGS_FILLED     = (1 << 0),
+        GDU_CURVE_FLAGS_FADE_EDGES = (1 << 1),
+} GduCurveFlags;
+
+#endif /* GDU_GTK_ENUMS_H */
diff --git a/src/gdu-gtk/gdu-gtk-enumtypes.c.template b/src/gdu-gtk/gdu-gtk-enumtypes.c.template
new file mode 100644
index 0000000..9ffe156
--- /dev/null
+++ b/src/gdu-gtk/gdu-gtk-enumtypes.c.template
@@ -0,0 +1,39 @@
+/*** BEGIN file-header ***/
+#include <gdu-gtk/gdu-gtk.h>
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+ enum_name@_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const G Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+        { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_ type@_register_static (g_intern_static_string ("@EnumName@"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/src/gdu-gtk/gdu-gtk-enumtypes.h.template b/src/gdu-gtk/gdu-gtk-enumtypes.h.template
new file mode 100644
index 0000000..4737f89
--- /dev/null
+++ b/src/gdu-gtk/gdu-gtk-enumtypes.h.template
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef __GDU_GTK_ENUM_TYPES_H__
+#define __GDU_GTK_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX _TYPE_@ENUMSHORT@ (@enum_name _get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __GDU_GTK_ENUM_TYPES_H__ */
+/*** END file-tail ***/
diff --git a/src/gdu-gtk/gdu-gtk-types.h b/src/gdu-gtk/gdu-gtk-types.h
index efab698..2be3ac0 100644
--- a/src/gdu-gtk/gdu-gtk-types.h
+++ b/src/gdu-gtk/gdu-gtk-types.h
@@ -29,13 +29,16 @@
 #include <glib-object.h>
 #include <gdu/gdu.h>
 #include <gtk/gtk.h>
+#include <gdu-gtk/gdu-gtk-enums.h>
 
 G_BEGIN_DECLS
 
+typedef struct GduPoint                    GduPoint;
+typedef struct GduColor                    GduColor;
+typedef struct GduCurve                    GduCurve;
 typedef struct GduGraph                    GduGraph;
 typedef struct GduTimeLabel                GduTimeLabel;
 typedef struct GduAtaSmartDialog           GduAtaSmartDialog;
-typedef struct GduAtaSmartAttributeDialog  GduAtaSmartAttributeDialog;
 
 G_END_DECLS
 
diff --git a/src/gdu-gtk/gdu-gtk.h b/src/gdu-gtk/gdu-gtk.h
index b530209..f3bce49 100644
--- a/src/gdu-gtk/gdu-gtk.h
+++ b/src/gdu-gtk/gdu-gtk.h
@@ -28,10 +28,11 @@
 
 #define __GDU_GTK_INSIDE_GDU_GTK_H
 #include <gdu-gtk/gdu-gtk-types.h>
+#include <gdu-gtk/gdu-gtk-enumtypes.h>
+#include <gdu-gtk/gdu-curve.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>
 #undef __GDU_GTK_INSIDE_GDU_GTK_H
 
 G_BEGIN_DECLS



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]