No issues found
1 /* Generic bezier rect item for GnomeCanvasWidget. Most code taken
2 * from gnome-canvas-bpath but made into a rect item.
3 *
4 * GnomeCanvas is basically a port of the Tk toolkit's most excellent
5 * canvas widget. Tk is copyrighted by the Regents of the University
6 * of California, Sun Microsystems, and other parties.
7 *
8 * Copyright (C) 1998,1999 The Free Software Foundation
9 *
10 * Authors: Federico Mena <federico@nuclecu.unam.mx>
11 * Raph Levien <raph@acm.org>
12 * Lauris Kaplinski <lauris@ximian.com>
13 * Miguel de Icaza <miguel@kernel.org>
14 * Cody Russell <bratsche@gnome.org>
15 * Rusty Conover <rconover@bangtail.net>
16 */
17
18 /* These includes are set up for standalone compile. If/when this codebase
19 * is integrated into libgnomeui, the includes will need to change. */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <math.h>
26 #include <string.h>
27
28 #include <gtk/gtk.h>
29 #include <cairo-gobject.h>
30 #include "gnome-canvas.h"
31 #include "gnome-canvas-util.h"
32
33 #include "gnome-canvas-rect.h"
34
35 #define GNOME_CANVAS_RECT_GET_PRIVATE(obj) \
36 (G_TYPE_INSTANCE_GET_PRIVATE \
37 ((obj), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRectPrivate))
38
39 struct _GnomeCanvasRectPrivate {
40 cairo_path_t *path; /* Our bezier path representation */
41
42 gdouble x1, y1, x2, y2;
43
44 gdouble scale; /* CTM scaling (for pen) */
45
46 guint fill_set : 1; /* Is fill color set? */
47 guint outline_set : 1; /* Is outline color set? */
48
49 gdouble line_width; /* Width of outline, in user coords */
50
51 guint32 fill_rgba; /* Fill color, RGBA */
52 guint32 outline_rgba; /* Outline color, RGBA */
53
54 cairo_line_cap_t cap; /* Cap style for line */
55 cairo_line_join_t join; /* Join style for line */
56 cairo_fill_rule_t wind; /* Winding rule */
57 gdouble miterlimit; /* Miter limit */
58
59 guint n_dash; /* Number of elements in dashing pattern */
60 gdouble *dash; /* Dashing pattern */
61 gdouble dash_offset; /* Dashing offset */
62 };
63
64 enum {
65 PROP_0,
66 PROP_X1,
67 PROP_Y1,
68 PROP_X2,
69 PROP_Y2,
70 PROP_FILL_COLOR,
71 PROP_FILL_COLOR_GDK,
72 PROP_FILL_COLOR_RGBA,
73 PROP_OUTLINE_COLOR,
74 PROP_OUTLINE_COLOR_GDK,
75 PROP_OUTLINE_COLOR_RGBA,
76 PROP_LINE_WIDTH,
77 PROP_CAP_STYLE,
78 PROP_JOIN_STYLE,
79 PROP_WIND,
80 PROP_MITERLIMIT,
81 PROP_DASH
82 };
83
84 static void gnome_canvas_rect_bounds (GnomeCanvasItem *item,
85 gdouble *x1, gdouble *y1, gdouble *x2, gdouble *y2);
86
87 G_DEFINE_TYPE (GnomeCanvasRect, gnome_canvas_rect, GNOME_TYPE_CANVAS_ITEM)
88
89 static guint32
90 get_rgba_from_color (GdkColor *color)
91 {
92 return ((color->red & 0xff00) << 16) |
93 ((color->green & 0xff00) << 8) |
94 (color->blue & 0xff00) | 0xff;
95 }
96
97 static gboolean
98 gnome_canvas_rect_setup_for_fill (GnomeCanvasRect *rect,
99 cairo_t *cr)
100 {
101 if (!rect->priv->fill_set)
102 return FALSE;
103
104 cairo_set_source_rgba (
105 cr,
106 ((rect->priv->fill_rgba >> 24) & 0xff) / 255.0,
107 ((rect->priv->fill_rgba >> 16) & 0xff) / 255.0,
108 ((rect->priv->fill_rgba >> 8) & 0xff) / 255.0,
109 ( rect->priv->fill_rgba & 0xff) / 255.0);
110 cairo_set_fill_rule (cr, rect->priv->wind);
111
112 return TRUE;
113 }
114
115 static gboolean
116 gnome_canvas_rect_setup_for_stroke (GnomeCanvasRect *rect,
117 cairo_t *cr)
118 {
119 if (!rect->priv->outline_set)
120 return FALSE;
121
122 cairo_set_source_rgba (
123 cr,
124 ((rect->priv->outline_rgba >> 24) & 0xff) / 255.0,
125 ((rect->priv->outline_rgba >> 16) & 0xff) / 255.0,
126 ((rect->priv->outline_rgba >> 8) & 0xff) / 255.0,
127 ( rect->priv->outline_rgba & 0xff) / 255.0);
128 cairo_set_line_width (cr, rect->priv->line_width);
129 cairo_set_line_cap (cr, rect->priv->cap);
130 cairo_set_line_join (cr, rect->priv->join);
131 cairo_set_miter_limit (cr, rect->priv->miterlimit);
132 cairo_set_dash (
133 cr, rect->priv->dash, rect->priv->n_dash,
134 rect->priv->dash_offset);
135
136 return TRUE;
137 }
138
139 static void
140 gnome_canvas_rect_set_property (GObject *object,
141 guint property_id,
142 const GValue *value,
143 GParamSpec *pspec)
144 {
145 GnomeCanvasItem *item;
146 GnomeCanvasRect *rect;
147 GnomeCanvasRectPrivate *priv;
148 GdkColor color;
149 GdkColor *colorptr;
150 const gchar *color_string;
151
152 item = GNOME_CANVAS_ITEM (object);
153 rect = GNOME_CANVAS_RECT (object);
154 priv = rect->priv;
155
156 switch (property_id) {
157 case PROP_X1:
158 priv->x1 = g_value_get_double (value);
159 gnome_canvas_item_request_update (item);
160 break;
161
162 case PROP_Y1:
163 priv->y1 = g_value_get_double (value);
164 gnome_canvas_item_request_update (item);
165 break;
166
167 case PROP_X2:
168 priv->x2 = g_value_get_double (value);
169 gnome_canvas_item_request_update (item);
170 break;
171
172 case PROP_Y2:
173 priv->y2 = g_value_get_double (value);
174 gnome_canvas_item_request_update (item);
175 break;
176
177 case PROP_FILL_COLOR:
178 color_string = g_value_get_string (value);
179 if (color_string != NULL) {
180 if (!gdk_color_parse (color_string, &color)) {
181 g_warning (
182 "Failed to parse color '%s'",
183 color_string);
184 break;
185 }
186 priv->fill_set = TRUE;
187 priv->fill_rgba = get_rgba_from_color (&color);
188 } else if (priv->fill_set)
189 priv->fill_set = FALSE;
190 else
191 break;
192
193 gnome_canvas_item_request_update (item);
194 break;
195
196 case PROP_FILL_COLOR_GDK:
197 colorptr = g_value_get_boxed (value);
198 if (colorptr != NULL) {
199 priv->fill_set = TRUE;
200 priv->fill_rgba = get_rgba_from_color (colorptr);
201 } else if (priv->fill_set)
202 priv->fill_set = FALSE;
203 else
204 break;
205
206 gnome_canvas_item_request_update (item);
207 break;
208
209 case PROP_FILL_COLOR_RGBA:
210 priv->fill_set = TRUE;
211 priv->fill_rgba = g_value_get_uint (value);
212
213 gnome_canvas_item_request_update (item);
214 break;
215
216 case PROP_OUTLINE_COLOR:
217 color_string = g_value_get_string (value);
218 if (color_string != NULL) {
219 if (!gdk_color_parse (color_string, &color)) {
220 g_warning (
221 "Failed to parse color '%s'",
222 color_string);
223 break;
224 }
225 priv->outline_set = TRUE;
226 priv->outline_rgba = get_rgba_from_color (&color);
227 } else if (priv->outline_set)
228 priv->outline_set = FALSE;
229 else
230 break;
231
232 gnome_canvas_item_request_update (item);
233 break;
234
235 case PROP_OUTLINE_COLOR_GDK:
236 colorptr = g_value_get_boxed (value);
237 if (colorptr != NULL) {
238 priv->outline_set = TRUE;
239 priv->outline_rgba = get_rgba_from_color (colorptr);
240 } else if (priv->outline_set)
241 priv->outline_set = FALSE;
242 else
243 break;
244
245 gnome_canvas_item_request_update (item);
246 break;
247
248 case PROP_OUTLINE_COLOR_RGBA:
249 priv->outline_set = TRUE;
250 priv->outline_rgba = g_value_get_uint (value);
251
252 gnome_canvas_item_request_update (item);
253 break;
254
255 case PROP_LINE_WIDTH:
256 priv->line_width = g_value_get_double (value);
257
258 gnome_canvas_item_request_update (item);
259 break;
260
261 case PROP_WIND:
262 priv->wind = g_value_get_enum (value);
263 gnome_canvas_item_request_update (item);
264 break;
265
266 case PROP_CAP_STYLE:
267 priv->cap = g_value_get_enum (value);
268 gnome_canvas_item_request_update (item);
269 break;
270
271 case PROP_JOIN_STYLE:
272 priv->join = g_value_get_enum (value);
273 gnome_canvas_item_request_update (item);
274 break;
275
276 case PROP_MITERLIMIT:
277 priv->miterlimit = g_value_get_double (value);
278 gnome_canvas_item_request_update (item);
279 break;
280
281 case PROP_DASH:
282 /* XXX */
283 g_warn_if_reached ();
284 break;
285
286 default:
287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
288 break;
289 }
290 }
291
292 static void
293 gnome_canvas_rect_get_property (GObject *object,
294 guint property_id,
295 GValue *value,
296 GParamSpec *pspec)
297 {
298 GnomeCanvasRect *rect = GNOME_CANVAS_RECT (object);
299 GnomeCanvasRectPrivate *priv = rect->priv;
300
301 switch (property_id) {
302
303 case PROP_X1:
304 g_value_set_double (value, priv->x1);
305 break;
306
307 case PROP_Y1:
308 g_value_set_double (value, priv->y1);
309 break;
310
311 case PROP_X2:
312 g_value_set_double (value, priv->x2);
313 break;
314
315 case PROP_Y2:
316 g_value_set_double (value, priv->y2);
317 break;
318
319 case PROP_FILL_COLOR_RGBA:
320 g_value_set_uint (value, priv->fill_rgba);
321 break;
322
323 case PROP_OUTLINE_COLOR_RGBA:
324 g_value_set_uint (value, priv->outline_rgba);
325 break;
326
327 case PROP_WIND:
328 g_value_set_uint (value, priv->wind);
329 break;
330
331 case PROP_CAP_STYLE:
332 g_value_set_enum (value, priv->cap);
333 break;
334
335 case PROP_JOIN_STYLE:
336 g_value_set_enum (value, priv->join);
337 break;
338
339 case PROP_LINE_WIDTH:
340 g_value_set_double (value, priv->line_width);
341 break;
342
343 case PROP_MITERLIMIT:
344 g_value_set_double (value, priv->miterlimit);
345 break;
346
347 case PROP_DASH:
348 /* XXX */
349 g_warn_if_reached ();
350 break;
351
352 default:
353 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
354 break;
355 }
356 }
357
358 static void
359 gnome_canvas_rect_dispose (GnomeCanvasItem *object)
360 {
361 GnomeCanvasRect *rect;
362
363 g_return_if_fail (GNOME_IS_CANVAS_RECT (object));
364
365 rect = GNOME_CANVAS_RECT (object);
366
367 if (rect->priv->path != NULL) {
368 cairo_path_destroy (rect->priv->path);
369 rect->priv->path = NULL;
370 }
371
372 g_free (rect->priv->dash);
373 rect->priv->dash = NULL;
374
375 if (GNOME_CANVAS_ITEM_CLASS (gnome_canvas_rect_parent_class)->dispose)
376 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_rect_parent_class)->dispose (object);
377 }
378
379 static void
380 gnome_canvas_rect_update (GnomeCanvasItem *item,
381 const cairo_matrix_t *i2c,
382 gint flags)
383 {
384 gdouble x1, x2, y1, y2;
385
386 GNOME_CANVAS_ITEM_CLASS (gnome_canvas_rect_parent_class)->
387 update (item, i2c, flags);
388
389 gnome_canvas_rect_bounds (item, &x1, &y1, &x2, &y2);
390 gnome_canvas_matrix_transform_rect (i2c, &x1, &y1, &x2, &y2);
391
392 gnome_canvas_update_bbox (
393 item, floor (x1), floor (y1), ceil (x2), ceil (y2));
394 }
395
396 static void
397 gnome_canvas_rect_draw (GnomeCanvasItem *item,
398 cairo_t *cr,
399 gint x,
400 gint y,
401 gint width,
402 gint height)
403 {
404 GnomeCanvasRect *rect;
405 cairo_matrix_t matrix;
406
407 rect = GNOME_CANVAS_RECT (item);
408
409 cairo_save (cr);
410
411 gnome_canvas_item_i2c_matrix (item, &matrix);
412 cairo_transform (cr, &matrix);
413
414 if (gnome_canvas_rect_setup_for_fill (rect, cr)) {
415 cairo_rectangle (
416 cr,
417 rect->priv->x1 - x,
418 rect->priv->y1 - y,
419 rect->priv->x2 - rect->priv->x1,
420 rect->priv->y2 - rect->priv->y1);
421 cairo_fill (cr);
422 }
423
424 if (gnome_canvas_rect_setup_for_stroke (rect, cr)) {
425 cairo_rectangle (
426 cr,
427 rect->priv->x1 - x,
428 rect->priv->y1 - y,
429 rect->priv->x2 - rect->priv->x1,
430 rect->priv->y2 - rect->priv->y1);
431 cairo_stroke (cr);
432 }
433
434 cairo_restore (cr);
435 }
436
437 static GnomeCanvasItem *
438 gnome_canvas_rect_point (GnomeCanvasItem *item,
439 gdouble x,
440 gdouble y,
441 gint cx,
442 gint cy)
443 {
444 GnomeCanvasRect *rect;
445 cairo_t *cr;
446
447 rect = GNOME_CANVAS_RECT (item);
448
449 cr = gnome_canvas_cairo_create_scratch ();
450
451 cairo_rectangle (
452 cr,
453 rect->priv->x1,
454 rect->priv->y1,
455 rect->priv->x2 - rect->priv->x1,
456 rect->priv->y2 - rect->priv->y1);
457
458 if (gnome_canvas_rect_setup_for_fill (rect, cr) &&
459 cairo_in_fill (cr, x, y)) {
460 cairo_destroy (cr);
461 return item;
462 }
463
464 if (gnome_canvas_rect_setup_for_stroke (rect, cr) &&
465 cairo_in_stroke (cr, x, y)) {
466 cairo_destroy (cr);
467 return item;
468 }
469
470 cairo_destroy (cr);
471
472 return NULL;
473 }
474
475 static void
476 gnome_canvas_rect_bounds (GnomeCanvasItem *item,
477 gdouble *x1,
478 gdouble *y1,
479 gdouble *x2,
480 gdouble *y2)
481 {
482 GnomeCanvasRect *rect;
483 cairo_t *cr;
484
485 rect = GNOME_CANVAS_RECT (item);
486
487 cr = gnome_canvas_cairo_create_scratch ();
488
489 cairo_rectangle (
490 cr,
491 rect->priv->x1,
492 rect->priv->y1,
493 rect->priv->x2 - rect->priv->x1,
494 rect->priv->y2 - rect->priv->y1);
495
496 if (gnome_canvas_rect_setup_for_stroke (rect, cr))
497 cairo_stroke_extents (cr, x1, y1, x2, y2);
498 else if (gnome_canvas_rect_setup_for_fill (rect, cr))
499 cairo_fill_extents (cr, x1, y1, x2, y2);
500 else {
501 *x1 = *x2 = *y1 = *y2 = 0;
502 }
503
504 cairo_destroy (cr);
505 }
506
507 static void
508 gnome_canvas_rect_class_init (GnomeCanvasRectClass *class)
509 {
510 GObjectClass *object_class;
511 GnomeCanvasItemClass *item_class;
512
513 g_type_class_add_private (class, sizeof (GnomeCanvasRectPrivate));
514
515 object_class = G_OBJECT_CLASS (class);
516 object_class->set_property = gnome_canvas_rect_set_property;
517 object_class->get_property = gnome_canvas_rect_get_property;
518
519 item_class = GNOME_CANVAS_ITEM_CLASS (class);
520 item_class->dispose = gnome_canvas_rect_dispose;
521 item_class->update = gnome_canvas_rect_update;
522 item_class->draw = gnome_canvas_rect_draw;
523 item_class->point = gnome_canvas_rect_point;
524 item_class->bounds = gnome_canvas_rect_bounds;
525
526 g_object_class_install_property (
527 object_class,
528 PROP_X1,
529 g_param_spec_double (
530 "x1",
531 NULL,
532 NULL,
533 -G_MAXDOUBLE,
534 G_MAXDOUBLE,
535 0,
536 G_PARAM_READWRITE));
537
538 g_object_class_install_property (
539 object_class,
540 PROP_Y1,
541 g_param_spec_double (
542 "y1",
543 NULL,
544 NULL,
545 -G_MAXDOUBLE,
546 G_MAXDOUBLE,
547 0,
548 G_PARAM_READWRITE));
549
550 g_object_class_install_property (
551 object_class,
552 PROP_X2,
553 g_param_spec_double (
554 "x2",
555 NULL,
556 NULL,
557 -G_MAXDOUBLE,
558 G_MAXDOUBLE,
559 0,
560 G_PARAM_READWRITE));
561
562 g_object_class_install_property (
563 object_class,
564 PROP_Y2,
565 g_param_spec_double (
566 "y2",
567 NULL,
568 NULL,
569 -G_MAXDOUBLE,
570 G_MAXDOUBLE,
571 0,
572 G_PARAM_READWRITE));
573
574 g_object_class_install_property (
575 object_class,
576 PROP_FILL_COLOR,
577 g_param_spec_string (
578 "fill_color",
579 NULL,
580 NULL,
581 NULL,
582 G_PARAM_WRITABLE));
583
584 g_object_class_install_property (
585 object_class,
586 PROP_FILL_COLOR_GDK,
587 g_param_spec_boxed (
588 "fill_color_gdk",
589 NULL,
590 NULL,
591 GDK_TYPE_COLOR,
592 G_PARAM_WRITABLE));
593
594 g_object_class_install_property (
595 object_class,
596 PROP_FILL_COLOR_RGBA,
597 g_param_spec_uint (
598 "fill_rgba",
599 NULL,
600 NULL,
601 0,
602 G_MAXUINT,
603 0,
604 G_PARAM_READWRITE));
605
606 g_object_class_install_property (
607 object_class,
608 PROP_OUTLINE_COLOR,
609 g_param_spec_string (
610 "outline_color",
611 NULL,
612 NULL,
613 NULL,
614 G_PARAM_WRITABLE));
615
616 g_object_class_install_property (
617 object_class,
618 PROP_OUTLINE_COLOR_GDK,
619 g_param_spec_boxed (
620 "outline_color_gdk",
621 NULL,
622 NULL,
623 GDK_TYPE_COLOR,
624 G_PARAM_WRITABLE));
625
626 g_object_class_install_property (
627 object_class,
628 PROP_OUTLINE_COLOR_RGBA,
629 g_param_spec_uint (
630 "outline_rgba",
631 NULL,
632 NULL,
633 0,
634 G_MAXUINT,
635 0,
636 G_PARAM_READWRITE));
637
638 g_object_class_install_property (
639 object_class,
640 PROP_LINE_WIDTH,
641 g_param_spec_double (
642 "line_width",
643 NULL,
644 NULL,
645 0.0,
646 G_MAXDOUBLE,
647 1.0,
648 G_PARAM_READWRITE));
649
650 g_object_class_install_property (
651 object_class,
652 PROP_CAP_STYLE,
653 g_param_spec_enum (
654 "cap_style",
655 NULL,
656 NULL,
657 CAIRO_GOBJECT_TYPE_LINE_CAP,
658 CAIRO_LINE_CAP_BUTT,
659 G_PARAM_READWRITE));
660
661 g_object_class_install_property (
662 object_class,
663 PROP_JOIN_STYLE,
664 g_param_spec_enum (
665 "join_style",
666 NULL,
667 NULL,
668 CAIRO_GOBJECT_TYPE_LINE_JOIN,
669 CAIRO_LINE_JOIN_MITER,
670 G_PARAM_READWRITE));
671
672 g_object_class_install_property (
673 object_class,
674 PROP_WIND,
675 g_param_spec_enum (
676 "wind",
677 NULL,
678 NULL,
679 CAIRO_GOBJECT_TYPE_FILL_RULE,
680 CAIRO_FILL_RULE_EVEN_ODD,
681 G_PARAM_READWRITE));
682
683 g_object_class_install_property (
684 object_class,
685 PROP_MITERLIMIT,
686 g_param_spec_double (
687 "miterlimit",
688 NULL,
689 NULL,
690 0.0,
691 G_MAXDOUBLE,
692 10.43,
693 G_PARAM_READWRITE));
694
695 #if 0
696 /* XXX: Find a good way to pass dash properties in a property */
697 g_object_class_install_property (
698 object_class,
699 PROP_DASH,
700 g_param_spec_pointer (
701 "dash",
702 NULL,
703 NULL,
704 G_PARAM_READWRITE));
705 #endif
706 }
707
708 static void
709 gnome_canvas_rect_init (GnomeCanvasRect *rect)
710 {
711 rect->priv = GNOME_CANVAS_RECT_GET_PRIVATE (rect);
712
713 rect->priv->scale = 1.0;
714
715 rect->priv->fill_set = FALSE;
716 rect->priv->outline_set = FALSE;
717
718 rect->priv->line_width = 1.0;
719
720 rect->priv->fill_rgba = 0x0000003f;
721 rect->priv->outline_rgba = 0x0000007f;
722
723 rect->priv->cap = CAIRO_LINE_CAP_BUTT;
724 rect->priv->join = CAIRO_LINE_JOIN_MITER;
725 rect->priv->wind = CAIRO_FILL_RULE_EVEN_ODD;
726 rect->priv->miterlimit = 10.43; /* X11 default */
727
728 rect->priv->n_dash = 0;
729 rect->priv->dash = NULL;
730 }