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 <stdio.h>
26 #include <string.h>
27 
28 #include "libgimp/gimp.h"
29 #include "libgimp/gimpui.h"
30 
31 #include "imap_browse.h"
32 #include "imap_commands.h"
33 #include "imap_default_dialog.h"
34 #include "imap_edit_area_info.h"
35 #include "imap_main.h"
36 #include "imap_stock.h"
37 #include "imap_table.h"
38 
39 #include "libgimp/stdplugins-intl.h"
40 
41 static gboolean callback_lock;
42 
43 
44 static gchar*
relative_filter(const char * name,gpointer data)45 relative_filter(const char *name, gpointer data)
46 {
47    AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
48    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->relative_link)))
49       return g_path_get_basename(name);
50    return g_strdup (name);
51 }
52 
53 static void
url_changed(GtkWidget * widget,gpointer data)54 url_changed(GtkWidget *widget, gpointer data)
55 {
56    AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
57    const gchar *url = gtk_entry_get_text(GTK_ENTRY(param->url));
58    GtkWidget *button;
59 
60    if (!g_ascii_strncasecmp(url, "http://", sizeof("http://") - 1))
61       button = param->web_site;
62    else if (!g_ascii_strncasecmp(url, "ftp://", sizeof("ftp://") - 1))
63       button = param->ftp_site;
64    else if (!g_ascii_strncasecmp(url, "gopher://", sizeof("gopher://") - 1))
65       button = param->gopher;
66    else if (!g_ascii_strncasecmp(url, "file:/", sizeof("file:/") - 1))
67       button = param->file;
68    else if (!g_ascii_strncasecmp(url, "wais://", sizeof("wais://") - 1))
69       button = param->wais;
70    else if (!g_ascii_strncasecmp(url, "telnet://", sizeof("telnet://") - 1))
71       button = param->telnet;
72    else if (!g_ascii_strncasecmp(url, "mailto:", sizeof("mailto:") - 1))
73       button = param->email;
74    else
75       button = param->other;
76 
77    callback_lock = TRUE;
78    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
79 }
80 
81 static void
set_url(GtkWidget * widget,AreaInfoDialog_t * param,const gchar * prefix)82 set_url(GtkWidget *widget, AreaInfoDialog_t *param, const gchar *prefix)
83 {
84    if (callback_lock)
85      {
86        callback_lock = FALSE;
87      }
88    else
89      {
90        if (gtk_widget_get_state (widget) & GTK_STATE_SELECTED)
91          {
92            char *p;
93            gchar *url = g_strdup(gtk_entry_get_text(GTK_ENTRY(param->url)));
94 
95            p = strstr(url, "//");    /* 'http://' */
96            if (p)
97              {
98                p += 2;
99              }
100            else
101              {
102                p = strchr(url, ':');    /* 'mailto:' */
103                if (p)
104                  {
105                    p++;
106                    if (*p == '/')       /* 'file:/' */
107                      p++;
108                  }
109                else
110                  {
111                    p = url;
112                  }
113              }
114            p = g_strconcat(prefix, p, NULL);
115            gtk_entry_set_text(GTK_ENTRY(param->url), p);
116            g_free(p);
117            g_free(url);
118          }
119      }
120    gtk_widget_grab_focus(param->url);
121 }
122 
123 static void
select_web_cb(GtkWidget * widget,AreaInfoDialog_t * param)124 select_web_cb(GtkWidget *widget, AreaInfoDialog_t *param)
125 {
126    set_url(widget, param, "http://");
127 }
128 
129 static void
select_ftp_cb(GtkWidget * widget,AreaInfoDialog_t * param)130 select_ftp_cb(GtkWidget *widget, AreaInfoDialog_t *param)
131 {
132    set_url(widget, param, "ftp://");
133 }
134 
135 static void
select_gopher_cb(GtkWidget * widget,AreaInfoDialog_t * param)136 select_gopher_cb(GtkWidget *widget, AreaInfoDialog_t *param)
137 {
138    set_url(widget, param, "gopher://");
139 }
140 
141 static void
select_other_cb(GtkWidget * widget,AreaInfoDialog_t * param)142 select_other_cb(GtkWidget *widget, AreaInfoDialog_t *param)
143 {
144    set_url(widget, param, "");
145 }
146 
147 static void
select_file_cb(GtkWidget * widget,AreaInfoDialog_t * param)148 select_file_cb(GtkWidget *widget, AreaInfoDialog_t *param)
149 {
150    set_url(widget, param, "file:/");
151 }
152 
153 static void
select_wais_cb(GtkWidget * widget,AreaInfoDialog_t * param)154 select_wais_cb(GtkWidget *widget, AreaInfoDialog_t *param)
155 {
156    set_url(widget, param, "wais://");
157 }
158 
159 static void
select_telnet_cb(GtkWidget * widget,AreaInfoDialog_t * param)160 select_telnet_cb(GtkWidget *widget, AreaInfoDialog_t *param)
161 {
162    set_url(widget, param, "telnet://");
163 }
164 
165 static void
select_email_cb(GtkWidget * widget,AreaInfoDialog_t * param)166 select_email_cb(GtkWidget *widget, AreaInfoDialog_t *param)
167 {
168    set_url(widget, param, "mailto:");
169 }
170 
171 static void
append_page(GtkWidget * notebook,GtkWidget * page,const gchar * icon_name,const gchar * label_name)172 append_page (GtkWidget *notebook, GtkWidget *page, const gchar *icon_name,
173              const gchar *label_name)
174 {
175    GtkWidget *hbox, *icon, *label;
176 
177    hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1);
178    gtk_widget_show(hbox);
179 
180    icon = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
181    gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
182    gtk_widget_show (icon);
183 
184    label = gtk_label_new_with_mnemonic (label_name);
185    gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
186    gtk_widget_show (label);
187 
188    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, hbox);
189 }
190 
191 static void
create_link_tab(AreaInfoDialog_t * dialog,GtkWidget * notebook)192 create_link_tab(AreaInfoDialog_t *dialog, GtkWidget *notebook)
193 {
194    BrowseWidget_t *browse;
195    GtkWidget *table, *label;
196    GtkWidget *subtable, *frame;
197    GSList    *group;
198 
199    table = gtk_table_new(11, 1, FALSE);
200    gtk_container_set_border_width(GTK_CONTAINER(table), 12);
201    gtk_table_set_row_spacings(GTK_TABLE(table), 6);
202    gtk_widget_show(table);
203 
204    frame = gimp_frame_new(_("Link Type"));
205    gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 0, 1);
206    gtk_widget_show(frame);
207 
208    subtable = gtk_table_new(2, 4, FALSE);
209    gtk_container_add (GTK_CONTAINER(frame), subtable);
210    gtk_widget_show(subtable);
211 
212    dialog->web_site = create_radio_button_in_table(subtable, NULL, 0, 0,
213                                                    _("_Web Site"));
214    g_signal_connect(dialog->web_site, "toggled",
215                     G_CALLBACK (select_web_cb), (gpointer) dialog);
216    group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->web_site));
217 
218    dialog->ftp_site = create_radio_button_in_table(subtable, group, 0, 1,
219                                                    _("_Ftp Site"));
220    g_signal_connect(dialog->ftp_site, "toggled",
221                     G_CALLBACK (select_ftp_cb), (gpointer) dialog);
222    group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->ftp_site));
223 
224    dialog->gopher = create_radio_button_in_table(subtable, group, 0, 2,
225                                                  _("_Gopher"));
226    g_signal_connect(dialog->gopher, "toggled",
227                     G_CALLBACK (select_gopher_cb), (gpointer) dialog);
228    group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->gopher));
229 
230    dialog->other = create_radio_button_in_table(subtable, group, 0, 3,
231                                                 _("Ot_her"));
232    g_signal_connect(dialog->other, "toggled",
233                     G_CALLBACK (select_other_cb), (gpointer) dialog);
234    group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->other));
235 
236    dialog->file = create_radio_button_in_table(subtable, group, 1, 0,
237                                                _("F_ile"));
238    g_signal_connect(dialog->file, "toggled",
239                     G_CALLBACK (select_file_cb), (gpointer) dialog);
240    group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->file));
241 
242    dialog->wais = create_radio_button_in_table(subtable, group, 1, 1,
243                                                _("WAI_S"));
244    g_signal_connect(dialog->wais, "toggled",
245                     G_CALLBACK (select_wais_cb), (gpointer) dialog);
246    group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->wais));
247 
248    dialog->telnet = create_radio_button_in_table(subtable, group, 1, 2,
249                                                  _("Tel_net"));
250    g_signal_connect(dialog->telnet, "toggled",
251                     G_CALLBACK (select_telnet_cb), (gpointer) dialog);
252    group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->telnet));
253 
254    dialog->email = create_radio_button_in_table(subtable, group, 1, 3,
255                                                 _("e-_mail"));
256    g_signal_connect(dialog->email, "toggled",
257                     G_CALLBACK (select_email_cb), (gpointer) dialog);
258 
259    label = create_label_in_table(
260       table, 2, 0,
261       _("_URL to activate when this area is clicked: (required)"));
262 
263    browse = browse_widget_new( _("Select HTML file"));
264    browse_widget_set_filter(browse, relative_filter, (gpointer) dialog);
265    gtk_table_attach_defaults(GTK_TABLE(table), browse->hbox, 0, 1, 3, 4);
266    dialog->url = browse->file;
267    g_signal_connect(dialog->url, "changed", G_CALLBACK(url_changed),
268                     dialog);
269    gtk_label_set_mnemonic_widget(GTK_LABEL(label), dialog->url);
270 
271    dialog->relative_link = create_check_button_in_table(table, 4, 0,
272                                                         _("Relati_ve link"));
273    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->relative_link),
274                                 TRUE);
275 
276    label = create_label_in_table(
277       table, 6, 0,
278       _("_Target frame name/ID: (optional - used for FRAMES only)"));
279    dialog->target = create_entry_in_table(table, label, 7, 0);
280 
281    label = create_label_in_table(table, 9, 0, _("ALT te_xt: (optional)"));
282    dialog->comment = create_entry_in_table(table, label, 10, 0);
283 
284    append_page (notebook, table, GIMP_ICON_WEB, _("_Link"));
285 }
286 
287 static void
geometry_changed(Object_t * obj,gpointer data)288 geometry_changed(Object_t *obj, gpointer data)
289 {
290    AreaInfoDialog_t *dialog = (AreaInfoDialog_t*) data;
291    if (dialog->geometry_lock) {
292       dialog->geometry_lock = FALSE;
293    } else {
294       if (dialog->obj == obj) {
295          object_update_info_widget(obj, dialog->infotab);
296          obj->class->assign(obj, dialog->clone);
297       }
298    }
299 }
300 
301 static void
toggle_preview_cb(GtkWidget * widget,AreaInfoDialog_t * param)302 toggle_preview_cb(GtkWidget *widget, AreaInfoDialog_t *param)
303 {
304    param->preview = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
305    edit_area_info_dialog_emit_geometry_signal(param);
306 }
307 
308 static void
create_info_tab(AreaInfoDialog_t * dialog,GtkWidget * notebook)309 create_info_tab(AreaInfoDialog_t *dialog, GtkWidget *notebook)
310 {
311    GtkWidget *vbox, *frame, *preview;
312    Object_t *obj = dialog->obj;
313 
314    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
315    gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
316    gtk_widget_show(vbox);
317 
318    frame = gimp_frame_new(_("Dimensions"));
319    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
320    gtk_widget_show(frame);
321 
322    preview = gtk_check_button_new_with_mnemonic(_("Pre_view"));
323    g_signal_connect(preview, "toggled",
324                     G_CALLBACK (toggle_preview_cb), (gpointer) dialog);
325    gtk_box_pack_start(GTK_BOX(vbox), preview, FALSE, FALSE, 0);
326    gtk_widget_show(preview);
327 
328    dialog->infotab = obj->class->create_info_widget(frame);
329 
330    append_page (notebook, vbox, obj->class->get_stock_icon_name (),
331                 gettext (obj->class->name));
332 }
333 
334 static void
create_java_script_tab(AreaInfoDialog_t * dialog,GtkWidget * notebook)335 create_java_script_tab(AreaInfoDialog_t *dialog, GtkWidget *notebook)
336 {
337    GtkWidget *vbox, *table, *label;
338 
339    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
340    gtk_widget_show(vbox);
341 
342    table = gtk_table_new(11, 1, FALSE);
343    gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
344    gtk_container_set_border_width(GTK_CONTAINER(table), 12);
345    gtk_table_set_row_spacings(GTK_TABLE(table), 6);
346    gtk_widget_show(table);
347 
348    label = create_label_in_table(table, 0, 0, "o_nMouseover:");
349    dialog->mouse_over = create_entry_in_table(table, label, 1, 0);
350 
351    label = create_label_in_table(table, 3, 0, "on_Mouseout:");
352    dialog->mouse_out = create_entry_in_table(table, label, 4, 0);
353 
354    label = create_label_in_table(table, 6, 0, "on_Focus (HTML 4.0):");
355    dialog->focus = create_entry_in_table(table, label, 7, 0);
356 
357    label = create_label_in_table(table, 9, 0, "on_Blur (HTML 4.0):");
358    dialog->blur = create_entry_in_table(table, label, 10, 0);
359 
360    append_page (notebook, vbox, IMAP_STOCK_JAVA, _("_JavaScript"));
361 }
362 
363 static gboolean
object_was_changed(AreaInfoDialog_t * dialog)364 object_was_changed(AreaInfoDialog_t *dialog)
365 {
366    Object_t *clone = dialog->clone;
367    Object_t *obj = dialog->obj;
368    gint old_x, old_y, old_width, old_height;
369    gint new_x, new_y, new_width, new_height;
370 
371    object_get_dimensions(clone, &old_x, &old_y, &old_width, &old_height);
372    object_get_dimensions(obj, &new_x, &new_y, &new_width, &new_height);
373 
374    return new_x != old_x || new_y != old_y || new_width != old_width ||
375       new_height != old_height || clone->selected != obj->selected;
376 }
377 
378 static void
edit_area_apply_cb(gpointer data)379 edit_area_apply_cb(gpointer data)
380 {
381    AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
382    Object_t *obj = param->obj;
383 
384    object_set_url(obj, gtk_entry_get_text(GTK_ENTRY(param->url)));
385    object_set_target(obj, gtk_entry_get_text(GTK_ENTRY(param->target)));
386    object_set_comment(obj, gtk_entry_get_text(GTK_ENTRY(param->comment)));
387    object_set_mouse_over(obj,
388                          gtk_entry_get_text(GTK_ENTRY(param->mouse_over)));
389    object_set_mouse_out(obj, gtk_entry_get_text(GTK_ENTRY(param->mouse_out)));
390    object_set_focus(obj, gtk_entry_get_text(GTK_ENTRY(param->focus)));
391    object_set_blur(obj, gtk_entry_get_text(GTK_ENTRY(param->blur)));
392    object_update(obj, param->infotab);
393    update_shape(obj);
394 
395    if (object_was_changed(param))
396       preview_redraw();
397 }
398 
399 static void
edit_area_ok_cb(gpointer data)400 edit_area_ok_cb(gpointer data)
401 {
402    AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
403    Object_t *obj = param->obj;
404 
405    object_list_remove_geometry_cb(obj->list, param->geometry_cb_id);
406 
407    /* Fix me: nasty hack */
408    if (param->add)
409       command_list_add(edit_object_command_new(obj));
410 
411    edit_area_apply_cb(data);
412    object_unlock(obj);
413    object_unref(param->clone);
414 }
415 
416 static void
edit_area_cancel_cb(gpointer data)417 edit_area_cancel_cb(gpointer data)
418 {
419    AreaInfoDialog_t *dialog = (AreaInfoDialog_t*) data;
420    Object_t *obj = dialog->obj;
421    gboolean changed = object_was_changed(dialog);
422    gboolean selected = obj->selected;
423 
424    object_list_remove_geometry_cb(obj->list, dialog->geometry_cb_id);
425    object_unlock(obj);
426    object_assign(dialog->clone, obj);
427    obj->selected = selected;
428    object_unref(dialog->clone);
429 
430    if (changed)
431       preview_redraw();
432 }
433 
434 static void
switch_page(GtkWidget * widget,gpointer page,gint page_num,gpointer data)435 switch_page(GtkWidget *widget, gpointer page, gint page_num,
436             gpointer data)
437 {
438    AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
439    if (page_num == 0) {
440       gtk_widget_grab_focus(param->url);
441    } else if (page_num == 1) {
442       Object_t *obj = param->obj;
443       obj->class->set_initial_focus(obj, param->infotab);
444    } else {
445       gtk_widget_grab_focus(param->mouse_over);
446    }
447 }
448 
449 AreaInfoDialog_t*
create_edit_area_info_dialog(Object_t * obj)450 create_edit_area_info_dialog(Object_t *obj)
451 {
452    AreaInfoDialog_t *data = g_new(AreaInfoDialog_t, 1);
453    GtkWidget *notebook;
454 
455    data->geometry_lock = FALSE;
456    data->preview = FALSE;
457    data->obj = obj;
458    data->browse = NULL;
459    data->dialog = make_default_dialog(_("Area Settings"));
460    default_dialog_set_ok_cb(data->dialog, edit_area_ok_cb, data);
461    default_dialog_set_apply_cb(data->dialog, edit_area_apply_cb, data);
462    default_dialog_set_cancel_cb(data->dialog, edit_area_cancel_cb, data);
463 
464    data->notebook = notebook = gtk_notebook_new();
465    g_signal_connect_after(notebook, "switch-page",
466                           G_CALLBACK(switch_page), (gpointer) data);
467 
468    gtk_box_pack_start(GTK_BOX(data->dialog->vbox), notebook, TRUE, TRUE, 0);
469    create_link_tab(data, notebook);
470    create_info_tab(data, notebook);
471    create_java_script_tab(data, notebook);
472    gtk_widget_show(notebook);
473 
474    return data;
475 }
476 
477 void
edit_area_info_dialog_show(AreaInfoDialog_t * dialog,Object_t * obj,gboolean add)478 edit_area_info_dialog_show(AreaInfoDialog_t *dialog, Object_t *obj,
479                            gboolean add)
480 {
481    gchar *title;
482 
483    object_unlock(dialog->obj);
484    object_lock(obj);
485    dialog->obj = obj;
486    dialog->clone = object_clone(obj);
487    dialog->add = add;
488    object_fill_info_tab(obj, dialog->infotab);
489    gtk_entry_set_text(GTK_ENTRY(dialog->url), obj->url);
490    gtk_entry_set_text(GTK_ENTRY(dialog->target), obj->target);
491    gtk_entry_set_text(GTK_ENTRY(dialog->comment), obj->comment);
492    gtk_entry_set_text(GTK_ENTRY(dialog->mouse_over), obj->mouse_over);
493    gtk_entry_set_text(GTK_ENTRY(dialog->mouse_out), obj->mouse_out);
494    gtk_entry_set_text(GTK_ENTRY(dialog->focus), obj->focus);
495    gtk_entry_set_text(GTK_ENTRY(dialog->blur), obj->blur);
496    gtk_widget_grab_focus(dialog->url);
497 
498    dialog->geometry_cb_id =
499       object_list_add_geometry_cb(obj->list, geometry_changed, dialog);
500 
501    title = g_strdup_printf (_("Area #%d Settings"),
502                             object_get_position_in_list(obj) + 1);
503    default_dialog_set_title(dialog->dialog, title);
504    g_free (title);
505    default_dialog_show(dialog->dialog);
506 }
507 
508 void
edit_area_info_dialog_emit_geometry_signal(AreaInfoDialog_t * dialog)509 edit_area_info_dialog_emit_geometry_signal(AreaInfoDialog_t *dialog)
510 {
511    if (dialog->preview) {
512       dialog->geometry_lock = TRUE;
513       object_emit_geometry_signal(dialog->obj);
514    }
515 }
516