284 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2014 Benjamin Otte <ottte@gnome.org>
 | 
						|
 *
 | 
						|
 * 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, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
 | 
						|
#include "cellrenderergraph.h"
 | 
						|
 | 
						|
#include "graphdata.h"
 | 
						|
 | 
						|
enum {
 | 
						|
  PROP_0,
 | 
						|
  PROP_DATA,
 | 
						|
  PROP_MINIMUM,
 | 
						|
  PROP_MAXIMUM
 | 
						|
};
 | 
						|
 | 
						|
struct _GtkCellRendererGraphPrivate
 | 
						|
{
 | 
						|
  GtkGraphData *data;
 | 
						|
  double minimum;
 | 
						|
  double maximum;
 | 
						|
};
 | 
						|
 | 
						|
G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererGraph, gtk_cell_renderer_graph, GTK_TYPE_CELL_RENDERER)
 | 
						|
 | 
						|
static void 
 | 
						|
gtk_cell_renderer_graph_dispose (GObject *object)
 | 
						|
{
 | 
						|
  GtkCellRendererGraph *graph = GTK_CELL_RENDERER_GRAPH (object);
 | 
						|
  GtkCellRendererGraphPrivate *priv = graph->priv;
 | 
						|
 | 
						|
  g_clear_object (&priv->data);
 | 
						|
 | 
						|
  G_OBJECT_CLASS (gtk_cell_renderer_graph_parent_class)->dispose (object);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
gtk_cell_renderer_graph_get_property (GObject    *object,
 | 
						|
                                      guint       param_id,
 | 
						|
                                      GValue     *value,
 | 
						|
                                      GParamSpec *pspec)
 | 
						|
{
 | 
						|
  GtkCellRendererGraph *cell = GTK_CELL_RENDERER_GRAPH (object);
 | 
						|
  GtkCellRendererGraphPrivate *priv = cell->priv;
 | 
						|
 | 
						|
  switch (param_id)
 | 
						|
    {
 | 
						|
      case PROP_DATA:
 | 
						|
        g_value_set_object (value, priv->data);
 | 
						|
        break;
 | 
						|
      case PROP_MINIMUM:
 | 
						|
        g_value_set_double (value, priv->minimum);
 | 
						|
        break;
 | 
						|
      case PROP_MAXIMUM:
 | 
						|
        g_value_set_double (value, priv->maximum);
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
gtk_cell_renderer_graph_set_property (GObject      *object,
 | 
						|
                                      guint         param_id,
 | 
						|
                                      const GValue *value,
 | 
						|
                                      GParamSpec   *pspec)
 | 
						|
{
 | 
						|
  GtkCellRendererGraph *cell = GTK_CELL_RENDERER_GRAPH (object);
 | 
						|
  GtkCellRendererGraphPrivate *priv = cell->priv;
 | 
						|
 | 
						|
  switch (param_id)
 | 
						|
    {
 | 
						|
      case PROP_DATA:
 | 
						|
        if (priv->data != g_value_get_object (value))
 | 
						|
          {
 | 
						|
            if (priv->data)
 | 
						|
              g_object_unref (priv->data);
 | 
						|
            priv->data = g_value_dup_object (value);
 | 
						|
            g_object_notify_by_pspec (object, pspec);
 | 
						|
          }
 | 
						|
        break;
 | 
						|
      case PROP_MINIMUM:
 | 
						|
        if (priv->minimum != g_value_get_double (value))
 | 
						|
          {
 | 
						|
            priv->minimum = g_value_get_double (value);
 | 
						|
            g_object_notify_by_pspec (object, pspec);
 | 
						|
          }
 | 
						|
        break;
 | 
						|
      case PROP_MAXIMUM:
 | 
						|
        if (priv->maximum != g_value_get_double (value))
 | 
						|
          {
 | 
						|
            priv->maximum = g_value_get_double (value);
 | 
						|
            g_object_notify_by_pspec (object, pspec);
 | 
						|
          }
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
gtk_cell_renderer_graph_get_size (GtkCellRenderer    *cell,
 | 
						|
                                  GtkWidget          *widget,
 | 
						|
                                  const GdkRectangle *cell_area,
 | 
						|
                                  gint               *x_offset,
 | 
						|
                                  gint               *y_offset,
 | 
						|
                                  gint               *width,
 | 
						|
                                  gint               *height)
 | 
						|
{
 | 
						|
  int xpad, ypad;
 | 
						|
 | 
						|
#define MIN_HEIGHT 24
 | 
						|
#define MIN_WIDTH 3 * MIN_HEIGHT
 | 
						|
 | 
						|
  g_object_get (cell,
 | 
						|
                "xpad", &xpad,
 | 
						|
                "ypad", &ypad,
 | 
						|
                NULL);
 | 
						|
 | 
						|
  if (cell_area)
 | 
						|
    {
 | 
						|
      if (width)
 | 
						|
        *width = cell_area->width - 2 * xpad;
 | 
						|
      if (height)
 | 
						|
        *height = cell_area->height - 2 * ypad;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (width)
 | 
						|
        *width = MIN_WIDTH + 2 * xpad;
 | 
						|
      if (height)
 | 
						|
        *height = MIN_HEIGHT + 2 * ypad;
 | 
						|
    }
 | 
						|
 | 
						|
  if (x_offset)
 | 
						|
    *x_offset = xpad;
 | 
						|
  if (y_offset)
 | 
						|
    *y_offset = ypad;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
gtk_cell_renderer_graph_render (GtkCellRenderer      *cell,
 | 
						|
                                cairo_t              *cr,
 | 
						|
                                GtkWidget            *widget,
 | 
						|
                                const GdkRectangle   *background_area,
 | 
						|
                                const GdkRectangle   *cell_area,
 | 
						|
                                GtkCellRendererState  flags)
 | 
						|
{
 | 
						|
  GtkCellRendererGraph *graph = GTK_CELL_RENDERER_GRAPH (cell);
 | 
						|
  GtkCellRendererGraphPrivate *priv = graph->priv;
 | 
						|
  GtkStyleContext *context;
 | 
						|
  double minimum, maximum, diff;
 | 
						|
  double x, y, width, height;
 | 
						|
  int xpad, ypad;
 | 
						|
  GdkRGBA color;
 | 
						|
  guint i, n;
 | 
						|
 | 
						|
#define LINE_WIDTH 1.0
 | 
						|
 | 
						|
  if (priv->data == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  g_object_get (cell,
 | 
						|
                "xpad", &xpad,
 | 
						|
                "ypad", &ypad,
 | 
						|
                NULL);
 | 
						|
 | 
						|
  if (priv->minimum == -G_MAXDOUBLE)
 | 
						|
    minimum = gtk_graph_data_get_minimum (priv->data);
 | 
						|
  else
 | 
						|
    minimum = priv->minimum;
 | 
						|
 | 
						|
  if (priv->maximum == G_MAXDOUBLE)
 | 
						|
    maximum = gtk_graph_data_get_maximum (priv->data);
 | 
						|
  else
 | 
						|
    maximum = priv->maximum;
 | 
						|
 | 
						|
  diff = maximum - minimum;
 | 
						|
 | 
						|
  context = gtk_widget_get_style_context (widget);
 | 
						|
  gtk_style_context_get_color (context, gtk_style_context_get_state (context), &color);
 | 
						|
 | 
						|
  cairo_set_line_width (cr, 1.0);
 | 
						|
 | 
						|
  x = background_area->x + xpad + LINE_WIDTH / 2.0;
 | 
						|
  y = background_area->y + ypad + LINE_WIDTH / 2.0;
 | 
						|
  width = background_area->width - 2 * xpad - LINE_WIDTH;
 | 
						|
  height = background_area->height - 2 * ypad - LINE_WIDTH;
 | 
						|
 | 
						|
  cairo_move_to (cr, x, y + height);
 | 
						|
 | 
						|
  if (diff > 0)
 | 
						|
    {
 | 
						|
      n = gtk_graph_data_get_n_values (priv->data);
 | 
						|
      for (i = 0; i < n; i++)
 | 
						|
        {
 | 
						|
          double val = gtk_graph_data_get_value (priv->data, i);
 | 
						|
 | 
						|
          val = (val - minimum) / diff;
 | 
						|
          val = y + height - val * height;
 | 
						|
 | 
						|
          cairo_line_to (cr, x + width * i / (n - 1), val);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  cairo_line_to (cr, x + width, y + height);
 | 
						|
  cairo_close_path (cr);
 | 
						|
 | 
						|
  gdk_cairo_set_source_rgba (cr, &color);
 | 
						|
  cairo_stroke_preserve (cr);
 | 
						|
 | 
						|
  color.alpha *= 0.2;
 | 
						|
  gdk_cairo_set_source_rgba (cr, &color);
 | 
						|
  cairo_fill (cr);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
gtk_cell_renderer_graph_class_init (GtkCellRendererGraphClass *klass)
 | 
						|
{
 | 
						|
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 | 
						|
  GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
 | 
						|
 | 
						|
  object_class->dispose = gtk_cell_renderer_graph_dispose;
 | 
						|
  object_class->get_property = gtk_cell_renderer_graph_get_property;
 | 
						|
  object_class->set_property = gtk_cell_renderer_graph_set_property;
 | 
						|
 | 
						|
  cell_class->get_size = gtk_cell_renderer_graph_get_size;
 | 
						|
  cell_class->render = gtk_cell_renderer_graph_render;
 | 
						|
 | 
						|
  g_object_class_install_property (object_class,
 | 
						|
                                   PROP_DATA,
 | 
						|
                                   g_param_spec_object ("data",
 | 
						|
                                                        "Data",
 | 
						|
                                                        "The data to display",
 | 
						|
                                                         GTK_TYPE_GRAPH_DATA,
 | 
						|
                                                         G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class,
 | 
						|
                                   PROP_MINIMUM,
 | 
						|
                                   g_param_spec_double ("minimum",
 | 
						|
                                                        "Minimum",
 | 
						|
                                                        "Minimum value to use (or -G_MAXDOUBLE for graph's value",
 | 
						|
                                                        -G_MAXDOUBLE, G_MAXDOUBLE, -G_MAXDOUBLE,
 | 
						|
                                                        G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
 | 
						|
 | 
						|
  g_object_class_install_property (object_class,
 | 
						|
                                   PROP_MINIMUM,
 | 
						|
                                   g_param_spec_double ("maximum",
 | 
						|
                                                        "Maximum",
 | 
						|
                                                        "Maximum value to use (or G_MAXDOUBLE for graph's value",
 | 
						|
                                                        -G_MAXDOUBLE, G_MAXDOUBLE, G_MAXDOUBLE,
 | 
						|
                                                        G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
gtk_cell_renderer_graph_init (GtkCellRendererGraph *cell)
 | 
						|
{
 | 
						|
  cell->priv = gtk_cell_renderer_graph_get_instance_private (cell);
 | 
						|
 | 
						|
  cell->priv->minimum = -G_MAXDOUBLE;
 | 
						|
  cell->priv->maximum = G_MAXDOUBLE;
 | 
						|
}
 | 
						|
 | 
						|
GtkCellRenderer *
 | 
						|
gtk_cell_renderer_graph_new (void)
 | 
						|
{
 | 
						|
  return g_object_new (GTK_TYPE_CELL_RENDERER_GRAPH, NULL);
 | 
						|
}
 | 
						|
 |