2014-10-14 12:32:57 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
|
|
|
|
2018-02-09 00:06:59 +00:00
|
|
|
#include "gtksnapshot.h"
|
|
|
|
#include "gtkstylecontext.h"
|
|
|
|
|
2014-10-14 12:32:57 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 19:18:55 +00:00
|
|
|
#define MIN_HEIGHT 24
|
|
|
|
#define MIN_WIDTH (3 * MIN_HEIGHT)
|
|
|
|
|
2014-10-14 12:32:57 +00:00
|
|
|
static void
|
2020-11-28 19:18:55 +00:00
|
|
|
gtk_cell_renderer_graph_get_preferred_width (GtkCellRenderer *cell,
|
|
|
|
GtkWidget *widget,
|
|
|
|
int *minimum,
|
|
|
|
int *natural)
|
2014-10-14 12:32:57 +00:00
|
|
|
{
|
2020-11-28 19:18:55 +00:00
|
|
|
int xpad, size;
|
2014-10-14 12:32:57 +00:00
|
|
|
|
2020-11-28 19:18:55 +00:00
|
|
|
g_object_get (cell, "xpad", &xpad, NULL);
|
2014-10-14 12:32:57 +00:00
|
|
|
|
2020-11-28 19:18:55 +00:00
|
|
|
size = MIN_WIDTH + 2 * xpad;
|
2014-10-14 12:32:57 +00:00
|
|
|
|
2020-11-28 19:18:55 +00:00
|
|
|
if (minimum != NULL)
|
|
|
|
*minimum = size;
|
|
|
|
if (natural != NULL)
|
|
|
|
*natural = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gtk_cell_renderer_graph_get_preferred_height (GtkCellRenderer *cell,
|
|
|
|
GtkWidget *widget,
|
|
|
|
int *minimum,
|
|
|
|
int *natural)
|
|
|
|
{
|
|
|
|
int ypad, size;
|
|
|
|
|
|
|
|
g_object_get (cell, "ypad", &ypad, NULL);
|
2014-10-14 12:32:57 +00:00
|
|
|
|
2020-11-28 19:18:55 +00:00
|
|
|
size = MIN_HEIGHT + 2 * ypad;
|
|
|
|
|
|
|
|
if (minimum != NULL)
|
|
|
|
*minimum = size;
|
|
|
|
if (natural != NULL)
|
|
|
|
*natural = size;
|
2014-10-14 12:32:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-12-23 10:11:52 +00:00
|
|
|
gtk_cell_renderer_graph_snapshot (GtkCellRenderer *cell,
|
|
|
|
GtkSnapshot *snapshot,
|
|
|
|
GtkWidget *widget,
|
|
|
|
const GdkRectangle *background_area,
|
|
|
|
const GdkRectangle *cell_area,
|
|
|
|
GtkCellRendererState flags)
|
2014-10-14 12:32:57 +00:00
|
|
|
{
|
|
|
|
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;
|
2016-12-23 10:11:52 +00:00
|
|
|
cairo_t *cr;
|
2014-10-14 12:32:57 +00:00
|
|
|
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);
|
2016-10-08 03:07:23 +00:00
|
|
|
gtk_style_context_get_color (context, &color);
|
2014-10-14 12:32:57 +00:00
|
|
|
|
2017-01-13 03:46:09 +00:00
|
|
|
cr = gtk_snapshot_append_cairo (snapshot,
|
|
|
|
&GRAPHENE_RECT_INIT (
|
|
|
|
background_area->x, background_area->y,
|
|
|
|
background_area->width, background_area->height
|
2018-04-24 01:17:23 +00:00
|
|
|
));
|
2016-12-23 10:11:52 +00:00
|
|
|
|
2014-10-14 12:32:57 +00:00
|
|
|
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);
|
2016-12-23 10:11:52 +00:00
|
|
|
|
|
|
|
cairo_destroy (cr);
|
2014-10-14 12:32:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2020-11-28 19:18:55 +00:00
|
|
|
cell_class->get_preferred_width = gtk_cell_renderer_graph_get_preferred_width;
|
|
|
|
cell_class->get_preferred_height = gtk_cell_renderer_graph_get_preferred_height;
|
2016-12-23 10:11:52 +00:00
|
|
|
cell_class->snapshot = gtk_cell_renderer_graph_snapshot;
|
2014-10-14 12:32:57 +00:00
|
|
|
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_DATA,
|
2022-05-11 12:19:39 +00:00
|
|
|
g_param_spec_object ("data", NULL, NULL,
|
2014-10-14 12:32:57 +00:00
|
|
|
GTK_TYPE_GRAPH_DATA,
|
|
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_MINIMUM,
|
2022-05-11 12:19:39 +00:00
|
|
|
g_param_spec_double ("minimum", NULL, NULL,
|
2014-10-14 12:32:57 +00:00
|
|
|
-G_MAXDOUBLE, G_MAXDOUBLE, -G_MAXDOUBLE,
|
|
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_MINIMUM,
|
2022-05-11 12:19:39 +00:00
|
|
|
g_param_spec_double ("maximum", NULL, NULL,
|
2014-10-14 12:32:57 +00:00
|
|
|
-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);
|
|
|
|
}
|