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