1 /*
2  * This is a plug-in for GIMP.
3  *
4  * Generates clickable image maps.
5  *
6  * Copyright (C) 1998-2005 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 <gtk/gtk.h>
26 
27 #include "imap_commands.h"
28 #include "imap_default_dialog.h"
29 #include "imap_grid.h"
30 #include "imap_main.h"
31 #include "imap_object.h"
32 #include "imap_string.h"
33 
34 typedef struct {
35    ObjectListCallbackFunc_t func;
36    gpointer data;
37 } ObjectListCB_t;
38 
39 static ObjectList_t *_paste_buffer;
40 
41 static gpointer
object_list_callback_add(ObjectListCallback_t * list,ObjectListCallbackFunc_t func,gpointer data)42 object_list_callback_add(ObjectListCallback_t *list,
43                          ObjectListCallbackFunc_t func, gpointer data)
44 {
45    ObjectListCB_t *cb = g_new(ObjectListCB_t, 1);
46    cb->func = func;
47    cb->data = data;
48    list->list = g_list_append(list->list, cb);
49    return cb;
50 }
51 
52 static void
object_list_callback_remove(ObjectListCallback_t * list,gpointer id)53 object_list_callback_remove(ObjectListCallback_t *list, gpointer id)
54 {
55    list->list = g_list_remove(list->list, id);
56 }
57 
58 static void
object_list_callback_call(ObjectListCallback_t * list,Object_t * obj)59 object_list_callback_call(ObjectListCallback_t *list, Object_t *obj)
60 {
61    GList *p;
62    for (p = list->list; p; p = p->next) {
63       ObjectListCB_t *cb = (ObjectListCB_t*) p->data;
64       cb->func(obj, cb->data);
65    }
66 }
67 
68 gpointer
object_list_add_changed_cb(ObjectList_t * list,ObjectListCallbackFunc_t func,gpointer data)69 object_list_add_changed_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
70                            gpointer data)
71 {
72    return object_list_callback_add(&list->changed_cb, func, data);
73 }
74 
75 gpointer
object_list_add_update_cb(ObjectList_t * list,ObjectListCallbackFunc_t func,gpointer data)76 object_list_add_update_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
77                           gpointer data)
78 {
79    return object_list_callback_add(&list->update_cb, func, data);
80 }
81 
82 gpointer
object_list_add_add_cb(ObjectList_t * list,ObjectListCallbackFunc_t func,gpointer data)83 object_list_add_add_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
84                        gpointer data)
85 {
86    return object_list_callback_add(&list->add_cb, func, data);
87 }
88 
89 gpointer
object_list_add_remove_cb(ObjectList_t * list,ObjectListCallbackFunc_t func,gpointer data)90 object_list_add_remove_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
91                           gpointer data)
92 {
93    return object_list_callback_add(&list->remove_cb, func, data);
94 }
95 
96 gpointer
object_list_add_select_cb(ObjectList_t * list,ObjectListCallbackFunc_t func,gpointer data)97 object_list_add_select_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
98                           gpointer data)
99 {
100    return object_list_callback_add(&list->select_cb, func, data);
101 }
102 
103 gpointer
object_list_add_move_cb(ObjectList_t * list,ObjectListCallbackFunc_t func,gpointer data)104 object_list_add_move_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
105                         gpointer data)
106 {
107    return object_list_callback_add(&list->move_cb, func, data);
108 }
109 
110 gpointer
object_list_add_geometry_cb(ObjectList_t * list,ObjectListCallbackFunc_t func,gpointer data)111 object_list_add_geometry_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
112                             gpointer data)
113 {
114    return object_list_callback_add(&list->geometry_cb, func, data);
115 }
116 
117 gpointer
paste_buffer_add_add_cb(ObjectListCallbackFunc_t func,gpointer data)118 paste_buffer_add_add_cb(ObjectListCallbackFunc_t func, gpointer data)
119 {
120    if (!_paste_buffer)
121       _paste_buffer = make_object_list();
122    return object_list_callback_add(&_paste_buffer->add_cb, func, data);
123 }
124 
125 gpointer
paste_buffer_add_remove_cb(ObjectListCallbackFunc_t func,gpointer data)126 paste_buffer_add_remove_cb(ObjectListCallbackFunc_t func, gpointer data)
127 {
128    if (!_paste_buffer)
129       _paste_buffer = make_object_list();
130    return object_list_callback_add(&_paste_buffer->remove_cb, func, data);
131 }
132 
133 void
object_list_remove_add_cb(ObjectList_t * list,gpointer id)134 object_list_remove_add_cb(ObjectList_t *list, gpointer id)
135 {
136    object_list_callback_remove(&list->add_cb, id);
137 }
138 
139 void
object_list_remove_select_cb(ObjectList_t * list,gpointer id)140 object_list_remove_select_cb(ObjectList_t *list, gpointer id)
141 {
142    object_list_callback_remove(&list->select_cb, id);
143 }
144 
145 void
object_list_remove_remove_cb(ObjectList_t * list,gpointer id)146 object_list_remove_remove_cb(ObjectList_t *list, gpointer id)
147 {
148    object_list_callback_remove(&list->remove_cb, id);
149 }
150 
151 void
object_list_remove_move_cb(ObjectList_t * list,gpointer id)152 object_list_remove_move_cb(ObjectList_t *list, gpointer id)
153 {
154    object_list_callback_remove(&list->move_cb, id);
155 }
156 
157 void
object_list_remove_geometry_cb(ObjectList_t * list,gpointer id)158 object_list_remove_geometry_cb(ObjectList_t *list, gpointer id)
159 {
160    object_list_callback_remove(&list->geometry_cb, id);
161 }
162 
163 Object_t*
object_init(Object_t * obj,ObjectClass_t * class)164 object_init(Object_t *obj, ObjectClass_t *class)
165 {
166    obj->class = class;
167    obj->refcount = 1;
168    obj->selected = FALSE;
169    obj->locked = FALSE;
170    obj->url = g_strdup("");
171    obj->target = g_strdup("");
172    obj->comment = g_strdup("");
173    obj->mouse_over = g_strdup("");
174    obj->mouse_out = g_strdup("");
175    obj->focus = g_strdup("");
176    obj->blur = g_strdup("");
177    return obj;
178 }
179 
180 static void
object_destruct(Object_t * obj)181 object_destruct(Object_t *obj)
182 {
183    if (obj->class->destruct)
184       obj->class->destruct(obj);
185    g_free(obj->url);
186    g_free(obj->target);
187    g_free(obj->comment);
188    g_free(obj->mouse_over);
189    g_free(obj->mouse_out);
190    g_free(obj->focus);
191    g_free(obj->blur);
192    g_free(obj);
193 }
194 
195 Object_t*
object_ref(Object_t * obj)196 object_ref(Object_t *obj)
197 {
198    obj->refcount++;
199    return obj;
200 }
201 
202 void
object_unref(Object_t * obj)203 object_unref(Object_t *obj)
204 {
205    if (!--obj->refcount)
206       object_destruct(obj);
207 }
208 
209 Object_t*
object_clone(Object_t * obj)210 object_clone(Object_t *obj)
211 {
212    Object_t *clone = obj->class->clone(obj);
213    clone->class = obj->class;
214    clone->refcount = 1;
215    clone->selected = obj->selected;
216    clone->locked = FALSE;
217    clone->url = g_strdup(obj->url);
218    clone->target = g_strdup(obj->target);
219    clone->comment = g_strdup(obj->comment);
220    clone->mouse_over = g_strdup(obj->mouse_over);
221    clone->mouse_out = g_strdup(obj->mouse_out);
222    clone->focus = g_strdup(obj->focus);
223    clone->blur = g_strdup(obj->blur);
224    return clone;
225 }
226 
227 static Object_t*
object_copy(Object_t * src,Object_t * des)228 object_copy(Object_t *src, Object_t *des)
229 {
230    des->class = src->class;
231    des->selected = src->selected;
232    des->locked = FALSE;
233    g_strreplace(&des->url, src->url);
234    g_strreplace(&des->target, src->target);
235    g_strreplace(&des->comment, src->comment);
236    g_strreplace(&des->mouse_over, src->mouse_over);
237    g_strreplace(&des->mouse_out, src->mouse_out);
238    g_strreplace(&des->focus, src->focus);
239    g_strreplace(&des->blur, src->blur);
240    return des;
241 }
242 
243 Object_t*
object_assign(Object_t * obj,Object_t * des)244 object_assign(Object_t *obj, Object_t *des)
245 {
246    obj->class->assign(obj, des);
247    return object_copy(obj, des);
248 }
249 
250 void
object_draw(Object_t * obj,cairo_t * cr)251 object_draw(Object_t *obj, cairo_t *cr)
252 {
253    PreferencesData_t *preferences = get_preferences();
254    ColorSelData_t *colors = &preferences->colors;
255    GdkColor *fg, *bg;
256    gdouble dash = 4.;
257 
258    if (obj->selected & 4) {
259       fg = &colors->interactive_fg;
260       bg = &colors->interactive_bg;
261       obj->selected &= ~4;
262    } else if (obj->selected) {
263       fg = &colors->selected_fg;
264       bg = &colors->selected_bg;
265    } else {
266       fg = &colors->normal_fg;
267       bg = &colors->normal_bg;
268    }
269 
270    cairo_save (cr);
271    gdk_cairo_set_source_color (cr, bg);
272    obj->class->draw(obj, cr);
273    gdk_cairo_set_source_color (cr, fg);
274    cairo_set_dash (cr, &dash, 1, 0.);
275    obj->class->draw(obj, cr);
276 
277    if (obj->selected && preferences->show_area_handle)
278       obj->class->draw_sashes(obj, cr);
279    cairo_restore (cr);
280 }
281 
282 void
object_edit(Object_t * obj,gboolean add)283 object_edit(Object_t *obj, gboolean add)
284 {
285    if (!obj->class->info_dialog)
286       obj->class->info_dialog = create_edit_area_info_dialog(obj);
287    edit_area_info_dialog_show(obj->class->info_dialog, obj, add);
288 }
289 
290 void
object_select(Object_t * obj)291 object_select(Object_t *obj)
292 {
293    obj->selected = TRUE;
294    object_list_callback_call(&obj->list->select_cb, obj);
295    object_emit_geometry_signal(obj);
296 }
297 
298 void
object_unselect(Object_t * obj)299 object_unselect(Object_t *obj)
300 {
301    obj->selected = FALSE;
302    object_list_callback_call(&obj->list->select_cb, obj);
303    object_emit_geometry_signal(obj);
304 }
305 
306 void
object_move(Object_t * obj,gint dx,gint dy)307 object_move(Object_t *obj, gint dx, gint dy)
308 {
309    obj->class->move(obj, dx, dy);
310    object_emit_geometry_signal(obj);
311 }
312 
313 void
object_move_sash(Object_t * obj,gint dx,gint dy)314 object_move_sash(Object_t *obj, gint dx, gint dy)
315 {
316    gint x, y, width, height;
317    MoveSashFunc_t sash_func;
318 
319    obj->class->get_dimensions(obj, &x, &y, &width, &height);
320    if (dx == 0)
321       x += (width / 2);
322    else
323       x += width;
324 
325    if (dy == 0)
326       y += (height / 2);
327    else
328       y += height;
329 
330    sash_func = obj->class->near_sash(obj, x, y);
331 
332    if (sash_func) {
333       sash_func(obj, dx, dy);
334       object_emit_geometry_signal(obj);
335    }
336 }
337 
338 void
object_remove(Object_t * obj)339 object_remove(Object_t *obj)
340 {
341    object_list_remove(obj->list, obj);
342    object_emit_geometry_signal(obj);
343 }
344 
345 void
object_lock(Object_t * obj)346 object_lock(Object_t *obj)
347 {
348    obj->locked = TRUE;
349 }
350 
351 void
object_unlock(Object_t * obj)352 object_unlock(Object_t *obj)
353 {
354    obj->locked = FALSE;
355 }
356 
357 void
object_set_url(Object_t * obj,const gchar * url)358 object_set_url(Object_t *obj, const gchar *url)
359 {
360    g_strreplace(&obj->url, url);
361 }
362 
363 void
object_set_target(Object_t * obj,const gchar * target)364 object_set_target(Object_t *obj, const gchar *target)
365 {
366    g_strreplace(&obj->target, target);
367 }
368 
369 void
object_set_comment(Object_t * obj,const gchar * comment)370 object_set_comment(Object_t *obj, const gchar *comment)
371 {
372    g_strreplace(&obj->comment, comment);
373 }
374 
375 void
object_set_mouse_over(Object_t * obj,const gchar * mouse_over)376 object_set_mouse_over(Object_t *obj, const gchar *mouse_over)
377 {
378    g_strreplace(&obj->mouse_over, mouse_over);
379 }
380 
381 void
object_set_mouse_out(Object_t * obj,const gchar * mouse_out)382 object_set_mouse_out(Object_t *obj, const gchar *mouse_out)
383 {
384    g_strreplace(&obj->mouse_out, mouse_out);
385 }
386 
387 void
object_set_focus(Object_t * obj,const gchar * focus)388 object_set_focus(Object_t *obj, const gchar *focus)
389 {
390    g_strreplace(&obj->focus, focus);
391 }
392 
393 void
object_set_blur(Object_t * obj,const gchar * blur)394 object_set_blur(Object_t *obj, const gchar *blur)
395 {
396    g_strreplace(&obj->blur, blur);
397 }
398 
399 gint
object_get_position_in_list(Object_t * obj)400 object_get_position_in_list(Object_t *obj)
401 {
402    return g_list_index(obj->list->list, (gpointer) obj);
403 }
404 
405 void
object_emit_changed_signal(Object_t * obj)406 object_emit_changed_signal(Object_t *obj)
407 {
408    object_list_callback_call(&obj->list->changed_cb, obj);
409 }
410 
411 void
object_emit_geometry_signal(Object_t * obj)412 object_emit_geometry_signal(Object_t *obj)
413 {
414    object_list_callback_call(&obj->list->geometry_cb, obj);
415 }
416 
417 void
object_emit_update_signal(Object_t * obj)418 object_emit_update_signal(Object_t *obj)
419 {
420    object_list_callback_call(&obj->list->update_cb, obj);
421 }
422 
423 void
do_object_locked_dialog(void)424 do_object_locked_dialog(void)
425 {
426    static DefaultDialog_t *dialog;
427    if (!dialog) {
428       dialog = make_default_dialog("Object locked");
429       default_dialog_hide_cancel_button(dialog);
430       default_dialog_hide_apply_button(dialog);
431       default_dialog_set_label(
432          dialog,
433          "\n  You cannot delete the selected object  \n"
434          "since it is currently being edited.\n");
435    }
436    default_dialog_show(dialog);
437 }
438 
439 static Object_t*
object_factory_create_object(ObjectFactory_t * factory,gint x,gint y)440 object_factory_create_object(ObjectFactory_t *factory, gint x, gint y)
441 {
442    return factory->obj = factory->create_object(x, y);
443 }
444 
445 static gboolean
button_motion(GtkWidget * widget,GdkEventMotion * event,ObjectFactory_t * factory)446 button_motion(GtkWidget *widget, GdkEventMotion *event,
447               ObjectFactory_t *factory)
448 {
449    gint x = get_real_coord((gint) event->x);
450    gint y = get_real_coord((gint) event->y);
451 
452    round_to_grid(&x, &y);
453 
454    factory->set_xy(factory->obj, event->state, x, y);
455 
456    preview_redraw ();
457 
458    return FALSE;
459 }
460 
461 gboolean
object_on_button_press(GtkWidget * widget,GdkEventButton * event,gpointer data)462 object_on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer data)
463 {
464    static ObjectFactory_t *factory;
465    PreferencesData_t *preferences = get_preferences();
466    gint x = get_real_coord((gint) event->x);
467    gint y = get_real_coord((gint) event->y);
468    static Object_t *obj;
469 
470    if (event->type == GDK_2BUTTON_PRESS)
471       return FALSE;
472    round_to_grid(&x, &y);
473 
474    if (obj) {
475       if (event->button == 1) {
476          if (!factory->finish || factory->finish(obj, x, y)) {
477             g_signal_handlers_disconnect_by_func(widget,
478                                                  button_motion,
479                                                  factory);
480             if (object_is_valid(obj)) {
481                Command_t *command = create_command_new(get_shapes(), obj);
482                command_execute(command);
483                if (preferences->prompt_for_area_info)
484                   object_edit(obj, FALSE);
485             } else {
486                object_unref(obj);
487             }
488             preview_unset_tmp_obj (obj);
489             preview_redraw ();
490             obj = NULL;
491             main_clear_dimension();
492          }
493       } else if (event->button == 3) {
494          if (!factory->cancel || factory->cancel(event, obj)) {
495             g_signal_handlers_disconnect_by_func(widget,
496                                                  button_motion,
497                                                  factory);
498             object_unref(obj);
499             preview_unset_tmp_obj (obj);
500             preview_redraw ();
501             obj = NULL;
502             main_clear_dimension();
503          }
504          return TRUE;
505       }
506    } else {
507       if (event->button == 1) {
508          factory = ((ObjectFactory_t*(*)(guint)) data)(event->state);
509          obj = object_factory_create_object(factory, x, y);
510          preview_set_tmp_obj (obj);
511 
512          g_signal_connect(widget, "motion-notify-event",
513                           G_CALLBACK(button_motion), factory);
514       }
515    }
516    return FALSE;
517 }
518 
519 ObjectList_t*
make_object_list(void)520 make_object_list(void)
521 {
522   return g_new0 (ObjectList_t, 1);
523 }
524 
525 void
object_list_destruct(ObjectList_t * list)526 object_list_destruct(ObjectList_t *list)
527 {
528    object_list_remove_all(list);
529    g_free(list->list);
530 }
531 
532 ObjectList_t*
object_list_append_list(ObjectList_t * des,ObjectList_t * src)533 object_list_append_list(ObjectList_t *des, ObjectList_t *src)
534 {
535    GList *p;
536    if (!src)
537      return des;
538    for (p = src->list; p; p = p->next)
539       object_list_append(des, object_clone((Object_t*) p->data));
540    object_list_set_changed(des, TRUE);
541    return des;
542 }
543 
544 ObjectList_t*
object_list_copy(ObjectList_t * des,ObjectList_t * src)545 object_list_copy(ObjectList_t *des, ObjectList_t *src)
546 {
547    if (des)
548       object_list_remove_all(des);
549    else
550       des = make_object_list();
551 
552    return object_list_append_list(des, src);
553 }
554 
555 void
object_list_append(ObjectList_t * list,Object_t * object)556 object_list_append(ObjectList_t *list, Object_t *object)
557 {
558    object->list = list;
559    list->list = g_list_append(list->list, (gpointer) object);
560    object_list_set_changed(list, TRUE);
561    object_list_callback_call(&list->add_cb, object);
562 }
563 
564 void
object_list_prepend(ObjectList_t * list,Object_t * object)565 object_list_prepend(ObjectList_t *list, Object_t *object)
566 {
567    object->list = list;
568    list->list = g_list_prepend(list->list, (gpointer) object);
569    object_list_set_changed(list, TRUE);
570    object_list_callback_call(&list->add_cb, object);
571 }
572 
573 void
object_list_insert(ObjectList_t * list,gint position,Object_t * object)574 object_list_insert(ObjectList_t *list, gint position, Object_t *object)
575 {
576    object->list = list;
577    list->list = g_list_insert(list->list, (gpointer) object, position);
578    object_list_set_changed(list, TRUE);
579    object_list_callback_call(&list->add_cb, object);
580 }
581 
582 void
object_list_remove(ObjectList_t * list,Object_t * object)583 object_list_remove(ObjectList_t *list, Object_t *object)
584 {
585    list->list = g_list_remove(list->list, (gpointer) object);
586    object_list_set_changed(list, TRUE);
587    object_list_callback_call(&list->remove_cb, object);
588    object_unref(object);
589 }
590 
591 void
object_list_remove_link(ObjectList_t * list,GList * link)592 object_list_remove_link(ObjectList_t *list, GList *link)
593 {
594    list->list = g_list_remove_link(list->list, link);
595    object_list_set_changed(list, TRUE);
596    object_list_callback_call(&list->remove_cb, (Object_t*) link->data);
597 }
598 
599 void
object_list_update(ObjectList_t * list,Object_t * object)600 object_list_update(ObjectList_t *list, Object_t *object)
601 {
602    object_list_callback_call(&list->update_cb, object);
603 }
604 
605 void
object_list_draw(ObjectList_t * list,cairo_t * cr)606 object_list_draw(ObjectList_t *list, cairo_t *cr)
607 {
608    GList *p;
609    for (p = list->list; p; p = p->next)
610       object_draw((Object_t*) p->data, cr);
611 }
612 
613 void
object_list_draw_selected(ObjectList_t * list,cairo_t * cr)614 object_list_draw_selected(ObjectList_t *list, cairo_t *cr)
615 {
616    GList *p;
617    for (p = list->list; p; p = p->next) {
618       Object_t *obj = (Object_t*) p->data;
619       if (obj->selected)
620          object_draw(obj, cr);
621    }
622 }
623 
624 Object_t*
object_list_find(ObjectList_t * list,gint x,gint y)625 object_list_find(ObjectList_t *list, gint x, gint y)
626 {
627    Object_t *found = NULL;
628    GList *p;
629    for (p = list->list; p; p = p->next) {
630       Object_t *obj = (Object_t*) p->data;
631       if (obj->class->point_is_on(obj, x, y))
632          found = obj;
633    }
634    return found;
635 }
636 
637 Object_t*
object_list_near_sash(ObjectList_t * list,gint x,gint y,MoveSashFunc_t * sash_func)638 object_list_near_sash(ObjectList_t *list, gint x, gint y,
639                       MoveSashFunc_t *sash_func)
640 {
641    Object_t *found = NULL;
642    GList *p;
643    for (p = list->list; p; p = p->next) {
644       Object_t *obj = (Object_t*) p->data;
645       if (obj->selected) {
646          MoveSashFunc_t func = obj->class->near_sash(obj, x, y);
647          if (func) {
648             found = obj;
649             *sash_func = func;
650          }
651       }
652    }
653    return found;
654 }
655 
656 void
object_list_remove_all(ObjectList_t * list)657 object_list_remove_all(ObjectList_t *list)
658 {
659    GList *p;
660    for (p = list->list; p; p = p->next) {
661       Object_t *obj = (Object_t*) p->data;
662       object_list_callback_call(&list->remove_cb, obj);
663       object_unref(obj);
664    }
665    g_list_free(list->list);
666    list->list = NULL;
667    object_list_set_changed(list, TRUE);
668 }
669 
670 void
clear_paste_buffer(void)671 clear_paste_buffer(void)
672 {
673    if (_paste_buffer)
674       object_list_remove_all(_paste_buffer);
675    else
676       _paste_buffer = make_object_list();
677 }
678 
679 ObjectList_t*
get_paste_buffer(void)680 get_paste_buffer(void)
681 {
682    return _paste_buffer;
683 }
684 
685 gint
object_list_cut(ObjectList_t * list)686 object_list_cut(ObjectList_t *list)
687 {
688    GList *p, *q;
689    gint count = 0;
690 
691    clear_paste_buffer();
692    for (p = list->list; p; p = q) {
693       Object_t *obj = (Object_t*) p->data;
694       q = p->next;
695       if (obj->selected) {
696          if (obj->locked) {
697             do_object_locked_dialog();
698          } else {
699             object_list_append(_paste_buffer, obj);
700             object_list_remove_link(list, p);
701             count++;
702          }
703       }
704    }
705    object_list_set_changed(list, (count) ? TRUE : FALSE);
706    return count;
707 }
708 
709 void
object_list_copy_to_paste_buffer(ObjectList_t * list)710 object_list_copy_to_paste_buffer(ObjectList_t *list)
711 {
712    GList *p;
713 
714    clear_paste_buffer();
715    for (p = list->list; p; p = p->next) {
716       Object_t *obj = (Object_t*) p->data;
717       if (obj->selected)
718          object_list_append(_paste_buffer, object_clone(obj));
719    }
720 }
721 
722 void
object_list_paste(ObjectList_t * list)723 object_list_paste(ObjectList_t *list)
724 {
725    object_list_append_list(list, _paste_buffer);
726 }
727 
728 void
object_list_delete_selected(ObjectList_t * list)729 object_list_delete_selected(ObjectList_t *list)
730 {
731    GList *p, *q;
732    for (p = list->list; p; p = q) {
733       Object_t *obj = (Object_t*) p->data;
734       q = p->next;
735       if (obj->selected) {
736          if (obj->locked) {
737             do_object_locked_dialog();
738          } else {
739             object_list_remove_link(list, p);
740             object_unref(obj);
741          }
742       }
743    }
744 }
745 
746 void
object_list_edit_selected(ObjectList_t * list)747 object_list_edit_selected(ObjectList_t *list)
748 {
749    GList *p;
750    for (p = list->list; p; p = p->next) {
751       Object_t *obj = (Object_t*) p->data;
752       if (obj->selected) {
753          object_edit(obj, TRUE);
754          break;
755       }
756    }
757 }
758 
759 gint
object_list_select_all(ObjectList_t * list)760 object_list_select_all(ObjectList_t *list)
761 {
762    GList *p;
763    gint count = 0;
764    for (p = list->list; p; p = p->next) {
765       Object_t *obj = (Object_t*) p->data;
766       if (!obj->selected) {
767          object_select(obj);
768          count++;
769       }
770    }
771    return count;
772 }
773 
774 void
object_list_select_next(ObjectList_t * list)775 object_list_select_next(ObjectList_t *list)
776 {
777    GList *p;
778    for (p = list->list; p; p = p->next) {
779       Object_t *obj = (Object_t*) p->data;
780       if (obj->selected) {
781          object_unselect(obj);
782          p = (p->next) ? p->next : list->list;
783          object_select((Object_t*) p->data);
784          for (p = p->next; p; p = p->next) {
785             obj = (Object_t*) p->data;
786             if (obj->selected)
787                object_unselect(obj);
788          }
789          break;
790       }
791    }
792 }
793 
object_list_select_prev(ObjectList_t * list)794 void object_list_select_prev(ObjectList_t *list)
795 {
796    GList *p;
797    for (p = list->list; p; p = p->next) {
798       Object_t *obj = (Object_t*) p->data;
799       if (obj->selected) {
800          GList *q = (p->prev) ? p->prev : g_list_last(list->list);
801          for (; p; p = p->next) {
802             obj = (Object_t*) p->data;
803             if (obj->selected)
804                object_unselect(obj);
805          }
806          object_select((Object_t*) q->data);
807          break;
808       }
809    }
810 }
811 
812 gint
object_list_select_region(ObjectList_t * list,gint x,gint y,gint width,gint height)813 object_list_select_region(ObjectList_t *list, gint x, gint y, gint width,
814                           gint height)
815 {
816    GList *p;
817    gint count = 0;
818    for (p = list->list; p; p = p->next) {
819       Object_t *obj = (Object_t*) p->data;
820       gint obj_x, obj_y, obj_width, obj_height;
821 
822       object_get_dimensions(obj, &obj_x, &obj_y, &obj_width, &obj_height);
823       if (obj_x >= x && obj_x + obj_width <= x + width &&
824           obj_y >= y && obj_y + obj_height <= y + height) {
825          object_select(obj);
826          count++;
827       }
828    }
829    return count;
830 }
831 
832 gint
object_list_deselect_all(ObjectList_t * list,Object_t * exception)833 object_list_deselect_all(ObjectList_t *list, Object_t *exception)
834 {
835    GList *p;
836    gint count = 0;
837    for (p = list->list; p; p = p->next) {
838       Object_t *obj = (Object_t*) p->data;
839       if (obj->selected && obj != exception) {
840          object_unselect(obj);
841          count++;
842       }
843    }
844    return count;
845 }
846 
847 gint
object_list_nr_selected(ObjectList_t * list)848 object_list_nr_selected(ObjectList_t *list)
849 {
850    GList *p;
851    gint count = 0;
852    for (p = list->list; p; p = p->next) {
853       Object_t *obj = (Object_t*) p->data;
854       if (obj->selected)
855          count++;
856    }
857    return count;
858 }
859 
860 void
object_list_resize(ObjectList_t * list,gint percentage_x,gint percentage_y)861 object_list_resize(ObjectList_t *list, gint percentage_x, gint percentage_y)
862 {
863    GList *p;
864    for (p = list->list; p; p = p->next) {
865       Object_t *obj = (Object_t*) p->data;
866       object_resize(obj, percentage_x, percentage_y);
867    }
868 }
869 
870 static void
object_list_swap_prev(ObjectList_t * list,GList * p)871 object_list_swap_prev(ObjectList_t *list, GList *p)
872 {
873    gpointer swap = p->data;
874    p->data = p->prev->data;
875    p->prev->data = swap;
876    object_list_callback_call(&list->move_cb, (Object_t*) p->data);
877    object_list_callback_call(&list->move_cb, (Object_t*) p->prev->data);
878 }
879 
880 static void
object_list_swap_next(ObjectList_t * list,GList * p)881 object_list_swap_next(ObjectList_t *list, GList *p)
882 {
883    gpointer swap = p->data;
884    p->data = p->next->data;
885    p->next->data = swap;
886    object_list_callback_call(&list->move_cb, (Object_t*) p->data);
887    object_list_callback_call(&list->move_cb, (Object_t*) p->next->data);
888 }
889 
890 void
object_list_move_selected(ObjectList_t * list,gint dx,gint dy)891 object_list_move_selected(ObjectList_t *list, gint dx, gint dy)
892 {
893    GList *p;
894    for (p = list->list; p; p = p->next) {
895       Object_t *obj = (Object_t*) p->data;
896       if (obj->selected)
897          object_move(obj, dx, dy);
898    }
899 }
900 
901 void
object_list_move_up(ObjectList_t * list,Object_t * obj)902 object_list_move_up(ObjectList_t *list, Object_t *obj)
903 {
904    GList *p = g_list_find(list->list, (gpointer) obj);
905    object_list_swap_prev(list, p);
906 }
907 
908 void
object_list_move_down(ObjectList_t * list,Object_t * obj)909 object_list_move_down(ObjectList_t *list, Object_t *obj)
910 {
911    GList *p = g_list_find(list->list, (gpointer) obj);
912    object_list_swap_next(list, p);
913 }
914 
915 void
object_list_move_selected_up(ObjectList_t * list)916 object_list_move_selected_up(ObjectList_t *list)
917 {
918    GList *p;
919 
920    for (p = list->list; p; p = p->next) {
921       Object_t *obj = (Object_t*) p->data;
922       if (obj->selected && p->prev)
923          object_list_swap_prev(list, p);
924    }
925 }
926 
927 void
object_list_move_selected_down(ObjectList_t * list)928 object_list_move_selected_down(ObjectList_t *list)
929 {
930    GList *p;
931 
932    for (p = g_list_last(list->list); p; p = p->prev) {
933       Object_t *obj = (Object_t*) p->data;
934       if (obj->selected && p->next)
935          object_list_swap_next(list, p);
936    }
937 }
938 
939 void
object_list_move_to_front(ObjectList_t * list)940 object_list_move_to_front(ObjectList_t *list)
941 {
942    GList *p, *q;
943    guint length = g_list_length(list->list);
944 
945    for (p = list->list; length; p = q, length--) {
946       Object_t *obj = (Object_t*) p->data;
947       q = p->next;
948       if (obj->selected) {
949          object_list_remove_link(list, p);
950          object_list_append(list, obj);
951       }
952    }
953 }
954 
955 void
object_list_send_to_back(ObjectList_t * list)956 object_list_send_to_back(ObjectList_t *list)
957 {
958    GList *p, *q;
959    guint length = g_list_length(list->list);
960 
961    for (p = list->list; length; p = q, length--) {
962       Object_t *obj = (Object_t*) p->data;
963       q = p->next;
964       if (obj->selected) {
965          object_list_remove_link(list, p);
966          object_list_prepend(list, obj);
967       }
968    }
969 }
970 
971 void
object_list_move_sash_selected(ObjectList_t * list,gint dx,gint dy)972 object_list_move_sash_selected(ObjectList_t *list, gint dx, gint dy)
973 {
974    GList *p;
975    for (p = list->list; p; p = p->next) {
976       Object_t *obj = (Object_t*) p->data;
977       if (obj->selected)
978          object_move_sash(obj, dx, dy);
979    }
980 }
981 
982 static void
write_xml_attrib(const gchar * attrib,const gchar * value,const gchar * default_text,gpointer param,OutputFunc_t output)983 write_xml_attrib(const gchar *attrib, const gchar *value,
984                  const gchar *default_text, gpointer param,
985                  OutputFunc_t output)
986 {
987    if (*value) {
988       gchar *escaped_value = g_markup_escape_text(value, -1);
989       output(param, " %s=\"%s\"", attrib, escaped_value);
990       g_free(escaped_value);
991    } else if (*default_text) {
992       output(param, " %s", default_text);
993    }
994 }
995 
996 void
object_list_write_csim(ObjectList_t * list,gpointer param,OutputFunc_t output)997 object_list_write_csim(ObjectList_t *list, gpointer param, OutputFunc_t output)
998 {
999    GList *p;
1000    for (p = list->list; p; p = p->next) {
1001       Object_t *obj = (Object_t*) p->data;
1002 
1003       output(param, "<area shape=");
1004       obj->class->write_csim(obj, param, output);
1005 
1006       write_xml_attrib("alt", obj->comment, "", param, output);
1007       write_xml_attrib("target", obj->target, "", param, output);
1008       write_xml_attrib("onmouseover", obj->mouse_over, "", param, output);
1009       write_xml_attrib("onmouseout", obj->mouse_out, "", param, output);
1010       write_xml_attrib("onfocus", obj->focus, "", param, output);
1011       write_xml_attrib("onblur", obj->blur, "", param, output);
1012       write_xml_attrib("href", obj->url, " nohref=\"nohref\"", param, output);
1013       output(param," />\n");
1014    }
1015 }
1016 
1017 void
object_list_write_cern(ObjectList_t * list,gpointer param,OutputFunc_t output)1018 object_list_write_cern(ObjectList_t *list, gpointer param, OutputFunc_t output)
1019 {
1020    GList *p;
1021    for (p = list->list; p; p = p->next) {
1022       Object_t *obj = (Object_t*) p->data;
1023       obj->class->write_cern(obj, param, output);
1024       output(param, " %s\n", obj->url);
1025    }
1026 }
1027 
1028 void
object_list_write_ncsa(ObjectList_t * list,gpointer param,OutputFunc_t output)1029 object_list_write_ncsa(ObjectList_t *list, gpointer param, OutputFunc_t output)
1030 {
1031    GList *p;
1032    for (p = list->list; p; p = p->next) {
1033       Object_t *obj = (Object_t*) p->data;
1034 
1035       if (*obj->comment)
1036          output(param, "# %s\n", obj->comment);
1037       obj->class->write_ncsa(obj, param, output);
1038       output(param, "\n");
1039    }
1040 }
1041