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