1 /*
2  * goc-rectangle.c:
3  *
4  * Copyright (C) 2008-2009 Jean Brefort (jean.brefort@normalesup.org)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) version 3.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21 
22 #include <goffice/goffice-config.h>
23 #include <goffice/goffice.h>
24 
25 #include <glib/gi18n-lib.h>
26 #include <gsf/gsf-impl-utils.h>
27 
28 /**
29  * SECTION:goc-rectangle
30  * @short_description: Rectangle.
31  *
32  * #GocPolygon implements rectangle drawing in the canvas.
33 **/
34 
35 static GocItemClass *parent_klass;
36 
37 enum {
38 	RECT_PROP_0,
39 	RECT_PROP_X,
40 	RECT_PROP_Y,
41 	RECT_PROP_W,
42 	RECT_PROP_H,
43 	RECT_PROP_ROTATION,
44 	RECT_PROP_RX,
45 	RECT_PROP_RY,
46 	RECT_PROP_TYPE,
47 };
48 
49 static void
goc_rectangle_set_property(GObject * gobject,guint param_id,GValue const * value,GParamSpec * pspec)50 goc_rectangle_set_property (GObject *gobject, guint param_id,
51 				    GValue const *value, GParamSpec *pspec)
52 {
53 	GocRectangle *rect = GOC_RECTANGLE (gobject);
54 
55 	switch (param_id) {
56 	case RECT_PROP_X:
57 		rect->x = g_value_get_double (value);
58 		break;
59 
60 	case RECT_PROP_Y:
61 		rect->y = g_value_get_double (value);
62 		break;
63 
64 	case RECT_PROP_W:
65 		rect->width = g_value_get_double (value);
66 		break;
67 
68 	case RECT_PROP_H:
69 		rect->height = g_value_get_double (value);
70 		break;
71 
72 	case RECT_PROP_ROTATION:
73 		rect->rotation = g_value_get_double (value);
74 		break;
75 
76 	case RECT_PROP_RX:
77 		rect->rx = g_value_get_double (value);
78 		break;
79 
80 	case RECT_PROP_RY:
81 		rect->ry = g_value_get_double (value);
82 		break;
83 
84 	case RECT_PROP_TYPE:
85 		rect->type = g_value_get_int (value);
86 		break;
87 
88 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
89 		return; /* NOTE : RETURN */
90 	}
91 	goc_item_bounds_changed (GOC_ITEM (gobject));
92 
93 }
94 
95 static void
goc_rectangle_get_property(GObject * gobject,guint param_id,GValue * value,GParamSpec * pspec)96 goc_rectangle_get_property (GObject *gobject, guint param_id,
97 				    GValue *value, GParamSpec *pspec)
98 {
99 	GocRectangle *rect = GOC_RECTANGLE (gobject);
100 
101 	switch (param_id) {
102 	case RECT_PROP_X:
103 		g_value_set_double (value, rect->x);
104 		break;
105 
106 	case RECT_PROP_Y:
107 		g_value_set_double (value, rect->y);
108 		break;
109 
110 	case RECT_PROP_W:
111 		g_value_set_double (value, rect->width);
112 		break;
113 
114 	case RECT_PROP_H:
115 		g_value_set_double (value, rect->height);
116 		break;
117 
118 	case RECT_PROP_ROTATION:
119 		g_value_set_double (value, rect->rotation);
120 		break;
121 
122 	case RECT_PROP_RX:
123 		g_value_set_double (value, rect->rx);
124 		break;
125 
126 	case RECT_PROP_RY:
127 		g_value_set_double (value, rect->ry);
128 		break;
129 
130 	case RECT_PROP_TYPE:
131 		g_value_set_int (value, rect->type);
132 		break;
133 
134 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
135 		return; /* NOTE : RETURN */
136 	}
137 }
138 
139 static gboolean
goc_rectangle_prepare_draw(GocItem const * item,cairo_t * cr,gboolean flag)140 goc_rectangle_prepare_draw (GocItem const *item, cairo_t *cr, gboolean flag)
141 {
142 	GocRectangle *rect = GOC_RECTANGLE (item);
143 	double sign = (item->canvas && goc_canvas_get_direction (item->canvas) == GOC_DIRECTION_RTL)? -1.: 1.;
144 
145 	if (0 == rect->width && 0 == rect->height)
146 		return FALSE;
147 
148 	_goc_item_transform (item, cr, flag);
149 	if (1 == flag) {
150 		goc_group_cairo_transform (item->parent, cr, rect->x, rect->y);
151 		cairo_scale (cr, sign, 1.);
152 	} else {
153 		cairo_translate (cr, rect->x, rect->y);
154 	}
155 	cairo_rotate (cr, rect->rotation * sign);
156 	if (0 == rect->type || 0 == rect->rx || 0 == rect->ry) {
157 		cairo_rectangle (cr, 0., 0., (int) rect->width, (int) rect->height);
158 	} else {
159 
160 		if (rect->type & 1) {
161 			cairo_move_to (cr, rect->rx, 0.);
162 			cairo_save (cr);
163 			cairo_translate (cr, rect->rx, rect->ry);
164 			cairo_scale (cr, rect->rx, rect->ry);
165 			cairo_arc_negative (cr, 0. , 0. ,1. , -M_PI/2. , M_PI);
166 			cairo_restore (cr);
167 		} else {
168 			cairo_move_to (cr, 0., 0.);
169 		}
170 
171 		if (rect->type & 8) {
172 			cairo_line_to (cr, 0., rect->height - rect->ry);
173 			cairo_save (cr);
174 			cairo_translate (cr, rect->rx, rect->height - rect->ry);
175 			cairo_scale (cr, rect->rx, rect->ry);
176 			cairo_arc_negative (cr, 0., 0. ,1. , M_PI, M_PI/2.);
177 			cairo_restore (cr);
178 		} else {
179 			cairo_line_to (cr, 0., rect->height);
180 		}
181 
182 		if (rect->type & 4) {
183 			cairo_line_to (cr, rect->width - rect->rx, rect->height);
184 			cairo_save (cr);
185 			cairo_translate (cr, rect->width - rect->rx, rect->height - rect->ry);
186 			cairo_scale (cr, rect->rx, rect->ry);
187 			cairo_arc_negative (cr, 0., 0. ,1. , M_PI/2., 0.);
188 			cairo_restore (cr);
189 		} else {
190 			cairo_line_to (cr, rect->width, rect->height);
191 		}
192 
193 		if (rect->type & 2) {
194 			cairo_line_to (cr, rect->width, rect->ry);
195 			cairo_save (cr);
196 			cairo_translate (cr, rect->width - rect->rx, rect->ry);
197 			cairo_scale (cr, rect->rx, rect->ry);
198 			cairo_arc_negative (cr, 0., 0. ,1. , 0., -M_PI/2.);
199 			cairo_restore (cr);
200 		} else {
201 			cairo_line_to (cr, rect->width, 0.);
202 		}
203 		cairo_close_path (cr);
204 	}
205 	return TRUE;
206 }
207 
208 static void
goc_rectangle_update_bounds(GocItem * item)209 goc_rectangle_update_bounds (GocItem *item)
210 {
211 	cairo_surface_t *surface;
212 	cairo_t *cr;
213 
214 	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1 , 1);
215 	cr = cairo_create (surface);
216 
217 	cairo_save (cr);
218 	if (goc_rectangle_prepare_draw (item, cr, 0)) {
219 		cairo_restore (cr);
220 		if (go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr))
221 			cairo_stroke_extents (cr, &item->x0, &item->y0, &item->x1, &item->y1);
222 		else if (go_style_is_fill_visible (go_styled_object_get_style (GO_STYLED_OBJECT (item))))
223 			cairo_fill_extents (cr, &item->x0, &item->y0, &item->x1, &item->y1);
224 		else {
225 			item->x0 = item->y0 = G_MAXDOUBLE;
226 			item->x1 = item->y1 = -G_MAXDOUBLE;
227 		}
228 	}
229 
230 	cairo_destroy (cr);
231 	cairo_surface_destroy (surface);
232 }
233 
234 static double
goc_rectangle_distance(GocItem * item,double x,double y,GocItem ** near_item)235 goc_rectangle_distance (GocItem *item, double x, double y, GocItem **near_item)
236 {
237 	GocRectangle *rect = GOC_RECTANGLE (item);
238 	GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (item));
239 	double tmp_width = 0;
240 	double res = 20;
241 	double ppu = goc_canvas_get_pixels_per_unit (item->canvas);
242 	cairo_surface_t *surface;
243 	cairo_t *cr;
244 
245 	if (0 == rect->width && 0 == rect->height)
246 		return res;
247 
248 	*near_item = item;
249 	tmp_width = style->line.width;
250 	if (style->line.width * ppu < 5)
251 		style->line.width = 5. / (ppu * ppu);
252 	else
253 		style->line.width /= ppu;
254 	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
255 	cr = cairo_create (surface);
256 
257 	if (goc_rectangle_prepare_draw (item, cr, 0)) {
258 		/* Filled OR both fill and stroke are none */
259 		if (style->fill.type != GO_STYLE_FILL_NONE ||
260 			(style->fill.type == GO_STYLE_FILL_NONE && !goc_styled_item_set_cairo_line (GOC_STYLED_ITEM (item), cr))) {
261 			if (cairo_in_fill (cr, x, y))
262 				res = 0;
263 		}
264 		if (goc_styled_item_set_cairo_line (GOC_STYLED_ITEM (item), cr) && cairo_in_stroke (cr, x, y)) {
265 			res = 0;
266 		}
267 	}
268 
269 	cairo_destroy (cr);
270 	cairo_surface_destroy (surface);
271 	style->line.width = tmp_width;
272 	return res;
273 }
274 
275 static void
goc_rectangle_draw(GocItem const * item,cairo_t * cr)276 goc_rectangle_draw (GocItem const *item, cairo_t *cr)
277 {
278 	gboolean scale_line_width = goc_styled_item_get_scale_line_width (GOC_STYLED_ITEM (item));
279 	cairo_save(cr);
280 	if (goc_rectangle_prepare_draw (item, cr, 1)) {
281 		go_styled_object_fill (GO_STYLED_OBJECT (item), cr, TRUE);
282 		if (!scale_line_width)
283 			cairo_restore (cr);
284 		if (go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr)) {
285 			cairo_stroke (cr);
286 		} else {
287 			cairo_new_path (cr);
288 		}
289 		if (scale_line_width)
290 			cairo_restore (cr);
291 	} else
292 		cairo_restore (cr);
293 }
294 
295 static void
goc_rectangle_init_style(G_GNUC_UNUSED GocStyledItem * item,GOStyle * style)296 goc_rectangle_init_style (G_GNUC_UNUSED GocStyledItem *item, GOStyle *style)
297 {
298 	style->interesting_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL;
299 	if (style->line.auto_dash)
300 		style->line.dash_type = GO_LINE_SOLID;
301 	if (style->line.auto_color)
302 		style->line.color = GO_COLOR_BLACK;
303 	if (style->fill.auto_type)
304 		style->fill.type  = GO_STYLE_FILL_PATTERN;
305 	if (style->fill.auto_fore)
306 		style->fill.pattern.fore = GO_COLOR_BLACK;
307 	if (style->fill.auto_back)
308 		style->fill.pattern.back = GO_COLOR_WHITE;
309 }
310 
311 static void
goc_rectangle_copy(GocItem * dest,GocItem * source)312 goc_rectangle_copy (GocItem *dest, GocItem *source)
313 {
314 	GocRectangle *src = GOC_RECTANGLE (source), *dst = GOC_RECTANGLE (dest);
315 
316 	dst->rotation = src->rotation;
317 	dst->x = src->x;
318 	dst->y = src->y;
319 	dst->width = src->width;
320 	dst->height = src->height;
321 	dst->type = src->type;
322 	dst->rx = src->rx;
323 	dst->ry = src->ry;
324 	parent_klass->copy (dest, source);
325 }
326 
327 static void
goc_rectangle_class_init(GocItemClass * item_klass)328 goc_rectangle_class_init (GocItemClass *item_klass)
329 {
330 	GObjectClass *obj_klass = (GObjectClass *) item_klass;
331 	GocStyledItemClass *gsi_klass = (GocStyledItemClass *) item_klass;
332 	parent_klass = g_type_class_peek_parent (item_klass);
333 
334 	obj_klass->get_property = goc_rectangle_get_property;
335 	obj_klass->set_property = goc_rectangle_set_property;
336 	g_object_class_install_property (obj_klass, RECT_PROP_X,
337 		g_param_spec_double ("x",
338 			_("x"),
339 			_("The rectangle left position (or right position in RTL mode)"),
340 			-G_MAXDOUBLE, G_MAXDOUBLE, 0.,
341 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
342 	g_object_class_install_property (obj_klass, RECT_PROP_Y,
343 		g_param_spec_double ("y",
344 			_("y"),
345 			_("The rectangle top position"),
346 			-G_MAXDOUBLE, G_MAXDOUBLE, 0.,
347 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
348 	g_object_class_install_property (obj_klass, RECT_PROP_W,
349 		g_param_spec_double ("width",
350 			_("Width"),
351 			_("The rectangle width"),
352 			0., G_MAXDOUBLE, 0.,
353 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
354 	g_object_class_install_property (obj_klass, RECT_PROP_H,
355 		g_param_spec_double ("height",
356 			_("Height"),
357 			_("The rectangle height"),
358 			0., G_MAXDOUBLE, 0.,
359 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
360 	g_object_class_install_property (obj_klass, RECT_PROP_ROTATION,
361 		g_param_spec_double ("rotation",
362 			_("Rotation"),
363 			_("The rotation around top left position"),
364 			0., 2 * M_PI, 0.,
365 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
366 	g_object_class_install_property (obj_klass, RECT_PROP_TYPE,
367 		g_param_spec_int ("type",
368 			_("Type"),
369 			_("The rectangle type: 0 for no rounded corner, 1 for rounded top left, 2 for top right, 4 for bottom right, 8 for bottom left, or any combination of these."),
370 			0, 15, 0,
371 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
372 	g_object_class_install_property (obj_klass, RECT_PROP_RX,
373 		g_param_spec_double ("rx",
374 			_("rx"),
375 			_("The round rectangle rx"),
376 			0., G_MAXDOUBLE, 0.,
377 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
378 	g_object_class_install_property (obj_klass, RECT_PROP_RY,
379 		g_param_spec_double ("ry",
380 			_("ry"),
381 			_("The round rectangle ry"),
382 			0., G_MAXDOUBLE, 0.,
383 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
384 
385 	gsi_klass->init_style = goc_rectangle_init_style;
386 
387 	item_klass->update_bounds = goc_rectangle_update_bounds;
388 	item_klass->distance = goc_rectangle_distance;
389 	item_klass->draw = goc_rectangle_draw;
390 	item_klass->copy = goc_rectangle_copy;
391 }
392 
393 GSF_CLASS (GocRectangle, goc_rectangle,
394 	   goc_rectangle_class_init, NULL,
395 	   GOC_TYPE_STYLED_ITEM)
396