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