1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpcanvascorner.c
5  * Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <gegl.h>
24 #include <gtk/gtk.h>
25 
26 #include "libgimpbase/gimpbase.h"
27 #include "libgimpmath/gimpmath.h"
28 
29 #include "display-types.h"
30 
31 #include "gimpcanvascorner.h"
32 #include "gimpdisplayshell.h"
33 
34 
35 enum
36 {
37   PROP_0,
38   PROP_X,
39   PROP_Y,
40   PROP_WIDTH,
41   PROP_HEIGHT,
42   PROP_ANCHOR,
43   PROP_CORNER_WIDTH,
44   PROP_CORNER_HEIGHT,
45   PROP_OUTSIDE
46 };
47 
48 
49 typedef struct _GimpCanvasCornerPrivate GimpCanvasCornerPrivate;
50 
51 struct _GimpCanvasCornerPrivate
52 {
53   gdouble          x;
54   gdouble          y;
55   gdouble          width;
56   gdouble          height;
57   GimpHandleAnchor anchor;
58   gint             corner_width;
59   gint             corner_height;
60   gboolean         outside;
61 };
62 
63 #define GET_PRIVATE(corner) \
64         ((GimpCanvasCornerPrivate *) gimp_canvas_corner_get_instance_private ((GimpCanvasCorner *) (corner)))
65 
66 
67 /*  local function prototypes  */
68 
69 static void             gimp_canvas_corner_set_property (GObject        *object,
70                                                          guint           property_id,
71                                                          const GValue   *value,
72                                                          GParamSpec     *pspec);
73 static void             gimp_canvas_corner_get_property (GObject        *object,
74                                                          guint           property_id,
75                                                          GValue         *value,
76                                                          GParamSpec     *pspec);
77 static void             gimp_canvas_corner_draw         (GimpCanvasItem *item,
78                                                          cairo_t        *cr);
79 static cairo_region_t * gimp_canvas_corner_get_extents  (GimpCanvasItem *item);
80 
81 
G_DEFINE_TYPE_WITH_PRIVATE(GimpCanvasCorner,gimp_canvas_corner,GIMP_TYPE_CANVAS_ITEM)82 G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasCorner, gimp_canvas_corner,
83                             GIMP_TYPE_CANVAS_ITEM)
84 
85 #define parent_class gimp_canvas_corner_parent_class
86 
87 
88 static void
89 gimp_canvas_corner_class_init (GimpCanvasCornerClass *klass)
90 {
91   GObjectClass        *object_class = G_OBJECT_CLASS (klass);
92   GimpCanvasItemClass *item_class   = GIMP_CANVAS_ITEM_CLASS (klass);
93 
94   object_class->set_property = gimp_canvas_corner_set_property;
95   object_class->get_property = gimp_canvas_corner_get_property;
96 
97   item_class->draw           = gimp_canvas_corner_draw;
98   item_class->get_extents    = gimp_canvas_corner_get_extents;
99 
100   g_object_class_install_property (object_class, PROP_X,
101                                    g_param_spec_double ("x", NULL, NULL,
102                                                         -GIMP_MAX_IMAGE_SIZE,
103                                                         GIMP_MAX_IMAGE_SIZE, 0,
104                                                         GIMP_PARAM_READWRITE));
105 
106   g_object_class_install_property (object_class, PROP_Y,
107                                    g_param_spec_double ("y", NULL, NULL,
108                                                         -GIMP_MAX_IMAGE_SIZE,
109                                                         GIMP_MAX_IMAGE_SIZE, 0,
110                                                         GIMP_PARAM_READWRITE));
111 
112   g_object_class_install_property (object_class, PROP_WIDTH,
113                                    g_param_spec_double ("width", NULL, NULL,
114                                                         -GIMP_MAX_IMAGE_SIZE,
115                                                         GIMP_MAX_IMAGE_SIZE, 0,
116                                                         GIMP_PARAM_READWRITE));
117 
118   g_object_class_install_property (object_class, PROP_HEIGHT,
119                                    g_param_spec_double ("height", NULL, NULL,
120                                                         -GIMP_MAX_IMAGE_SIZE,
121                                                         GIMP_MAX_IMAGE_SIZE, 0,
122                                                         GIMP_PARAM_READWRITE));
123 
124   g_object_class_install_property (object_class, PROP_ANCHOR,
125                                    g_param_spec_enum ("anchor", NULL, NULL,
126                                                       GIMP_TYPE_HANDLE_ANCHOR,
127                                                       GIMP_HANDLE_ANCHOR_CENTER,
128                                                       GIMP_PARAM_READWRITE));
129 
130   g_object_class_install_property (object_class, PROP_CORNER_WIDTH,
131                                    g_param_spec_int ("corner-width", NULL, NULL,
132                                                      3, GIMP_MAX_IMAGE_SIZE, 3,
133                                                      GIMP_PARAM_READWRITE));
134 
135   g_object_class_install_property (object_class, PROP_CORNER_HEIGHT,
136                                    g_param_spec_int ("corner-height", NULL, NULL,
137                                                      3, GIMP_MAX_IMAGE_SIZE, 3,
138                                                      GIMP_PARAM_READWRITE));
139 
140   g_object_class_install_property (object_class, PROP_OUTSIDE,
141                                    g_param_spec_boolean ("outside", NULL, NULL,
142                                                          FALSE,
143                                                          GIMP_PARAM_READWRITE));
144 }
145 
146 static void
gimp_canvas_corner_init(GimpCanvasCorner * corner)147 gimp_canvas_corner_init (GimpCanvasCorner *corner)
148 {
149 }
150 
151 static void
gimp_canvas_corner_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)152 gimp_canvas_corner_set_property (GObject      *object,
153                                  guint         property_id,
154                                  const GValue *value,
155                                  GParamSpec   *pspec)
156 {
157   GimpCanvasCornerPrivate *private = GET_PRIVATE (object);
158 
159   switch (property_id)
160     {
161     case PROP_X:
162       private->x = g_value_get_double (value);
163       break;
164     case PROP_Y:
165       private->y = g_value_get_double (value);
166       break;
167     case PROP_WIDTH:
168       private->width = g_value_get_double (value);
169       break;
170     case PROP_HEIGHT:
171       private->height = g_value_get_double (value);
172       break;
173     case PROP_ANCHOR:
174       private->anchor = g_value_get_enum (value);
175       break;
176     case PROP_CORNER_WIDTH:
177       private->corner_width = g_value_get_int (value);
178       break;
179     case PROP_CORNER_HEIGHT:
180       private->corner_height = g_value_get_int (value);
181       break;
182    case PROP_OUTSIDE:
183       private->outside = g_value_get_boolean (value);
184       break;
185 
186     default:
187       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
188       break;
189     }
190 }
191 
192 static void
gimp_canvas_corner_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)193 gimp_canvas_corner_get_property (GObject    *object,
194                                  guint       property_id,
195                                  GValue     *value,
196                                  GParamSpec *pspec)
197 {
198   GimpCanvasCornerPrivate *private = GET_PRIVATE (object);
199 
200   switch (property_id)
201     {
202     case PROP_X:
203       g_value_set_double (value, private->x);
204       break;
205     case PROP_Y:
206       g_value_set_double (value, private->y);
207       break;
208     case PROP_WIDTH:
209       g_value_set_double (value, private->width);
210       break;
211     case PROP_HEIGHT:
212       g_value_set_double (value, private->height);
213       break;
214     case PROP_ANCHOR:
215       g_value_set_enum (value, private->anchor);
216       break;
217     case PROP_CORNER_WIDTH:
218       g_value_set_int (value, private->corner_width);
219       break;
220     case PROP_CORNER_HEIGHT:
221       g_value_set_int (value, private->corner_height);
222       break;
223     case PROP_OUTSIDE:
224       g_value_set_boolean (value, private->outside);
225       break;
226 
227     default:
228       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
229       break;
230     }
231 }
232 
233 static void
gimp_canvas_corner_transform(GimpCanvasItem * item,gdouble * x,gdouble * y,gdouble * w,gdouble * h)234 gimp_canvas_corner_transform (GimpCanvasItem *item,
235                               gdouble        *x,
236                               gdouble        *y,
237                               gdouble        *w,
238                               gdouble        *h)
239 {
240   GimpCanvasCornerPrivate *private = GET_PRIVATE (item);
241   gdouble                  rx, ry;
242   gdouble                  rw, rh;
243   gint                     top_and_bottom_handle_x_offset;
244   gint                     left_and_right_handle_y_offset;
245 
246   gimp_canvas_item_transform_xy_f (item,
247                                    MIN (private->x,
248                                         private->x + private->width),
249                                    MIN (private->y,
250                                         private->y + private->height),
251                                    &rx, &ry);
252   gimp_canvas_item_transform_xy_f (item,
253                                    MAX (private->x,
254                                         private->x + private->width),
255                                    MAX (private->y,
256                                         private->y + private->height),
257                                    &rw, &rh);
258 
259   rw -= rx;
260   rh -= ry;
261 
262   rx = floor (rx) + 0.5;
263   ry = floor (ry) + 0.5;
264   rw = ceil (rw) - 1.0;
265   rh = ceil (rh) - 1.0;
266 
267   top_and_bottom_handle_x_offset = (rw - private->corner_width)  / 2;
268   left_and_right_handle_y_offset = (rh - private->corner_height) / 2;
269 
270   *w = private->corner_width;
271   *h = private->corner_height;
272 
273   switch (private->anchor)
274     {
275     case GIMP_HANDLE_ANCHOR_CENTER:
276       break;
277 
278     case GIMP_HANDLE_ANCHOR_NORTH_WEST:
279       if (private->outside)
280         {
281           *x = rx - private->corner_width;
282           *y = ry - private->corner_height;
283         }
284       else
285         {
286           *x = rx;
287           *y = ry;
288         }
289       break;
290 
291     case GIMP_HANDLE_ANCHOR_NORTH_EAST:
292       if (private->outside)
293         {
294           *x = rx + rw;
295           *y = ry - private->corner_height;
296         }
297       else
298         {
299           *x = rx + rw - private->corner_width;
300           *y = ry;
301         }
302       break;
303 
304     case GIMP_HANDLE_ANCHOR_SOUTH_WEST:
305       if (private->outside)
306         {
307           *x = rx - private->corner_width;
308           *y = ry + rh;
309         }
310       else
311         {
312           *x = rx;
313           *y = ry + rh - private->corner_height;
314         }
315       break;
316 
317     case GIMP_HANDLE_ANCHOR_SOUTH_EAST:
318       if (private->outside)
319         {
320           *x = rx + rw;
321           *y = ry + rh;
322         }
323       else
324         {
325           *x = rx + rw - private->corner_width;
326           *y = ry + rh - private->corner_height;
327         }
328       break;
329 
330     case GIMP_HANDLE_ANCHOR_NORTH:
331       if (private->outside)
332         {
333           *x = rx;
334           *y = ry - private->corner_height;
335           *w = rw;
336         }
337       else
338         {
339           *x = rx + top_and_bottom_handle_x_offset;
340           *y = ry;
341         }
342       break;
343 
344     case GIMP_HANDLE_ANCHOR_SOUTH:
345       if (private->outside)
346         {
347           *x = rx;
348           *y = ry + rh;
349           *w = rw;
350         }
351       else
352         {
353           *x = rx + top_and_bottom_handle_x_offset;
354           *y = ry + rh - private->corner_height;
355         }
356       break;
357 
358     case GIMP_HANDLE_ANCHOR_WEST:
359       if (private->outside)
360         {
361           *x = rx - private->corner_width;
362           *y = ry;
363           *h = rh;
364         }
365       else
366         {
367           *x = rx;
368           *y = ry + left_and_right_handle_y_offset;
369         }
370       break;
371 
372     case GIMP_HANDLE_ANCHOR_EAST:
373       if (private->outside)
374         {
375           *x = rx + rw;
376           *y = ry;
377           *h = rh;
378         }
379       else
380         {
381           *x = rx + rw - private->corner_width;
382           *y = ry + left_and_right_handle_y_offset;
383         }
384       break;
385     }
386 }
387 
388 static void
gimp_canvas_corner_draw(GimpCanvasItem * item,cairo_t * cr)389 gimp_canvas_corner_draw (GimpCanvasItem *item,
390                          cairo_t        *cr)
391 {
392   gdouble x, y;
393   gdouble w, h;
394 
395   gimp_canvas_corner_transform (item, &x, &y, &w, &h);
396 
397   cairo_rectangle (cr, x, y, w, h);
398 
399   _gimp_canvas_item_stroke (item, cr);
400 }
401 
402 static cairo_region_t *
gimp_canvas_corner_get_extents(GimpCanvasItem * item)403 gimp_canvas_corner_get_extents (GimpCanvasItem *item)
404 {
405   cairo_rectangle_int_t rectangle;
406   gdouble               x, y;
407   gdouble               w, h;
408 
409   gimp_canvas_corner_transform (item, &x, &y, &w, &h);
410 
411   rectangle.x      = floor (x - 1.5);
412   rectangle.y      = floor (y - 1.5);
413   rectangle.width  = ceil (w + 3.0);
414   rectangle.height = ceil (h + 3.0);
415 
416   return cairo_region_create_rectangle (&rectangle);
417 }
418 
419 GimpCanvasItem *
gimp_canvas_corner_new(GimpDisplayShell * shell,gdouble x,gdouble y,gdouble width,gdouble height,GimpHandleAnchor anchor,gint corner_width,gint corner_height,gboolean outside)420 gimp_canvas_corner_new (GimpDisplayShell *shell,
421                         gdouble           x,
422                         gdouble           y,
423                         gdouble           width,
424                         gdouble           height,
425                         GimpHandleAnchor  anchor,
426                         gint              corner_width,
427                         gint              corner_height,
428                         gboolean          outside)
429 {
430   g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
431 
432   return g_object_new (GIMP_TYPE_CANVAS_CORNER,
433                        "shell",         shell,
434                        "x",             x,
435                        "y",             y,
436                        "width",         width,
437                        "height",        height,
438                        "anchor",        anchor,
439                        "corner-width",  corner_width,
440                        "corner-height", corner_height,
441                        "outside",       outside,
442                        NULL);
443 }
444 
445 void
gimp_canvas_corner_set(GimpCanvasItem * corner,gdouble x,gdouble y,gdouble width,gdouble height,gint corner_width,gint corner_height,gboolean outside)446 gimp_canvas_corner_set (GimpCanvasItem *corner,
447                         gdouble         x,
448                         gdouble         y,
449                         gdouble         width,
450                         gdouble         height,
451                         gint            corner_width,
452                         gint            corner_height,
453                         gboolean        outside)
454 {
455   g_return_if_fail (GIMP_IS_CANVAS_CORNER (corner));
456 
457   gimp_canvas_item_begin_change (corner);
458   g_object_set (corner,
459                 "x",             x,
460                 "y",             y,
461                 "width",         width,
462                 "height",        height,
463                 "corner-width",  corner_width,
464                 "corner-height", corner_height,
465                 "outside",       outside,
466                 NULL);
467   gimp_canvas_item_end_change (corner);
468 }
469