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