1 /*
2  * GooCanvas Demo. Copyright (C) 2007 Damon Chaplin.
3  * Released under the GNU LGPL license. See COPYING for details.
4  *
5  * demo-large-rect.c - a demo item that exceeds the cairo 16-bit size limit.
6  *                     Note that it doesn't support rotations or shears.
7  */
8 #include <config.h>
9 #include "goocanvas.h"
10 #include "demo-large-rect.h"
11 
12 
13 /* Use the GLib convenience macro to define the type. GooDemoLargeRect is the
14    class struct, goo_demo_large_rect is the function prefix, and our class is a
15    subclass of GOO_TYPE_CANVAS_ITEM_SIMPLE. */
G_DEFINE_TYPE(GooDemoLargeRect,goo_demo_large_rect,GOO_TYPE_CANVAS_ITEM_SIMPLE)16 G_DEFINE_TYPE (GooDemoLargeRect, goo_demo_large_rect,
17 	       GOO_TYPE_CANVAS_ITEM_SIMPLE)
18 
19 
20 /* The standard object initialization function. */
21 static void
22 goo_demo_large_rect_init (GooDemoLargeRect *demo_large_rect)
23 {
24   demo_large_rect->x = 0.0;
25   demo_large_rect->y = 0.0;
26   demo_large_rect->width = 0.0;
27   demo_large_rect->height = 0.0;
28 }
29 
30 
31 /* The convenience function to create new items. This should start with a
32    parent argument and end with a variable list of object properties to fit
33    in with the standard canvas items. */
34 GooCanvasItem*
goo_demo_large_rect_new(GooCanvasItem * parent,gdouble x,gdouble y,gdouble width,gdouble height,...)35 goo_demo_large_rect_new (GooCanvasItem      *parent,
36 			 gdouble             x,
37 			 gdouble             y,
38 			 gdouble             width,
39 			 gdouble             height,
40 			 ...)
41 {
42   GooCanvasItem *item;
43   GooDemoLargeRect *demo_large_rect;
44   const char *first_property;
45   va_list var_args;
46 
47   item = g_object_new (GOO_TYPE_DEMO_LARGE_RECT, NULL);
48 
49   demo_large_rect = (GooDemoLargeRect*) item;
50   demo_large_rect->x = x;
51   demo_large_rect->y = y;
52   demo_large_rect->width = width;
53   demo_large_rect->height = height;
54 
55   va_start (var_args, height);
56   first_property = va_arg (var_args, char*);
57   if (first_property)
58     g_object_set_valist ((GObject*) item, first_property, var_args);
59   va_end (var_args);
60 
61   if (parent)
62     {
63       goo_canvas_item_add_child (parent, item, -1);
64       g_object_unref (item);
65     }
66 
67   return item;
68 }
69 
70 
71 /* The update method. This is called when the canvas is initially shown and
72    also whenever the object is updated and needs to change its size and/or
73    shape. It should calculate its new bounds in its own coordinate space,
74    storing them in simple->bounds. */
75 static void
goo_demo_large_rect_update(GooCanvasItemSimple * simple,cairo_t * cr)76 goo_demo_large_rect_update  (GooCanvasItemSimple *simple,
77 			     cairo_t             *cr)
78 {
79   GooDemoLargeRect *item = (GooDemoLargeRect*) simple;
80   gdouble half_line_width;
81 
82   /* We can quickly compute the bounds as being just the rectangle's size
83      plus half the line width around each edge. */
84   half_line_width = goo_canvas_item_simple_get_line_width (simple) / 2;
85 
86   simple->bounds.x1 = item->x - half_line_width;
87   simple->bounds.y1 = item->y - half_line_width;
88   simple->bounds.x2 = item->x + item->width + half_line_width;
89   simple->bounds.y2 = item->y + item->height + half_line_width;
90 }
91 
92 
93 static void
create_large_rect_path(GooDemoLargeRect * rect,cairo_t * cr,const GooCanvasBounds * bounds,gdouble line_width,gdouble x,gdouble y,gdouble width,gdouble height)94 create_large_rect_path (GooDemoLargeRect      *rect,
95 			cairo_t               *cr,
96 			const GooCanvasBounds *bounds,
97 			gdouble                line_width,
98 			gdouble                x,
99 			gdouble                y,
100 			gdouble                width,
101 			gdouble                height)
102 {
103   GooCanvasItem *item = (GooCanvasItem*) rect;
104   GooCanvasItemSimple *simple = (GooCanvasItemSimple*) rect;
105   GooCanvas *canvas = simple->canvas;
106   GooCanvasBounds tmp_bounds = *bounds;
107   gdouble x1 = x, y1 = y, x2 = x + width, y2 = y + height;
108 
109   /* Transform the coordinates to the canvas device space, so we can clamp
110      the line to the bounds to be painted. */
111   goo_canvas_convert_from_item_space (canvas, item, &x1, &y1);
112   goo_canvas_convert_from_item_space (canvas, item, &x2, &y2);
113 
114   /* Extend the bounds a bit to account for the line width, so when we clamp
115      the rect to the bounds the outside edges aren't visible. */
116   tmp_bounds.x1 -= line_width;
117   tmp_bounds.y1 -= line_width;
118   tmp_bounds.x2 += line_width;
119   tmp_bounds.y2 += line_width;
120 
121   /* If the rect is completely outside the bounds just return. */
122   if (x1 > tmp_bounds.x2 || x2 < tmp_bounds.x1
123       || y1 > tmp_bounds.y2 || y2 < tmp_bounds.y1)
124     return;
125 
126   /* Clamp the rect to the bounds. */
127   if (x1 < tmp_bounds.x1)
128     x1 = tmp_bounds.x1;
129   if (x2 > tmp_bounds.x2)
130     x2 = tmp_bounds.x2;
131 
132   if (y1 < tmp_bounds.y1)
133     y1 = tmp_bounds.y1;
134   if (y2 > tmp_bounds.y2)
135     y2 = tmp_bounds.y2;
136 
137   /* Convert back to item space. */
138   goo_canvas_convert_to_item_space (canvas, item, &x1, &y1);
139   goo_canvas_convert_to_item_space (canvas, item, &x2, &y2);
140 
141   /* Create the path. */
142   cairo_move_to (cr, x1, y1);
143   cairo_line_to (cr, x2, y1);
144   cairo_line_to (cr, x2, y2);
145   cairo_line_to (cr, x1, y2);
146   cairo_close_path (cr);
147 }
148 
149 
150 /* The paint method. This should draw the item on the given cairo_t, using
151    the item's own coordinate space. */
152 static void
goo_demo_large_rect_paint(GooCanvasItemSimple * simple,cairo_t * cr,const GooCanvasBounds * bounds)153 goo_demo_large_rect_paint (GooCanvasItemSimple   *simple,
154 			   cairo_t               *cr,
155 			   const GooCanvasBounds *bounds)
156 {
157   GooDemoLargeRect *item = (GooDemoLargeRect*) simple;
158   gdouble line_width;
159 
160   line_width = goo_canvas_item_simple_get_line_width (simple);
161   create_large_rect_path (item, cr, bounds, line_width,
162 			  item->x, item->y, item->width, item->height);
163   goo_canvas_item_simple_paint_path (simple, cr);
164 }
165 
166 
167 /* Hit detection. This should check if the given coordinate (in the item's
168    coordinate space) is within the item. If it is it should return TRUE,
169    otherwise it should return FALSE. */
170 static gboolean
goo_demo_large_rect_is_item_at(GooCanvasItemSimple * simple,gdouble x,gdouble y,cairo_t * cr,gboolean is_pointer_event)171 goo_demo_large_rect_is_item_at (GooCanvasItemSimple *simple,
172 				gdouble              x,
173 				gdouble              y,
174 				cairo_t             *cr,
175 				gboolean             is_pointer_event)
176 {
177   GooDemoLargeRect *item = (GooDemoLargeRect*) simple;
178   gdouble half_line_width;
179 
180   /* We assume the item covers its rectangle + line widths. */
181   half_line_width = goo_canvas_item_simple_get_line_width (simple) / 2;
182 
183   if ((x < item->x - half_line_width)
184       || (x > item->x + item->width + half_line_width)
185       || (y < item->y - half_line_width)
186       || (y > item->y + item->height + half_line_width))
187     return FALSE;
188 
189   return TRUE;
190 }
191 
192 
193 /* The class initialization function. Here we set the class' update(), paint()
194    and is_item_at() methods. */
195 static void
goo_demo_large_rect_class_init(GooDemoLargeRectClass * klass)196 goo_demo_large_rect_class_init (GooDemoLargeRectClass *klass)
197 {
198   GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass;
199 
200   simple_class->simple_update        = goo_demo_large_rect_update;
201   simple_class->simple_paint         = goo_demo_large_rect_paint;
202   simple_class->simple_is_item_at    = goo_demo_large_rect_is_item_at;
203 }
204 
205 
206