1 /* $Id: gtkdatabox_grid.c 4 2008-06-22 09:19:11Z rbock $ */
2 /* GtkDatabox - An extension to the gtk+ library
3  * Copyright (C) 1998 - 2008  Dr. Roland Bock
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <gtkdatabox_grid.h>
21 #include <math.h>
22 
23 G_DEFINE_TYPE(GtkDataboxGrid, gtk_databox_grid,
24 	GTK_DATABOX_TYPE_GRAPH)
25 
26 static void gtk_databox_grid_real_draw (GtkDataboxGraph * grid,
27 					GtkDatabox* box);
28 static cairo_t* gtk_databox_grid_real_create_gc (GtkDataboxGraph * graph,
29 					     GtkDatabox* box);
30 
31 /* IDs of properties */
32 enum
33 {
34    GRID_HLINES = 1,
35    GRID_VLINES,
36    GRID_HLINE_VALS,
37    GRID_VLINE_VALS,
38    GRID_LINE_STYLE
39 };
40 
41 /**
42  * GtkDataboxGridPrivate
43  *
44  * A private data structure used by the #GtkDataboxGrid. It shields all internal things
45  * from developers who are just using the object.
46  *
47  **/
48 typedef struct _GtkDataboxGridPrivate GtkDataboxGridPrivate;
49 
50 struct _GtkDataboxGridPrivate
51 {
52    gint hlines;
53    gint vlines;
54    gfloat *hline_vals;
55    gfloat *vline_vals;
56    GtkDataboxGridLineStyle line_style;
57 };
58 
59 static void
gtk_databox_grid_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)60 gtk_databox_grid_set_property (GObject * object,
61 			       guint property_id,
62 			       const GValue * value, GParamSpec * pspec)
63 {
64    GtkDataboxGrid *grid = GTK_DATABOX_GRID (object);
65 
66    switch (property_id)
67    {
68    case GRID_HLINES:
69       {
70 	 gtk_databox_grid_set_hlines (grid, g_value_get_int (value));
71       }
72       break;
73    case GRID_VLINES:
74       {
75 	 gtk_databox_grid_set_vlines (grid, g_value_get_int (value));
76       }
77       break;
78    case GRID_HLINE_VALS:
79       {
80 	 gtk_databox_grid_set_hline_vals (grid, (gfloat *) g_value_get_pointer (value));
81       }
82       break;
83    case GRID_VLINE_VALS:
84       {
85 	 gtk_databox_grid_set_vline_vals (grid, (gfloat *) g_value_get_pointer (value));
86       }
87       break;
88    case GRID_LINE_STYLE:
89       {
90 	 gtk_databox_grid_set_line_style (grid, g_value_get_int (value));
91       }
92       break;
93    default:
94       /* We don't have any other property... */
95       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
96       break;
97    }
98 }
99 
100 static void
gtk_databox_grid_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)101 gtk_databox_grid_get_property (GObject * object,
102 			       guint property_id,
103 			       GValue * value, GParamSpec * pspec)
104 {
105    GtkDataboxGrid *grid = GTK_DATABOX_GRID (object);
106 
107    switch (property_id)
108    {
109    case GRID_HLINES:
110       {
111 	 g_value_set_int (value, gtk_databox_grid_get_hlines (grid));
112       }
113       break;
114    case GRID_VLINES:
115       {
116 	 g_value_set_int (value, gtk_databox_grid_get_vlines (grid));
117       }
118       break;
119    case GRID_HLINE_VALS:
120       {
121     g_value_set_pointer (value, gtk_databox_grid_get_hline_vals (grid));
122       }
123       break;
124    case GRID_VLINE_VALS:
125       {
126     g_value_set_pointer (value, gtk_databox_grid_get_vline_vals (grid));
127       }
128       break;
129    case GRID_LINE_STYLE:
130       {
131 	 g_value_set_int (value, gtk_databox_grid_get_line_style (grid));
132       }
133       break;
134    default:
135       /* We don't have any other property... */
136       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
137       break;
138    }
139 }
140 
141 
142 
143 static cairo_t*
gtk_databox_grid_real_create_gc(GtkDataboxGraph * graph,GtkDatabox * box)144 gtk_databox_grid_real_create_gc (GtkDataboxGraph * graph,
145 				 GtkDatabox* box)
146 {
147    cairo_t *cr;
148 
149    g_return_val_if_fail (GTK_DATABOX_IS_GRID (graph), NULL);
150 
151    cr = GTK_DATABOX_GRAPH_CLASS (gtk_databox_grid_parent_class)->create_gc (graph, box);
152 
153    return cr;
154 }
155 
156 static void
grid_finalize(GObject * object)157 grid_finalize (GObject * object)
158 {
159   //GtkDataboxGraph *graph = GTK_DATABOX_GRAPH (object);
160 
161   /* Chain up to the parent class */
162   G_OBJECT_CLASS (gtk_databox_grid_parent_class)->finalize (object);
163 }
164 
165 static void
gtk_databox_grid_class_init(GtkDataboxGridClass * klass)166 gtk_databox_grid_class_init (GtkDataboxGridClass *klass)
167 {
168    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
169    GtkDataboxGraphClass *graph_class = GTK_DATABOX_GRAPH_CLASS (klass);
170    GParamSpec *grid_param_spec;
171 
172    gobject_class->set_property = gtk_databox_grid_set_property;
173    gobject_class->get_property = gtk_databox_grid_get_property;
174    gobject_class->finalize = grid_finalize;
175 
176    grid_param_spec = g_param_spec_int ("grid-hlines", "grid-hlines", "Number of horizontal lines", G_MININT, G_MAXINT, 0,	/* default value */
177 				       G_PARAM_READWRITE);
178 
179    g_object_class_install_property (gobject_class,
180 				    GRID_HLINES, grid_param_spec);
181 
182    grid_param_spec = g_param_spec_int ("grid-vlines", "grid-vlines", "Number of vertical lines", G_MININT, G_MAXINT, 0,	/* default value */
183 				       G_PARAM_READWRITE);
184 
185    g_object_class_install_property (gobject_class,
186 				    GRID_VLINES, grid_param_spec);
187 
188    grid_param_spec = g_param_spec_pointer ("grid-hline-vals", "Grid Hline Vals", "The locations of each of the horizontal lines", G_PARAM_READWRITE);
189 
190    g_object_class_install_property (gobject_class,
191 				    GRID_HLINE_VALS, grid_param_spec);
192 
193    grid_param_spec = g_param_spec_pointer ("grid-vline-vals", "Grid Vline Vals", "The locations of each of the vertical lines", G_PARAM_READWRITE);
194 
195    g_object_class_install_property (gobject_class,
196 				    GRID_VLINE_VALS, grid_param_spec);
197 
198    grid_param_spec = g_param_spec_int ("line-style", "line-style", "Line style of grid lines",
199 				       GTK_DATABOX_GRID_DASHED_LINES, GTK_DATABOX_GRID_DOTTED_LINES,
200 				       GTK_DATABOX_GRID_DASHED_LINES,
201 				       G_PARAM_READWRITE);
202 
203    g_object_class_install_property (gobject_class,
204 				    GRID_LINE_STYLE, grid_param_spec);
205 
206 
207    graph_class->draw = gtk_databox_grid_real_draw;
208    graph_class->create_gc = gtk_databox_grid_real_create_gc;
209 
210    g_type_class_add_private (klass, sizeof (GtkDataboxGridPrivate));
211 }
212 
gtk_databox_grid_init(GtkDataboxGrid * grid)213 static void gtk_databox_grid_init (GtkDataboxGrid *grid) { grid = grid; }
214 
215 /**
216  * gtk_databox_grid_new:
217  * @hlines: number of horizontal lines in the grid
218  * @vlines: number of vertical lines in the grid
219  * @color: color of the grid
220  * @size: line width of the grid
221  *
222  * Creates a new #GtkDataboxGrid object which can be added to a #GtkDatabox widget as nice decoration for other graphs.
223  *
224  * Return value: A new #GtkDataboxGrid object
225  **/
226 GtkDataboxGraph *
gtk_databox_grid_new(gint hlines,gint vlines,GdkRGBA * color,guint size)227 gtk_databox_grid_new (gint hlines, gint vlines, GdkRGBA * color, guint size)
228 {
229    GtkDataboxGrid *grid;
230    grid = g_object_new (GTK_DATABOX_TYPE_GRID,
231 			"color", color,
232 			"size", size,
233 			"grid-hlines", hlines, "grid-vlines", vlines, "grid-hline-vals",NULL, "grid-vline-vals", NULL, NULL);
234 
235    return GTK_DATABOX_GRAPH (grid);
236 }
237 
238 /**
239  * gtk_databox_grid_array_new:
240  * @hlines: number of horizontal lines in the grid
241  * @vlines: number of vertical lines in the grid
242  * @hline_vals: a pointer to an array of gfloat horizontal grid coordinate
243  * @vline_vals: a pointer to an array of gfloat vertical grid coordinate
244  * @color: color of the grid
245  * @size: line width of the grid
246  *
247  * Creates a new #GtkDataboxGrid object which can be added to a #GtkDatabox widget as nice decoration for other graphs.
248  *
249  * Return value: A new #GtkDataboxGrid object
250  **/
gtk_databox_grid_array_new(gint hlines,gint vlines,gfloat * local_hline_vals,gfloat * local_vline_vals,GdkRGBA * color,guint size)251 GtkDataboxGraph *gtk_databox_grid_array_new (gint hlines, gint vlines, gfloat * local_hline_vals, gfloat * local_vline_vals,
252 					  GdkRGBA * color, guint size)
253 {
254    GtkDataboxGrid *grid;
255 
256    grid = g_object_new (GTK_DATABOX_TYPE_GRID,
257 			"color", color,
258 			"size", size,
259 			"grid-hlines", hlines, "grid-vlines", vlines, "grid-hline-vals", local_hline_vals, "grid-vline-vals", local_vline_vals, NULL);
260 
261    return GTK_DATABOX_GRAPH (grid);
262 }
263 
264 static void
gtk_databox_grid_real_draw(GtkDataboxGraph * graph,GtkDatabox * box)265 gtk_databox_grid_real_draw (GtkDataboxGraph * graph,
266 			    GtkDatabox* box)
267 {
268    GtkWidget *widget;
269    GtkDataboxGrid *grid = GTK_DATABOX_GRID (graph);
270    GtkDataboxGridPrivate *priv = GTK_DATABOX_GRID_GET_PRIVATE(grid);   gint i = 0;
271    gfloat x;
272    gfloat y;
273    gint16 width;
274    gint16 height;
275    gfloat offset_x;
276    gfloat offset_y;
277    gfloat factor_x;
278    gfloat factor_y;
279    gint16 pixel_x;
280    gint16 pixel_y;
281    gfloat left, right, top, bottom;
282    double pixel_right, pixel_left, pixel_top, pixel_bottom;
283    double target_spacing, grid_spacing;
284    double grid_dot[] = {0.0, 0.0};
285    cairo_t *cr;
286    GtkAllocation allocation;
287 
288    g_return_if_fail (GTK_DATABOX_IS_GRID (grid));
289    g_return_if_fail (GTK_IS_DATABOX (box));
290 
291    widget = GTK_WIDGET(box);
292    gtk_widget_get_allocation(widget, &allocation);
293 
294    gtk_databox_get_total_limits (box, &left, &right, &top, &bottom);
295 
296    cr = gtk_databox_graph_create_gc (graph, box);
297 
298    width = allocation.width;
299    height = allocation.height;
300 
301    offset_x = left;
302    factor_x =
303       (right - left) / (priv->vlines + 1);
304 
305    offset_y = top;
306    factor_y =
307       (bottom - top) / (priv->hlines + 1);
308 
309    /* Cairo accepts spacing of dotted and dashed lines in user-space
310     * coordinates, in our case, pixels, but using floating point
311     * values, and we use this to our advantage!
312     *
313     * For normal size dotted lines, we target a dot every five pixels,
314     * but adjust this so that horizontal and vertical grid lines
315     * always meet at a dot.
316     *
317     * For normal size dashed lines, we target five pixel dashes with
318     * five pixel spaces between them, but adjust this so that grid
319     * crossings always occur in the middle of a dash.
320     *
321     * We widen the target spacing if the line size is wider.
322     *
323     * This doesn't work for custom hline_vals, but we'll always get
324     * something close to five pixels per dot or dash.
325     */
326 
327    pixel_right = gtk_databox_value_to_pixel_x (box, right);
328    pixel_left = gtk_databox_value_to_pixel_x (box, left);
329    grid_spacing = (pixel_right - pixel_left)/(priv->vlines+1);
330    target_spacing = 4.0 + cairo_get_line_width(cr);
331 
332    switch (priv->line_style) {
333    case GTK_DATABOX_GRID_DASHED_LINES:
334      grid_spacing /= 2*round(grid_spacing/target_spacing/2);
335      cairo_set_dash(cr, &grid_spacing, 1, grid_spacing/2);
336      break;
337 
338    case GTK_DATABOX_GRID_DOTTED_LINES:
339      grid_spacing /= round(grid_spacing/target_spacing);
340      grid_dot[1] = grid_spacing;
341      cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
342      cairo_set_dash(cr, grid_dot, 2, 0.0);
343      break;
344 
345    case GTK_DATABOX_GRID_SOLID_LINES:
346      break;
347    }
348 
349    if (priv->hline_vals == NULL)
350       for (i = 0; i < priv->hlines; i++)
351       {
352          y = offset_y + (i + 1) * factor_y;
353          pixel_y = gtk_databox_value_to_pixel_y (box, y);
354          cairo_move_to (cr, 0.0, pixel_y + 0.5);
355          cairo_line_to (cr, width, pixel_y + 0.5);
356       }
357    else
358       for (i = 0; i < priv->hlines; i++)
359       {
360          y = priv->hline_vals[i];
361          pixel_y = gtk_databox_value_to_pixel_y (box, y);
362          cairo_move_to (cr, 0.0, pixel_y + 0.5);
363          cairo_line_to (cr, width, pixel_y + 0.5);
364       }
365 
366    cairo_stroke(cr);
367 
368    pixel_bottom = gtk_databox_value_to_pixel_y (box, bottom);
369    pixel_top = gtk_databox_value_to_pixel_y (box, top);
370    grid_spacing = (pixel_bottom - pixel_top)/(priv->hlines+1);
371 
372    switch (priv->line_style) {
373    case GTK_DATABOX_GRID_DASHED_LINES:
374      grid_spacing /= 2*round(grid_spacing/target_spacing/2);
375      cairo_set_dash(cr, &grid_spacing, 1, grid_spacing/2);
376      break;
377 
378    case GTK_DATABOX_GRID_DOTTED_LINES:
379      grid_spacing /= round(grid_spacing/target_spacing);
380      grid_dot[1] = grid_spacing;
381      cairo_set_dash(cr, grid_dot, 2, 0.0);
382      break;
383 
384    case GTK_DATABOX_GRID_SOLID_LINES:
385      break;
386    }
387 
388    if (priv->vline_vals == NULL)
389       for (i = 0; i < priv->vlines; i++)
390       {
391          x = offset_x + (i + 1) * factor_x;
392          pixel_x = gtk_databox_value_to_pixel_x (box, x);
393          cairo_move_to (cr, pixel_x + 0.5, 0.0);
394          cairo_line_to (cr, pixel_x + 0.5, height);
395       }
396    else
397       for (i = 0; i < priv->vlines; i++)
398       {
399          x = priv->vline_vals[i];
400          pixel_x = gtk_databox_value_to_pixel_x (box, x);
401          cairo_move_to (cr, pixel_x + 0.5, 0);
402          cairo_line_to (cr, pixel_x + 0.5, height);
403       }
404    cairo_stroke(cr);
405    cairo_destroy(cr);
406 
407    return;
408 }
409 
410 /**
411  * gtk_databox_grid_set_hlines:
412  * @grid: a #GtkDataboxGrid graph object
413  * @hlines: number of vertical lines in the grid
414  *
415  * Sets the number of horizontal lines in the @grid.
416  **/
417 void
gtk_databox_grid_set_hlines(GtkDataboxGrid * grid,gint hlines)418 gtk_databox_grid_set_hlines (GtkDataboxGrid * grid, gint hlines)
419 {
420    g_return_if_fail (GTK_DATABOX_IS_GRID (grid));
421 
422    GTK_DATABOX_GRID_GET_PRIVATE(grid)->hlines = MAX (1, hlines);
423 
424    g_object_notify (G_OBJECT (grid), "grid-hlines");
425 }
426 
427 /**
428  * gtk_databox_grid_get_hlines:
429  * @grid: a #GtkDataboxGrid graph object
430  *
431  * Gets the number of horizontal lines in the @grid.
432  *
433  * Return value: Number of horizontal lines in the @grid.
434  **/
435 gint
gtk_databox_grid_get_hlines(GtkDataboxGrid * grid)436 gtk_databox_grid_get_hlines (GtkDataboxGrid * grid)
437 {
438    g_return_val_if_fail (GTK_DATABOX_IS_GRID (grid), -1);
439 
440    return GTK_DATABOX_GRID_GET_PRIVATE(grid)->hlines;
441 }
442 
443 /**
444  * gtk_databox_grid_set_vlines:
445  * @grid: a #GtkDataboxGrid graph object
446  * @vlines: number of vertical lines in the grid
447  *
448  * Sets the number of vertical lines in the @grid.
449  **/
450 void
gtk_databox_grid_set_vlines(GtkDataboxGrid * grid,gint vlines)451 gtk_databox_grid_set_vlines (GtkDataboxGrid * grid, gint vlines)
452 {
453    g_return_if_fail (GTK_DATABOX_IS_GRID (grid));
454 
455    GTK_DATABOX_GRID_GET_PRIVATE(grid)->vlines = MAX (1, vlines);
456 
457    g_object_notify (G_OBJECT (grid), "grid-vlines");
458 }
459 
460 /**
461  * gtk_databox_grid_get_vlines:
462  * @grid: a #GtkDataboxGrid graph object
463  *
464  * Gets the number of vertical lines in the @grid.
465  *
466  * Return value: Number of vertical lines in the @grid.
467  **/
468 gint
gtk_databox_grid_get_vlines(GtkDataboxGrid * grid)469 gtk_databox_grid_get_vlines (GtkDataboxGrid * grid)
470 {
471    g_return_val_if_fail (GTK_DATABOX_IS_GRID (grid), -1);
472 
473    return GTK_DATABOX_GRID_GET_PRIVATE(grid)->vlines;
474 }
475 
476 /**
477  * gtk_databox_grid_set_hline_vals:
478  * @grid: a #GtkDataboxGrid graph object
479  * @hline_vals: sets the pointer to the hline values for the grid
480  *
481  * Sets the pointer to the horizontal lines in the @grid.
482  **/
483 void
gtk_databox_grid_set_hline_vals(GtkDataboxGrid * grid,gfloat * hline_vals)484 gtk_databox_grid_set_hline_vals (GtkDataboxGrid * grid, gfloat *hline_vals)
485 {
486    g_return_if_fail (GTK_DATABOX_IS_GRID (grid));
487 
488    GTK_DATABOX_GRID_GET_PRIVATE(grid)->hline_vals = hline_vals;
489 
490    g_object_notify (G_OBJECT (grid), "grid-hline-vals");
491 }
492 
493 /**
494  * gtk_databox_grid_get_hline_vals:
495  * @grid: a #GtkDataboxGrid graph object
496  *
497  * Gets the pointer to the horizontal line values for the @grid.
498  *
499  * Return value: Pointer to the horizontal line values for the @grid. (or NULL if error)
500  **/
501 gfloat*
gtk_databox_grid_get_hline_vals(GtkDataboxGrid * grid)502 gtk_databox_grid_get_hline_vals (GtkDataboxGrid * grid)
503 {
504    g_return_val_if_fail (GTK_DATABOX_IS_GRID (grid), NULL);
505 
506    return GTK_DATABOX_GRID_GET_PRIVATE(grid)->hline_vals;
507 }
508 
509 /**
510  * gtk_databox_grid_set_vline_vals:
511  * @grid: a #GtkDataboxGrid graph object
512  * @vline_vals: sets the pointer to the vline values for the grid
513  *
514  * Sets the pointer to the vertical lines in the @grid.
515  **/
516 void
gtk_databox_grid_set_vline_vals(GtkDataboxGrid * grid,gfloat * vline_vals)517 gtk_databox_grid_set_vline_vals (GtkDataboxGrid * grid, gfloat *vline_vals)
518 {
519    g_return_if_fail (GTK_DATABOX_IS_GRID (grid));
520 
521    GTK_DATABOX_GRID_GET_PRIVATE(grid)->vline_vals = vline_vals;
522 
523    g_object_notify (G_OBJECT (grid), "grid-vline-vals");
524 }
525 
526 /**
527  * gtk_databox_grid_get_vline_vals:
528  * @grid: a #GtkDataboxGrid graph object
529  *
530  * Gets the pointer to the vertical line values for the @grid.
531  *
532  * Return value: Pointer to the vertical line values for the @grid. (or NULL if error)
533  **/
534 gfloat*
gtk_databox_grid_get_vline_vals(GtkDataboxGrid * grid)535 gtk_databox_grid_get_vline_vals (GtkDataboxGrid * grid)
536 {
537    g_return_val_if_fail (GTK_DATABOX_IS_GRID (grid), NULL);
538 
539    return GTK_DATABOX_GRID_GET_PRIVATE(grid)->vline_vals;
540 }
541 
542 /**
543  * gtk_databox_grid_set_line_style:
544  * @grid: a #GtkDataboxGrid graph object
545  * @line_style: GTK_DATABOX_GRID_DASHED_LINES,
546  *   GTK_DATABOX_GRID_SOLID_LINES, or GTK_DATABOX_GRID_DOTTED_LINES
547  *
548  * Sets the line style to draw the lines in the @grid.
549  **/
550 void
gtk_databox_grid_set_line_style(GtkDataboxGrid * grid,gint line_style)551 gtk_databox_grid_set_line_style (GtkDataboxGrid *grid, gint line_style)
552 {
553      g_return_if_fail (GTK_DATABOX_IS_GRID (grid));
554 
555      GTK_DATABOX_GRID_GET_PRIVATE(grid)->line_style = line_style;
556 
557      g_object_notify (G_OBJECT (grid), "line-style");
558 }
559 
560 /**
561  * gtk_databox_grid_get_line_style:
562  * @grid: a #GtkDataboxGrid graph object
563  *
564  * Retrieve the line style to draw the lines in the @grid.
565  *
566  * Return value: GTK_DATABOX_GRID_DASHED_LINES,
567  *   GTK_DATABOX_GRID_SOLID_LINES, or GTK_DATABOX_GRID_DOTTED_LINES
568  **/
569 gint
gtk_databox_grid_get_line_style(GtkDataboxGrid * grid)570 gtk_databox_grid_get_line_style (GtkDataboxGrid *grid)
571 {
572   g_return_val_if_fail (GTK_DATABOX_IS_GRID (grid), -1);
573 
574   return GTK_DATABOX_GRID_GET_PRIVATE(grid)->line_style;
575 }
576