1 /*
2  * Copyright (c) 2014 Benjamin Otte <ottte@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include "cellrenderergraph.h"
21 
22 #include "graphdata.h"
23 
24 enum {
25   PROP_0,
26   PROP_DATA,
27   PROP_MINIMUM,
28   PROP_MAXIMUM
29 };
30 
31 struct _GtkCellRendererGraphPrivate
32 {
33   GtkGraphData *data;
34   double minimum;
35   double maximum;
36 };
37 
G_DEFINE_TYPE_WITH_PRIVATE(GtkCellRendererGraph,gtk_cell_renderer_graph,GTK_TYPE_CELL_RENDERER)38 G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererGraph, gtk_cell_renderer_graph, GTK_TYPE_CELL_RENDERER)
39 
40 static void
41 gtk_cell_renderer_graph_dispose (GObject *object)
42 {
43   GtkCellRendererGraph *graph = GTK_CELL_RENDERER_GRAPH (object);
44   GtkCellRendererGraphPrivate *priv = graph->priv;
45 
46   g_clear_object (&priv->data);
47 
48   G_OBJECT_CLASS (gtk_cell_renderer_graph_parent_class)->dispose (object);
49 }
50 
51 static void
gtk_cell_renderer_graph_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)52 gtk_cell_renderer_graph_get_property (GObject    *object,
53                                       guint       param_id,
54                                       GValue     *value,
55                                       GParamSpec *pspec)
56 {
57   GtkCellRendererGraph *cell = GTK_CELL_RENDERER_GRAPH (object);
58   GtkCellRendererGraphPrivate *priv = cell->priv;
59 
60   switch (param_id)
61     {
62       case PROP_DATA:
63         g_value_set_object (value, priv->data);
64         break;
65       case PROP_MINIMUM:
66         g_value_set_double (value, priv->minimum);
67         break;
68       case PROP_MAXIMUM:
69         g_value_set_double (value, priv->maximum);
70         break;
71       default:
72         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
73     }
74 }
75 
76 static void
gtk_cell_renderer_graph_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)77 gtk_cell_renderer_graph_set_property (GObject      *object,
78                                       guint         param_id,
79                                       const GValue *value,
80                                       GParamSpec   *pspec)
81 {
82   GtkCellRendererGraph *cell = GTK_CELL_RENDERER_GRAPH (object);
83   GtkCellRendererGraphPrivate *priv = cell->priv;
84 
85   switch (param_id)
86     {
87       case PROP_DATA:
88         if (priv->data != g_value_get_object (value))
89           {
90             if (priv->data)
91               g_object_unref (priv->data);
92             priv->data = g_value_dup_object (value);
93             g_object_notify_by_pspec (object, pspec);
94           }
95         break;
96       case PROP_MINIMUM:
97         if (priv->minimum != g_value_get_double (value))
98           {
99             priv->minimum = g_value_get_double (value);
100             g_object_notify_by_pspec (object, pspec);
101           }
102         break;
103       case PROP_MAXIMUM:
104         if (priv->maximum != g_value_get_double (value))
105           {
106             priv->maximum = g_value_get_double (value);
107             g_object_notify_by_pspec (object, pspec);
108           }
109         break;
110       default:
111         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
112     }
113 }
114 
115 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)116 gtk_cell_renderer_graph_get_size (GtkCellRenderer    *cell,
117                                   GtkWidget          *widget,
118                                   const GdkRectangle *cell_area,
119                                   gint               *x_offset,
120                                   gint               *y_offset,
121                                   gint               *width,
122                                   gint               *height)
123 {
124   int xpad, ypad;
125 
126 #define MIN_HEIGHT 24
127 #define MIN_WIDTH 3 * MIN_HEIGHT
128 
129   g_object_get (cell,
130                 "xpad", &xpad,
131                 "ypad", &ypad,
132                 NULL);
133 
134   if (cell_area)
135     {
136       if (width)
137         *width = cell_area->width - 2 * xpad;
138       if (height)
139         *height = cell_area->height - 2 * ypad;
140     }
141   else
142     {
143       if (width)
144         *width = MIN_WIDTH + 2 * xpad;
145       if (height)
146         *height = MIN_HEIGHT + 2 * ypad;
147     }
148 
149   if (x_offset)
150     *x_offset = xpad;
151   if (y_offset)
152     *y_offset = ypad;
153 }
154 
155 static void
gtk_cell_renderer_graph_render(GtkCellRenderer * cell,cairo_t * cr,GtkWidget * widget,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)156 gtk_cell_renderer_graph_render (GtkCellRenderer      *cell,
157                                 cairo_t              *cr,
158                                 GtkWidget            *widget,
159                                 const GdkRectangle   *background_area,
160                                 const GdkRectangle   *cell_area,
161                                 GtkCellRendererState  flags)
162 {
163   GtkCellRendererGraph *graph = GTK_CELL_RENDERER_GRAPH (cell);
164   GtkCellRendererGraphPrivate *priv = graph->priv;
165   GtkStyleContext *context;
166   double minimum, maximum, diff;
167   double x, y, width, height;
168   int xpad, ypad;
169   GdkRGBA color;
170   guint i, n;
171 
172 #define LINE_WIDTH 1.0
173 
174   if (priv->data == NULL)
175     return;
176 
177   g_object_get (cell,
178                 "xpad", &xpad,
179                 "ypad", &ypad,
180                 NULL);
181 
182   if (priv->minimum == -G_MAXDOUBLE)
183     minimum = gtk_graph_data_get_minimum (priv->data);
184   else
185     minimum = priv->minimum;
186 
187   if (priv->maximum == G_MAXDOUBLE)
188     maximum = gtk_graph_data_get_maximum (priv->data);
189   else
190     maximum = priv->maximum;
191 
192   diff = maximum - minimum;
193 
194   context = gtk_widget_get_style_context (widget);
195   gtk_style_context_get_color (context, gtk_style_context_get_state (context), &color);
196 
197   cairo_set_line_width (cr, 1.0);
198 
199   x = background_area->x + xpad + LINE_WIDTH / 2.0;
200   y = background_area->y + ypad + LINE_WIDTH / 2.0;
201   width = background_area->width - 2 * xpad - LINE_WIDTH;
202   height = background_area->height - 2 * ypad - LINE_WIDTH;
203 
204   cairo_move_to (cr, x, y + height);
205 
206   if (diff > 0)
207     {
208       n = gtk_graph_data_get_n_values (priv->data);
209       for (i = 0; i < n; i++)
210         {
211           double val = gtk_graph_data_get_value (priv->data, i);
212 
213           val = (val - minimum) / diff;
214           val = y + height - val * height;
215 
216           cairo_line_to (cr, x + width * i / (n - 1), val);
217         }
218     }
219 
220   cairo_line_to (cr, x + width, y + height);
221   cairo_close_path (cr);
222 
223   gdk_cairo_set_source_rgba (cr, &color);
224   cairo_stroke_preserve (cr);
225 
226   color.alpha *= 0.2;
227   gdk_cairo_set_source_rgba (cr, &color);
228   cairo_fill (cr);
229 }
230 
231 static void
gtk_cell_renderer_graph_class_init(GtkCellRendererGraphClass * klass)232 gtk_cell_renderer_graph_class_init (GtkCellRendererGraphClass *klass)
233 {
234   GObjectClass *object_class = G_OBJECT_CLASS (klass);
235   GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
236 
237   object_class->dispose = gtk_cell_renderer_graph_dispose;
238   object_class->get_property = gtk_cell_renderer_graph_get_property;
239   object_class->set_property = gtk_cell_renderer_graph_set_property;
240 
241   cell_class->get_size = gtk_cell_renderer_graph_get_size;
242   cell_class->render = gtk_cell_renderer_graph_render;
243 
244   g_object_class_install_property (object_class,
245                                    PROP_DATA,
246                                    g_param_spec_object ("data",
247                                                         "Data",
248                                                         "The data to display",
249                                                          GTK_TYPE_GRAPH_DATA,
250                                                          G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
251 
252   g_object_class_install_property (object_class,
253                                    PROP_MINIMUM,
254                                    g_param_spec_double ("minimum",
255                                                         "Minimum",
256                                                         "Minimum value to use (or -G_MAXDOUBLE for graph's value",
257                                                         -G_MAXDOUBLE, G_MAXDOUBLE, -G_MAXDOUBLE,
258                                                         G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
259 
260   g_object_class_install_property (object_class,
261                                    PROP_MINIMUM,
262                                    g_param_spec_double ("maximum",
263                                                         "Maximum",
264                                                         "Maximum value to use (or G_MAXDOUBLE for graph's value",
265                                                         -G_MAXDOUBLE, G_MAXDOUBLE, G_MAXDOUBLE,
266                                                         G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
267 }
268 
269 static void
gtk_cell_renderer_graph_init(GtkCellRendererGraph * cell)270 gtk_cell_renderer_graph_init (GtkCellRendererGraph *cell)
271 {
272   cell->priv = gtk_cell_renderer_graph_get_instance_private (cell);
273 
274   cell->priv->minimum = -G_MAXDOUBLE;
275   cell->priv->maximum = G_MAXDOUBLE;
276 }
277 
278 GtkCellRenderer *
gtk_cell_renderer_graph_new(void)279 gtk_cell_renderer_graph_new (void)
280 {
281   return g_object_new (GTK_TYPE_CELL_RENDERER_GRAPH, NULL);
282 }
283 
284