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