1 /*
2  * This is a plug-in for GIMP.
3  *
4  * Generates clickable image maps.
5  *
6  * Copyright (C) 1998-2004 Maurits Rijk  m.rijk@chello.nl
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #include "config.h"
24 
25 #include <math.h>
26 #include <stdlib.h>
27 
28 #include <gtk/gtk.h>
29 
30 #include "imap_circle.h"
31 #include "imap_main.h"
32 #include "imap_misc.h"
33 #include "imap_object_popup.h"
34 #include "imap_stock.h"
35 #include "imap_table.h"
36 
37 #include "libgimp/stdplugins-intl.h"
38 
39 static gboolean circle_is_valid(Object_t *obj);
40 static Object_t *circle_clone(Object_t *obj);
41 static void circle_assign(Object_t *obj, Object_t *des);
42 static void circle_draw(Object_t* obj, cairo_t *cr);
43 static void circle_draw_sashes(Object_t* obj, cairo_t *cr);
44 static MoveSashFunc_t circle_near_sash(Object_t *obj, gint x, gint y);
45 static gboolean circle_point_is_on(Object_t *obj, gint x, gint y);
46 static void circle_get_dimensions(Object_t *obj, gint *x, gint *y,
47                                   gint *width, gint *height);
48 static void circle_resize(Object_t *obj, gint percentage_x, gint percentage_y);
49 static void circle_move(Object_t *obj, gint dx, gint dy);
50 static gpointer circle_create_info_widget(GtkWidget *frame);
51 static void circle_fill_info_tab(Object_t *obj, gpointer data);
52 static void circle_set_initial_focus(Object_t *obj, gpointer data);
53 static void circle_update(Object_t* obj, gpointer data);
54 static void circle_write_csim(Object_t* obj, gpointer param,
55                               OutputFunc_t output);
56 static void circle_write_cern(Object_t* obj, gpointer param,
57                               OutputFunc_t output);
58 static void circle_write_ncsa(Object_t* obj, gpointer param,
59                               OutputFunc_t output);
60 static const gchar* circle_get_stock_icon_name(void);
61 
62 static ObjectClass_t circle_class = {
63    N_("C_ircle"),
64    NULL,                        /* info_dialog */
65 
66    circle_is_valid,
67    NULL,                        /* circle_destruct */
68    circle_clone,
69    circle_assign,
70    NULL,                        /* circle_normalize */
71    circle_draw,
72    circle_draw_sashes,
73    circle_near_sash,
74    circle_point_is_on,
75    circle_get_dimensions,
76    circle_resize,
77    circle_move,
78    circle_create_info_widget,
79    circle_fill_info_tab,        /* circle_update_info_widget */
80    circle_fill_info_tab,
81    circle_set_initial_focus,
82    circle_update,
83    circle_write_csim,
84    circle_write_cern,
85    circle_write_ncsa,
86    object_do_popup,
87    circle_get_stock_icon_name
88 };
89 
90 Object_t*
create_circle(gint x,gint y,gint r)91 create_circle(gint x, gint y, gint r)
92 {
93    Circle_t *circle = g_new(Circle_t, 1);
94    circle->x = x;
95    circle->y = y;
96    circle->r = r;
97    return object_init(&circle->obj, &circle_class);
98 }
99 
100 static gboolean
circle_is_valid(Object_t * obj)101 circle_is_valid(Object_t *obj)
102 {
103    return ObjectToCircle(obj)->r > 0;
104 }
105 
106 static Object_t*
circle_clone(Object_t * obj)107 circle_clone(Object_t *obj)
108 {
109    Circle_t *circle = ObjectToCircle(obj);
110    Circle_t *clone = g_new(Circle_t, 1);
111 
112    clone->x = circle->x;
113    clone->y = circle->y;
114    clone->r = circle->r;
115    return &clone->obj;
116 }
117 
118 static void
circle_assign(Object_t * obj,Object_t * des)119 circle_assign(Object_t *obj, Object_t *des)
120 {
121    Circle_t *src_circle = ObjectToCircle(obj);
122    Circle_t *des_circle = ObjectToCircle(des);
123    des_circle->x = src_circle->x;
124    des_circle->y = src_circle->y;
125    des_circle->r = src_circle->r;
126 }
127 
128 static void
circle_draw(Object_t * obj,cairo_t * cr)129 circle_draw(Object_t *obj, cairo_t *cr)
130 {
131    Circle_t *circle = ObjectToCircle(obj);
132    draw_circle(cr, circle->x, circle->y, circle->r);
133 }
134 
135 static void
circle_draw_sashes(Object_t * obj,cairo_t * cr)136 circle_draw_sashes(Object_t *obj, cairo_t *cr)
137 {
138    Circle_t *circle = ObjectToCircle(obj);
139    draw_sash(cr, circle->x - circle->r, circle->y - circle->r);
140    draw_sash(cr, circle->x + circle->r, circle->y - circle->r);
141    draw_sash(cr, circle->x - circle->r, circle->y + circle->r);
142    draw_sash(cr, circle->x + circle->r, circle->y + circle->r);
143 }
144 
145 static gint sash_x;
146 static gint sash_y;
147 
148 static void
move_sash(Object_t * obj,gint dx,gint dy)149 move_sash(Object_t *obj, gint dx, gint dy)
150 {
151    Circle_t *circle = ObjectToCircle(obj);
152    gint rx, ry;
153    sash_x += dx;
154    sash_y += dy;
155 
156    rx = abs(circle->x - sash_x);
157    ry = abs(circle->y - sash_y);
158    circle->r = (rx > ry) ? rx : ry;
159 }
160 
161 static void
circle_resize(Object_t * obj,gint percentage_x,gint percentage_y)162 circle_resize(Object_t *obj, gint percentage_x, gint percentage_y)
163 {
164    Circle_t *circle = ObjectToCircle(obj);
165    circle->x = circle->x * percentage_x / 100;
166    circle->y = circle->y * percentage_y / 100;
167    circle->r = circle->r * ((percentage_x < percentage_y)
168                             ? percentage_x : percentage_y) / 100;
169 }
170 
171 static MoveSashFunc_t
circle_near_sash(Object_t * obj,gint x,gint y)172 circle_near_sash(Object_t *obj, gint x, gint y)
173 {
174    Circle_t *circle = ObjectToCircle(obj);
175    sash_x = x;
176    sash_y = y;
177    if (near_sash(circle->x - circle->r, circle->y - circle->r, x, y) ||
178        near_sash(circle->x + circle->r, circle->y - circle->r, x, y) ||
179        near_sash(circle->x - circle->r, circle->y + circle->r, x, y) ||
180        near_sash(circle->x + circle->r, circle->y + circle->r, x, y))
181       return move_sash;
182    return NULL;
183 }
184 
185 static gboolean
circle_point_is_on(Object_t * obj,gint x,gint y)186 circle_point_is_on(Object_t *obj, gint x, gint y)
187 {
188    Circle_t *circle = ObjectToCircle(obj);
189    x -= circle->x;
190    y -= circle->y;
191    return x * x + y * y <= circle->r * circle->r;
192 }
193 
194 static void
circle_get_dimensions(Object_t * obj,gint * x,gint * y,gint * width,gint * height)195 circle_get_dimensions(Object_t *obj, gint *x, gint *y,
196                       gint *width, gint *height)
197 {
198    Circle_t *circle = ObjectToCircle(obj);
199    *x = circle->x - circle->r;
200    *y = circle->y - circle->r;
201    *width = *height = 2 * circle->r;
202 }
203 
204 static void
circle_move(Object_t * obj,gint dx,gint dy)205 circle_move(Object_t *obj, gint dx, gint dy)
206 {
207    Circle_t *circle = ObjectToCircle(obj);
208    circle->x += dx;
209    circle->y += dy;
210 }
211 
212 typedef struct {
213    Object_t  *obj;
214    GtkWidget *x;
215    GtkWidget *y;
216    GtkWidget *r;
217 } CircleProperties_t;
218 
219 static void
x_changed_cb(GtkWidget * widget,gpointer data)220 x_changed_cb(GtkWidget *widget, gpointer data)
221 {
222    Object_t *obj = ((CircleProperties_t*) data)->obj;
223    gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
224    ObjectToCircle(obj)->x = x;
225    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
226 }
227 
228 static void
y_changed_cb(GtkWidget * widget,gpointer data)229 y_changed_cb(GtkWidget *widget, gpointer data)
230 {
231    Object_t *obj = ((CircleProperties_t*) data)->obj;
232    gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
233    ObjectToCircle(obj)->y = y;
234    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
235 }
236 
237 static void
r_changed_cb(GtkWidget * widget,gpointer data)238 r_changed_cb(GtkWidget *widget, gpointer data)
239 {
240    Object_t *obj = ((CircleProperties_t*) data)->obj;
241    gint r = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
242    ObjectToCircle(obj)->r = r;
243    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
244 }
245 
246 static gpointer
circle_create_info_widget(GtkWidget * frame)247 circle_create_info_widget(GtkWidget *frame)
248 {
249    CircleProperties_t *props = g_new(CircleProperties_t, 1);
250    GtkWidget *table, *label;
251    gint max_width = get_image_width();
252    gint max_height = get_image_height();
253 
254    table = gtk_table_new(3, 3, FALSE);
255    gtk_container_add(GTK_CONTAINER(frame), table);
256 
257    gtk_table_set_row_spacings(GTK_TABLE(table), 6);
258    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
259    gtk_widget_show(table);
260 
261    label = create_label_in_table(table, 0, 0, _("Center _x:"));
262    props->x = create_spin_button_in_table(table, label, 0, 1, 1, 0,
263                                           max_width - 1);
264    g_signal_connect(props->x, "value-changed",
265                     G_CALLBACK (x_changed_cb), (gpointer) props);
266    create_label_in_table(table, 0, 2, _("pixels"));
267 
268    label = create_label_in_table(table, 1, 0, _("Center _y:"));
269    props->y = create_spin_button_in_table(table, label, 1, 1, 1, 0,
270                                           max_height - 1);
271    g_signal_connect(props->y, "value-changed",
272                     G_CALLBACK (y_changed_cb), (gpointer) props);
273    create_label_in_table(table, 1, 2, _("pixels"));
274 
275    label = create_label_in_table(table, 2, 0, _("_Radius:"));
276    props->r = create_spin_button_in_table(table, label, 2, 1, 1, 1, G_MAXINT);
277    g_signal_connect(props->r, "value-changed",
278                     G_CALLBACK (r_changed_cb), (gpointer) props);
279    create_label_in_table(table, 2, 2, _("pixels"));
280 
281    return props;
282 }
283 
284 static void
circle_fill_info_tab(Object_t * obj,gpointer data)285 circle_fill_info_tab(Object_t *obj, gpointer data)
286 {
287    Circle_t *circle = ObjectToCircle(obj);
288    CircleProperties_t *props = (CircleProperties_t*) data;
289 
290    props->obj = obj;
291    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->x), circle->x);
292    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), circle->y);
293    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->r), circle->r);
294 }
295 
296 static void
circle_set_initial_focus(Object_t * obj,gpointer data)297 circle_set_initial_focus(Object_t *obj, gpointer data)
298 {
299    CircleProperties_t *props = (CircleProperties_t*) data;
300    gtk_widget_grab_focus(props->x);
301 }
302 
303 static void
circle_update(Object_t * obj,gpointer data)304 circle_update(Object_t* obj, gpointer data)
305 {
306    Circle_t *circle = ObjectToCircle(obj);
307    CircleProperties_t *props = (CircleProperties_t*) data;
308 
309    circle->x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->x));
310    circle->y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->y));
311    circle->r = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->r));
312 }
313 
314 static void
circle_write_csim(Object_t * obj,gpointer param,OutputFunc_t output)315 circle_write_csim(Object_t *obj, gpointer param, OutputFunc_t output)
316 {
317    Circle_t *circle = ObjectToCircle(obj);
318    output(param, "\"circle\" coords=\"%d,%d,%d\"", circle->x, circle->y,
319           circle->r);
320 }
321 
322 static void
circle_write_cern(Object_t * obj,gpointer param,OutputFunc_t output)323 circle_write_cern(Object_t *obj, gpointer param, OutputFunc_t output)
324 {
325    Circle_t *circle = ObjectToCircle(obj);
326    output(param, "circ (%d,%d) %d", circle->x, circle->y, circle->r);
327 }
328 
329 static void
circle_write_ncsa(Object_t * obj,gpointer param,OutputFunc_t output)330 circle_write_ncsa(Object_t *obj, gpointer param, OutputFunc_t output)
331 {
332    Circle_t *circle = ObjectToCircle(obj);
333    output(param, "circle %s %d,%d %d,%d", obj->url,
334           circle->x, circle->y, circle->x, circle->y + circle->r);
335 }
336 
337 static const gchar*
circle_get_stock_icon_name(void)338 circle_get_stock_icon_name(void)
339 {
340    return IMAP_STOCK_CIRCLE;
341 }
342 
343 static gint _start_x, _start_y;
344 
345 static Object_t*
circle_factory_create_object1(gint x,gint y)346 circle_factory_create_object1(gint x, gint y)
347 {
348    _start_x = x;
349    _start_y = y;
350    return create_circle(x, y, 0);
351 }
352 
353 static void
circle_factory_set_xy1(Object_t * obj,guint state,gint x,gint y)354 circle_factory_set_xy1(Object_t *obj, guint state, gint x, gint y)
355 {
356    Circle_t *circle = ObjectToCircle(obj);
357 
358    circle->x = (_start_x + x) / 2;
359    circle->y = (_start_y + y) / 2;
360    x -= _start_x;
361    y -= _start_y;
362    circle->r = (gint) sqrt(x * x + y * y) / 2;
363 
364    main_set_dimension(circle->r, circle->r);
365 }
366 
367 static ObjectFactory_t circle_factory1 = {
368    NULL,                        /* Object pointer */
369    NULL,                        /* Finish func */
370    NULL,                        /* Cancel func */
371    circle_factory_create_object1,
372    circle_factory_set_xy1
373 };
374 
375 static Object_t*
circle_factory_create_object2(gint x,gint y)376 circle_factory_create_object2(gint x, gint y)
377 {
378    return create_circle(x, y, 0);
379 }
380 
381 static void
circle_factory_set_xy2(Object_t * obj,guint state,gint x,gint y)382 circle_factory_set_xy2(Object_t *obj, guint state, gint x, gint y)
383 {
384    Circle_t *circle = ObjectToCircle(obj);
385 
386    x -= circle->x;
387    y -= circle->y;
388    circle->r = (gint) sqrt(x * x + y * y);
389 
390    main_set_dimension(circle->r, circle->r);
391 }
392 
393 static ObjectFactory_t circle_factory2 = {
394    NULL,                        /* Object pointer */
395    NULL,                        /* Finish func */
396    NULL,                        /* Cancel func */
397    circle_factory_create_object2,
398    circle_factory_set_xy2
399 };
400 
401 ObjectFactory_t*
get_circle_factory(guint state)402 get_circle_factory(guint state)
403 {
404    return (state & GDK_SHIFT_MASK) ? &circle_factory1 : &circle_factory2;
405 }
406