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 <stdlib.h> /* abs */
26 
27 #include <gtk/gtk.h>
28 
29 #include <libgimp/gimp.h>
30 #include <libgimp/gimpui.h>
31 
32 #include "imap_main.h"
33 #include "imap_misc.h"
34 #include "imap_object_popup.h"
35 #include "imap_rectangle.h"
36 #include "imap_stock.h"
37 #include "imap_table.h"
38 
39 #include "libgimp/stdplugins-intl.h"
40 
41 
42 static gboolean rectangle_is_valid(Object_t *obj);
43 static Object_t *rectangle_clone(Object_t *obj);
44 static void rectangle_assign(Object_t *obj, Object_t *des);
45 static void rectangle_normalize(Object_t *obj);
46 static void rectangle_draw(Object_t *obj, cairo_t *cr);
47 static void rectangle_draw_sashes(Object_t *obj, cairo_t *cr);
48 static MoveSashFunc_t rectangle_near_sash(Object_t *obj, gint x, gint y);
49 static gboolean rectangle_point_is_on(Object_t *obj, gint x, gint y);
50 static void rectangle_get_dimensions(Object_t *obj, gint *x, gint *y,
51                                      gint *width, gint *height);
52 static void rectangle_resize(Object_t *obj, gint percentage_x,
53                              gint percentage_y);
54 static void rectangle_move(Object_t *obj, gint dx, gint dy);
55 static gpointer rectangle_create_info_widget(GtkWidget *frame);
56 static void rectangle_fill_info_tab(Object_t *obj, gpointer data);
57 static void rectangle_set_initial_focus(Object_t *obj, gpointer data);
58 static void rectangle_update(Object_t *obj, gpointer data);
59 static void rectangle_write_csim(Object_t *obj, gpointer param,
60                                  OutputFunc_t output);
61 static void rectangle_write_cern(Object_t *obj, gpointer param,
62                                  OutputFunc_t output);
63 static void rectangle_write_ncsa(Object_t *obj, gpointer param,
64                                  OutputFunc_t output);
65 static const gchar* rectangle_get_stock_icon_name(void);
66 
67 static ObjectClass_t rectangle_class = {
68    N_("_Rectangle"),
69    NULL,                        /* info_dialog */
70 
71    rectangle_is_valid,
72    NULL,                        /* rectangle_destruct */
73    rectangle_clone,
74    rectangle_assign,
75    rectangle_normalize,
76    rectangle_draw,
77    rectangle_draw_sashes,
78    rectangle_near_sash,
79    rectangle_point_is_on,
80    rectangle_get_dimensions,
81    rectangle_resize,
82    rectangle_move,
83    rectangle_create_info_widget,
84    rectangle_fill_info_tab,     /* rectangle_update_info_widget */
85    rectangle_fill_info_tab,
86    rectangle_set_initial_focus,
87    rectangle_update,
88    rectangle_write_csim,
89    rectangle_write_cern,
90    rectangle_write_ncsa,
91    object_do_popup,
92    rectangle_get_stock_icon_name
93 };
94 
95 Object_t*
create_rectangle(gint x,gint y,gint width,gint height)96 create_rectangle(gint x, gint y, gint width, gint height)
97 {
98    Rectangle_t *rectangle = g_new(Rectangle_t, 1);
99    rectangle->x = x;
100    rectangle->y = y;
101    rectangle->width = width;
102    rectangle->height = height;
103    return object_init(&rectangle->obj, &rectangle_class);
104 }
105 
106 static void
draw_any_rectangle(cairo_t * cr,gint x,gint y,gint w,gint h)107 draw_any_rectangle(cairo_t *cr, gint x, gint y, gint w, gint h)
108 {
109    if (w < 0) {
110       x += w;
111       w = -w;
112    }
113    if (h < 0) {
114       y += h;
115       h = -h;
116    }
117    draw_rectangle(cr, FALSE, x, y, w, h);
118 }
119 
120 static gboolean
rectangle_is_valid(Object_t * obj)121 rectangle_is_valid(Object_t *obj)
122 {
123    Rectangle_t *rectangle = ObjectToRectangle(obj);
124    return rectangle->width && rectangle->height;
125 }
126 
127 static Object_t*
rectangle_clone(Object_t * obj)128 rectangle_clone(Object_t *obj)
129 {
130    Rectangle_t *rectangle = ObjectToRectangle(obj);
131    Rectangle_t *clone = g_new(Rectangle_t, 1);
132 
133    clone->x = rectangle->x;
134    clone->y = rectangle->y;
135    clone->width = rectangle->width;
136    clone->height = rectangle->height;
137    return &clone->obj;
138 }
139 
140 static void
rectangle_assign(Object_t * obj,Object_t * des)141 rectangle_assign(Object_t *obj, Object_t *des)
142 {
143    Rectangle_t *src_rectangle = ObjectToRectangle(obj);
144    Rectangle_t *des_rectangle = ObjectToRectangle(des);
145    des_rectangle->x = src_rectangle->x;
146    des_rectangle->y = src_rectangle->y;
147    des_rectangle->width = src_rectangle->width;
148    des_rectangle->height = src_rectangle->height;
149 }
150 
151 static void
rectangle_normalize(Object_t * obj)152 rectangle_normalize(Object_t *obj)
153 {
154    Rectangle_t *rectangle = ObjectToRectangle(obj);
155    if (rectangle->width < 0) {
156       rectangle->x += rectangle->width;
157       rectangle->width = -rectangle->width;
158    }
159    if (rectangle->height < 0) {
160       rectangle->y += rectangle->height;
161       rectangle->height = -rectangle->height;
162    }
163 }
164 
165 static void
rectangle_draw(Object_t * obj,cairo_t * cr)166 rectangle_draw(Object_t *obj, cairo_t *cr)
167 {
168    Rectangle_t *rectangle = ObjectToRectangle(obj);
169    draw_any_rectangle(cr, rectangle->x, rectangle->y,
170                       rectangle->width, rectangle->height);
171 }
172 
173 static void
rectangle_draw_sashes(Object_t * obj,cairo_t * cr)174 rectangle_draw_sashes(Object_t *obj, cairo_t *cr)
175 {
176    Rectangle_t *rectangle = ObjectToRectangle(obj);
177    draw_sash(cr, rectangle->x, rectangle->y);
178    draw_sash(cr, rectangle->x + rectangle->width / 2, rectangle->y);
179    draw_sash(cr, rectangle->x + rectangle->width, rectangle->y);
180    draw_sash(cr, rectangle->x, rectangle->y + rectangle->height / 2);
181    draw_sash(cr, rectangle->x + rectangle->width,
182              rectangle->y + rectangle->height / 2);
183    draw_sash(cr, rectangle->x, rectangle->y + rectangle->height);
184    draw_sash(cr, rectangle->x + rectangle->width / 2,
185              rectangle->y + rectangle->height);
186    draw_sash(cr, rectangle->x + rectangle->width,
187              rectangle->y + rectangle->height);
188 }
189 
190 static void
MoveUpperSash(Object_t * obj,gint dx,gint dy)191 MoveUpperSash(Object_t *obj, gint dx, gint dy)
192 {
193    Rectangle_t *rectangle = ObjectToRectangle(obj);
194    rectangle->y += dy;
195    rectangle->height -= dy;
196 }
197 
198 static void
MoveLeftSash(Object_t * obj,gint dx,gint dy)199 MoveLeftSash(Object_t *obj, gint dx, gint dy)
200 {
201    Rectangle_t *rectangle = ObjectToRectangle(obj);
202    rectangle->x += dx;
203    rectangle->width -= dx;
204 }
205 
206 static void
MoveRightSash(Object_t * obj,gint dx,gint dy)207 MoveRightSash(Object_t *obj, gint dx, gint dy)
208 {
209    Rectangle_t *rectangle = ObjectToRectangle(obj);
210    rectangle->width += dx;
211 }
212 
213 static void
MoveLowerSash(Object_t * obj,gint dx,gint dy)214 MoveLowerSash(Object_t *obj, gint dx, gint dy)
215 {
216    Rectangle_t *rectangle = ObjectToRectangle(obj);
217    rectangle->height += dy;
218 }
219 
220 static void
MoveUpperLeftSash(Object_t * obj,gint dx,gint dy)221 MoveUpperLeftSash(Object_t *obj, gint dx, gint dy)
222 {
223    Rectangle_t *rectangle = ObjectToRectangle(obj);
224    rectangle->x += dx;
225    rectangle->y += dy;
226    rectangle->width -= dx;
227    rectangle->height -= dy;
228 }
229 
230 static void
MoveUpperRightSash(Object_t * obj,gint dx,gint dy)231 MoveUpperRightSash(Object_t *obj, gint dx, gint dy)
232 {
233    Rectangle_t *rectangle = ObjectToRectangle(obj);
234    rectangle->y += dy;
235    rectangle->width += dx;
236    rectangle->height -= dy;
237 }
238 
239 static void
MoveLowerLeftSash(Object_t * obj,gint dx,gint dy)240 MoveLowerLeftSash(Object_t *obj, gint dx, gint dy)
241 {
242    Rectangle_t *rectangle = ObjectToRectangle(obj);
243    rectangle->x += dx;
244    rectangle->width -= dx;
245    rectangle->height += dy;
246 }
247 
248 static void
MoveLowerRightSash(Object_t * obj,gint dx,gint dy)249 MoveLowerRightSash(Object_t *obj, gint dx, gint dy)
250 {
251    Rectangle_t *rectangle = ObjectToRectangle(obj);
252    rectangle->width += dx;
253    rectangle->height += dy;
254 }
255 
256 static MoveSashFunc_t
rectangle_near_sash(Object_t * obj,gint x,gint y)257 rectangle_near_sash(Object_t *obj, gint x, gint y)
258 {
259    Rectangle_t *rectangle = ObjectToRectangle(obj);
260    if (near_sash(rectangle->x, rectangle->y, x, y))
261       return MoveUpperLeftSash;
262    else if (near_sash(rectangle->x + rectangle->width / 2, rectangle->y, x, y))
263       return MoveUpperSash;
264    else if (near_sash(rectangle->x + rectangle->width, rectangle->y, x, y))
265       return MoveUpperRightSash;
266    else if (near_sash(rectangle->x, rectangle->y + rectangle->height / 2,
267                       x, y))
268       return MoveLeftSash;
269    else if (near_sash(rectangle->x + rectangle->width,
270                       rectangle->y + rectangle->height / 2, x, y))
271       return MoveRightSash;
272    else if (near_sash(rectangle->x, rectangle->y + rectangle->height, x, y))
273       return MoveLowerLeftSash;
274    else if (near_sash(rectangle->x + rectangle->width / 2,
275                       rectangle->y + rectangle->height, x, y))
276       return MoveLowerSash;
277    else if (near_sash(rectangle->x + rectangle->width,
278                       rectangle->y + rectangle->height, x, y))
279       return MoveLowerRightSash;
280    return NULL;
281 }
282 
283 static gboolean
rectangle_point_is_on(Object_t * obj,gint x,gint y)284 rectangle_point_is_on(Object_t *obj, gint x, gint y)
285 {
286    Rectangle_t *rectangle = ObjectToRectangle(obj);
287    return x >= rectangle->x && x <= rectangle->x + rectangle->width &&
288       y >= rectangle->y && y <= rectangle->y + rectangle->height;
289 }
290 
291 static void
rectangle_get_dimensions(Object_t * obj,gint * x,gint * y,gint * width,gint * height)292 rectangle_get_dimensions(Object_t *obj, gint *x, gint *y,
293                          gint *width, gint *height)
294 {
295    Rectangle_t *rectangle = ObjectToRectangle(obj);
296    *x = rectangle->x;
297    *y = rectangle->y;
298    *width = rectangle->width;
299    *height = rectangle->height;
300 }
301 
302 static void
rectangle_resize(Object_t * obj,gint percentage_x,gint percentage_y)303 rectangle_resize(Object_t *obj, gint percentage_x, gint percentage_y)
304 {
305    Rectangle_t *rectangle = ObjectToRectangle(obj);
306    rectangle->x = rectangle->x * percentage_x / 100;
307    rectangle->y = rectangle->y * percentage_y / 100;
308    rectangle->width = rectangle->width * percentage_x / 100;
309    rectangle->height = rectangle->height * percentage_y / 100;
310 }
311 
312 static void
rectangle_move(Object_t * obj,gint dx,gint dy)313 rectangle_move(Object_t *obj, gint dx, gint dy)
314 {
315    Rectangle_t *rectangle = ObjectToRectangle(obj);
316    rectangle->x += dx;
317    rectangle->y += dy;
318 }
319 
320 typedef struct {
321    Object_t  *obj;
322    GtkWidget *x;
323    GtkWidget *y;
324    GtkWidget *width;
325    GtkWidget *height;
326    GtkWidget *chain_button;
327 } RectangleProperties_t;
328 
329 static void
x_changed_cb(GtkWidget * widget,gpointer data)330 x_changed_cb(GtkWidget *widget, gpointer data)
331 {
332    RectangleProperties_t *props = (RectangleProperties_t*) data;
333    Object_t *obj = props->obj;
334    gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
335 
336    if (gimp_chain_button_get_active(GIMP_CHAIN_BUTTON(props->chain_button)))
337       gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), x);
338 
339    ObjectToRectangle(obj)->x = x;
340    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
341 }
342 
343 static void
y_changed_cb(GtkWidget * widget,gpointer data)344 y_changed_cb(GtkWidget *widget, gpointer data)
345 {
346    Object_t *obj = ((RectangleProperties_t*) data)->obj;
347    gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
348    ObjectToRectangle(obj)->y = y;
349    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
350 }
351 
352 static void
width_changed_cb(GtkWidget * widget,gpointer data)353 width_changed_cb(GtkWidget *widget, gpointer data)
354 {
355    Object_t *obj = ((RectangleProperties_t*) data)->obj;
356    gint width = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
357    ObjectToRectangle(obj)->width = width;
358    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
359 }
360 
361 static void
height_changed_cb(GtkWidget * widget,gpointer data)362 height_changed_cb(GtkWidget *widget, gpointer data)
363 {
364    Object_t *obj = ((RectangleProperties_t*) data)->obj;
365    gint height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
366    ObjectToRectangle(obj)->height = height;
367    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
368 }
369 
370 static gpointer
rectangle_create_info_widget(GtkWidget * frame)371 rectangle_create_info_widget(GtkWidget *frame)
372 {
373    RectangleProperties_t *props = g_new(RectangleProperties_t, 1);
374    GtkWidget *table, *label, *chain_button;
375    gint max_width = get_image_width();
376    gint max_height = get_image_height();
377 
378    table = gtk_table_new(4, 4, FALSE);
379    gtk_container_add(GTK_CONTAINER(frame), table);
380 
381    gtk_table_set_row_spacings(GTK_TABLE(table), 6);
382    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
383    gtk_widget_show(table);
384 
385    label = create_label_in_table(table, 0, 0, _("Upper left _x:"));
386    props->x = create_spin_button_in_table(table, label, 0, 1, 1, 0,
387                                           max_width - 1);
388    g_signal_connect(props->x, "value-changed",
389                     G_CALLBACK(x_changed_cb), (gpointer) props);
390    create_label_in_table(table, 0, 3, _("pixels"));
391 
392    label = create_label_in_table(table, 1, 0, _("Upper left _y:"));
393    props->y = create_spin_button_in_table(table, label, 1, 1, 1, 0,
394                                           max_height - 1);
395    g_signal_connect(props->y, "value-changed",
396                     G_CALLBACK(y_changed_cb), (gpointer) props);
397    create_label_in_table(table, 1, 3, _("pixels"));
398 
399    label = create_label_in_table(table, 2, 0, _("_Width:"));
400    props->width = create_spin_button_in_table(table, label, 2, 1, 1, 1,
401                                               max_width);
402    g_signal_connect(props->width, "value-changed",
403                     G_CALLBACK(width_changed_cb), (gpointer) props);
404    create_label_in_table(table, 2, 3, _("pixels"));
405 
406    label = create_label_in_table(table, 3, 0, _("_Height:"));
407    props->height = create_spin_button_in_table(table, label, 3, 1, 1, 1,
408                                                max_height);
409    g_signal_connect(props->height, "value-changed",
410                     G_CALLBACK(height_changed_cb), (gpointer) props);
411    create_label_in_table(table, 3, 3, _("pixels"));
412 
413    chain_button = gimp_chain_button_new(GIMP_CHAIN_RIGHT);
414    props->chain_button = chain_button;
415    gtk_table_attach_defaults(GTK_TABLE(table), chain_button, 2, 3, 2, 4);
416    gtk_widget_show(chain_button);
417 
418    return props;
419 }
420 
421 static void
rectangle_fill_info_tab(Object_t * obj,gpointer data)422 rectangle_fill_info_tab(Object_t *obj, gpointer data)
423 {
424    Rectangle_t *rectangle = ObjectToRectangle(obj);
425    RectangleProperties_t *props = (RectangleProperties_t*) data;
426 
427    props->obj = obj;
428    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->x), rectangle->x);
429    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), rectangle->y);
430    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->width), rectangle->width);
431    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->height),
432                              rectangle->height);
433 }
434 
435 static void
rectangle_set_initial_focus(Object_t * obj,gpointer data)436 rectangle_set_initial_focus(Object_t *obj, gpointer data)
437 {
438    RectangleProperties_t *props = (RectangleProperties_t*) data;
439    gtk_widget_grab_focus(props->x);
440 }
441 
442 static void
rectangle_update(Object_t * obj,gpointer data)443 rectangle_update(Object_t* obj, gpointer data)
444 {
445    Rectangle_t *rectangle = ObjectToRectangle(obj);
446    RectangleProperties_t *props = (RectangleProperties_t*) data;
447 
448    rectangle->x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->x));
449    rectangle->y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->y));
450    rectangle->width = gtk_spin_button_get_value_as_int(
451       GTK_SPIN_BUTTON(props->width));
452    rectangle->height = gtk_spin_button_get_value_as_int(
453       GTK_SPIN_BUTTON(props->height));
454 }
455 
456 static void
rectangle_write_csim(Object_t * obj,gpointer param,OutputFunc_t output)457 rectangle_write_csim(Object_t *obj, gpointer param, OutputFunc_t output)
458 {
459    Rectangle_t *rectangle = ObjectToRectangle(obj);
460    output(param, "\"rect\" coords=\"%d,%d,%d,%d\"", rectangle->x, rectangle->y,
461           rectangle->x + rectangle->width, rectangle->y + rectangle->height);
462 }
463 
464 static void
rectangle_write_cern(Object_t * obj,gpointer param,OutputFunc_t output)465 rectangle_write_cern(Object_t *obj, gpointer param, OutputFunc_t output)
466 {
467    Rectangle_t *rectangle = ObjectToRectangle(obj);
468    output(param, "rect (%d,%d) (%d,%d)", rectangle->x, rectangle->y,
469           rectangle->x + rectangle->width, rectangle->y + rectangle->height);
470 }
471 
472 static void
rectangle_write_ncsa(Object_t * obj,gpointer param,OutputFunc_t output)473 rectangle_write_ncsa(Object_t *obj, gpointer param, OutputFunc_t output)
474 {
475    Rectangle_t *rectangle = ObjectToRectangle(obj);
476    output(param, "rect %s %d,%d %d,%d", obj->url,
477           rectangle->x, rectangle->y,
478           rectangle->x + rectangle->width, rectangle->y + rectangle->height);
479 }
480 
481 static const gchar*
rectangle_get_stock_icon_name(void)482 rectangle_get_stock_icon_name(void)
483 {
484    return IMAP_STOCK_RECTANGLE;
485 }
486 
487 static gboolean
rectangle_factory_finish(Object_t * obj,gint x,gint y)488 rectangle_factory_finish(Object_t *obj, gint x, gint y)
489 {
490    Rectangle_t *rectangle = ObjectToRectangle(obj);
491 
492    rectangle->width = x - rectangle->x;
493    rectangle->height = y - rectangle->y;
494 
495    rectangle_normalize(obj);
496 
497    return TRUE;
498 }
499 
500 static Object_t*
rectangle_factory_create_object(gint x,gint y)501 rectangle_factory_create_object(gint x, gint y)
502 {
503    return create_rectangle(x, y, 0, 0);
504 }
505 
506 static void
rectangle_factory_set_xy(Object_t * obj,guint state,gint x,gint y)507 rectangle_factory_set_xy(Object_t *obj, guint state, gint x, gint y)
508 {
509    Rectangle_t *rectangle = ObjectToRectangle(obj);
510 
511    rectangle->width = x - rectangle->x;
512    rectangle->height = y - rectangle->y;
513 
514    if (state & GDK_SHIFT_MASK){
515       gint width = abs(rectangle->width);
516       gint height = abs(rectangle->height);
517       if (width < height)
518          rectangle->height = (rectangle->height < 0) ? -width : width;
519       else
520          rectangle->width = (rectangle->width < 0) ? -height : height;
521    }
522 
523    main_set_dimension(rectangle->width, rectangle->height);
524 }
525 
526 static ObjectFactory_t rectangle_factory = {
527    NULL,                        /* Object pointer */
528    rectangle_factory_finish,
529    NULL,                        /* Cancel func */
530    rectangle_factory_create_object,
531    rectangle_factory_set_xy
532 };
533 
534 ObjectFactory_t*
get_rectangle_factory(guint state)535 get_rectangle_factory(guint state)
536 {
537    return &rectangle_factory;
538 }
539