home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / imagemap / imap_polygon.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-05-22  |  22.7 KB  |  788 lines

  1. /*
  2.  * This is a plug-in for the GIMP.
  3.  *
  4.  * Generates clickable image maps.
  5.  *
  6.  * Copyright (C) 1998-1999 Maurits Rijk  lpeek.mrijk@consunet.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 2 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, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  *
  22.  */
  23.  
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26.  
  27. #include "config.h"
  28. #include "imap_cmd_delete_point.h"
  29. #include "imap_cmd_insert_point.h"
  30. #include "libgimp/stdplugins-intl.h"
  31. #include "imap_main.h"
  32. #include "imap_misc.h"
  33. #include "imap_object_popup.h"
  34. #include "imap_polygon.h"
  35. #include "imap_table.h"
  36.  
  37. #include "polygon.xpm"
  38.  
  39. #define MAX_POLYGON_POINTS 99
  40.  
  41. static gboolean polygon_is_valid(Object_t *obj);
  42. static void polygon_destruct(Object_t *obj);
  43. static Object_t *polygon_clone(Object_t *obj);
  44. static void polygon_assign(Object_t *obj, Object_t *des);
  45. static void polygon_draw(Object_t* obj, GdkWindow *window, GdkGC* gc);
  46. static void polygon_draw_sashes(Object_t* obj, GdkWindow *window, GdkGC* gc);
  47. static MoveSashFunc_t polygon_near_sash(Object_t *obj, gint x, gint y);
  48. static gboolean polygon_point_is_on(Object_t *obj, gint x, gint y);
  49. static void polygon_get_dimensions(Object_t *obj, gint *x, gint *y,
  50.                    gint *width, gint *height);
  51. static void polygon_resize(Object_t *obj, gint percentage_x,
  52.                gint percentage_y);
  53. static void polygon_move(Object_t *obj, gint dx, gint dy);
  54. static gpointer polygon_create_info_widget(GtkWidget *frame);
  55. static void polygon_update_info_widget(Object_t *obj, gpointer data);
  56. static void polygon_fill_info_tab(Object_t *obj, gpointer data);
  57. static void polygon_set_initial_focus(Object_t *obj, gpointer data);
  58. static void polygon_update(Object_t* obj, gpointer data);
  59. static void polygon_write_csim(Object_t* obj, gpointer param, 
  60.                    OutputFunc_t output);
  61. static void polygon_write_cern(Object_t* obj, gpointer param, 
  62.                    OutputFunc_t output);
  63. static void polygon_write_ncsa(Object_t* obj, gpointer param, 
  64.                    OutputFunc_t output);
  65. static void polygon_do_popup(Object_t *obj, GdkEventButton *event);
  66. static char** polygon_get_icon_data(void);
  67.  
  68. static ObjectClass_t polygon_class = {
  69.    N_("Polygon"),
  70.    NULL,            /* info_dialog */
  71.    NULL,            /* icon */
  72.    NULL,            /* mask */
  73.  
  74.    polygon_is_valid,
  75.    polygon_destruct,
  76.    polygon_clone,
  77.    polygon_assign,
  78.    NULL,            /* polygon_normalize */
  79.    polygon_draw,
  80.    polygon_draw_sashes,
  81.    polygon_near_sash,
  82.    polygon_point_is_on,
  83.    polygon_get_dimensions,
  84.    polygon_resize,
  85.    polygon_move,
  86.    polygon_create_info_widget,
  87.    polygon_update_info_widget,
  88.    polygon_fill_info_tab,
  89.    polygon_set_initial_focus,
  90.    polygon_update,
  91.    polygon_write_csim,
  92.    polygon_write_cern,
  93.    polygon_write_ncsa,
  94.    polygon_do_popup,
  95.    polygon_get_icon_data
  96. };
  97.  
  98. Object_t*
  99. create_polygon(GList *points)
  100. {
  101.    Polygon_t *polygon = g_new(Polygon_t, 1);
  102.    polygon->points = points;
  103.    return object_init(&polygon->obj, &polygon_class);
  104. }
  105.  
  106. static void
  107. polygon_free_list(Polygon_t *polygon)
  108. {
  109.    g_list_foreach(polygon->points, (GFunc) g_free, NULL);
  110.    g_list_free(polygon->points);
  111.    polygon->points = NULL;
  112. }
  113.  
  114. static void
  115. polygon_destruct(Object_t *obj)
  116. {
  117.    Polygon_t *polygon = ObjectToPolygon(obj);
  118.    polygon_free_list(polygon);
  119. }
  120.  
  121. static gboolean 
  122. polygon_is_valid(Object_t *obj)
  123. {
  124.    return g_list_length(ObjectToPolygon(obj)->points) > 2;
  125. }
  126.  
  127. static Object_t*
  128. polygon_clone(Object_t *obj)
  129. {
  130.    Polygon_t *polygon = ObjectToPolygon(obj);
  131.    Polygon_t *clone = g_new(Polygon_t, 1);
  132.    GList     *p;
  133.    
  134.    clone->points = NULL;
  135.    for (p = polygon->points; p; p = p->next) {
  136.       GdkPoint *point = (GdkPoint*) p->data;
  137.       clone->points = g_list_append(clone->points, 
  138.                     new_point(point->x, point->y));
  139.    }
  140.    return &clone->obj;
  141. }
  142.  
  143. static void
  144. polygon_assign(Object_t *obj, Object_t *des)
  145. {
  146.    Polygon_t *src_polygon = ObjectToPolygon(obj);
  147.    Polygon_t *des_polygon = ObjectToPolygon(des);
  148.    GList     *p;
  149.  
  150.    polygon_free_list(des_polygon);
  151.    for (p = src_polygon->points; p; p = p->next) {
  152.       GdkPoint *point = (GdkPoint*) p->data;
  153.       des_polygon->points = g_list_append(des_polygon->points, 
  154.                       new_point(point->x, point->y));
  155.    }
  156. }
  157.  
  158. static void
  159. polygon_draw(Object_t *obj, GdkWindow *window, GdkGC *gc)
  160. {
  161.    Polygon_t *polygon = ObjectToPolygon(obj);
  162.    draw_polygon(window, gc, polygon->points);
  163. }
  164.  
  165. static void
  166. polygon_draw_sashes(Object_t *obj, GdkWindow *window, GdkGC *gc)
  167. {
  168.    Polygon_t *polygon = ObjectToPolygon(obj);
  169.    GList     *p;
  170.    for (p = polygon->points; p; p = p->next) {
  171.       GdkPoint *point = (GdkPoint*) p->data;
  172.       draw_sash(window, gc, point->x, point->y);
  173.    }
  174. }
  175.  
  176. static GdkPoint *_sash_point;
  177. static gint _sash_index;
  178.  
  179. static void
  180. move_sash(Object_t *obj, gint dx, gint dy)
  181. {
  182.    _sash_point->x += dx;
  183.    _sash_point->y += dy;
  184. }
  185.  
  186. static MoveSashFunc_t
  187. polygon_near_sash(Object_t *obj, gint x, gint y)
  188. {
  189.    Polygon_t *polygon = ObjectToPolygon(obj);
  190.    GList     *p;
  191.  
  192.    _sash_index = 0;
  193.    for (p = polygon->points; p; p = p->next, _sash_index++) {
  194.       GdkPoint *point = (GdkPoint*) p->data;
  195.       if (near_sash(point->x, point->y, x, y)) {
  196.      _sash_point = point;
  197.      return move_sash;
  198.       }
  199.    }
  200.    return NULL;
  201. }
  202.  
  203. static gboolean
  204. right_intersect(GdkPoint *p1, GdkPoint *p2, gint x, gint y)
  205. {
  206.    gint dx = p2->x - p1->x;
  207.    gint dy = p2->y - p1->y;
  208.  
  209.    if ((dy > 0 && y > p1->y && y < p2->y) ||
  210.        (dy < y && y > p2->y && y < p1->y)) {
  211.       gint sx = p1->x + (y - p1->y) * dx / dy;
  212.       return sx > x;
  213.    }
  214.    return FALSE;
  215. }
  216.  
  217. static gboolean
  218. polygon_point_is_on(Object_t *obj, gint x, gint y)
  219. {
  220.    Polygon_t *polygon = ObjectToPolygon(obj);
  221.    GList     *p;
  222.    int        count = 0;
  223.    GdkPoint  *first, *prev;
  224.  
  225.    p = polygon->points;
  226.    first = prev = (GdkPoint*) p->data;
  227.    p = p->next;
  228.  
  229.    for (; p; p = p->next) {
  230.       GdkPoint *point = (GdkPoint*) p->data;
  231.       if (right_intersect(prev, point, x, y))
  232.      count++;
  233.       prev = point;
  234.    }
  235.    if (right_intersect(prev, first, x, y))
  236.        count++;
  237.  
  238.    return count % 2;
  239. }
  240.  
  241. static void 
  242. polygon_get_dimensions(Object_t *obj, gint *x, gint *y,
  243.                gint *width, gint *height)
  244. {
  245.    Polygon_t *polygon = ObjectToPolygon(obj);
  246.    gint min_x = G_MAXINT, min_y = G_MAXINT;
  247.    gint max_x = G_MININT, max_y = G_MININT;
  248.    GList     *p;
  249.  
  250.    for (p = polygon->points; p; p = p->next) {
  251.       GdkPoint *point = (GdkPoint*) p->data;
  252.       if (point->x < min_x)
  253.      min_x = point->x;
  254.       if (point->x > max_x)
  255.      max_x = point->x;
  256.       if (point->y < min_y)
  257.      min_y = point->y;
  258.       if (point->y > max_y)
  259.      max_y = point->y;
  260.    }
  261.    *x = min_x;
  262.    *y = min_y;
  263.    *width = max_x - min_x;
  264.    *height = max_y - min_y;
  265. }
  266.  
  267. static void 
  268. polygon_resize(Object_t *obj, gint percentage_x, gint percentage_y)
  269. {
  270.    Polygon_t *polygon = ObjectToPolygon(obj);
  271.    GList     *p;
  272.    for (p = polygon->points; p; p = p->next) {
  273.       GdkPoint *point = (GdkPoint*) p->data;
  274.       point->x = point->x * percentage_x / 100;
  275.       point->y = point->y * percentage_y / 100;
  276.    }
  277. }
  278.  
  279. static void
  280. polygon_move(Object_t *obj, gint dx, gint dy)
  281. {
  282.    Polygon_t *polygon = ObjectToPolygon(obj);
  283.    GList     *p;
  284.    for (p = polygon->points; p; p = p->next) {
  285.       GdkPoint *point = (GdkPoint*) p->data;
  286.       point->x += dx;
  287.       point->y += dy;
  288.    }
  289. }
  290.  
  291. typedef struct {
  292.    Object_t  *obj;
  293.    GtkWidget *list;
  294.    GtkWidget *x;
  295.    GtkWidget *y;
  296.    GtkWidget *update;
  297.    GtkWidget *insert;
  298.    GtkWidget *append;
  299.    GtkWidget *remove;
  300.    gint          selected_row;
  301.    gint          timeout;
  302. } PolygonProperties_t;
  303.  
  304. static void
  305. select_row_cb(GtkWidget *widget, gint row, gint column, GdkEventButton *event,
  306.           PolygonProperties_t *data)
  307. {
  308.    gchar *text;
  309.    data->selected_row = row;
  310.  
  311.    _sash_point = g_list_nth(ObjectToPolygon(data->obj)->points, row)->data;
  312.  
  313.    gtk_clist_get_text(GTK_CLIST(data->list), row, 0, &text);
  314.    gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->x), atoi(text));
  315.  
  316.    gtk_clist_get_text(GTK_CLIST(data->list), row, 1, &text);
  317.    gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->y), atoi(text));
  318. }
  319.  
  320. static void
  321. update_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
  322. {
  323.    gtk_clist_set_text(GTK_CLIST(data->list), data->selected_row, 0,
  324.               gtk_entry_get_text(GTK_ENTRY(data->x)));
  325.    gtk_clist_set_text(GTK_CLIST(data->list), data->selected_row, 1,
  326.               gtk_entry_get_text(GTK_ENTRY(data->y)));
  327. }
  328.  
  329. static void
  330. set_buttons_sensitivity(PolygonProperties_t *data)
  331. {
  332.    gint rows = GTK_CLIST(data->list)->rows;
  333.    gint sensitive = (rows != MAX_POLYGON_POINTS) && (rows > 2);
  334.    gtk_widget_set_sensitive(data->insert, sensitive);
  335.    gtk_widget_set_sensitive(data->append, sensitive);
  336. }
  337.  
  338. static void
  339. insert_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
  340. {
  341.    gchar *text[2];
  342.    text[0] = gtk_entry_get_text(GTK_ENTRY(data->x));
  343.    text[1] = gtk_entry_get_text(GTK_ENTRY(data->y));
  344.    gtk_clist_insert(GTK_CLIST(data->list), data->selected_row, text);
  345.    set_buttons_sensitivity(data);
  346. }
  347.  
  348. static void
  349. append_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
  350. {
  351.    gchar *text[2];
  352.    text[0] = gtk_entry_get_text(GTK_ENTRY(data->x));
  353.    text[1] = gtk_entry_get_text(GTK_ENTRY(data->y));
  354.    gtk_clist_append(GTK_CLIST(data->list), text);
  355.    set_buttons_sensitivity(data);
  356. }
  357.  
  358. static void
  359. remove_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
  360. {
  361.    gtk_clist_remove(GTK_CLIST(data->list), data->selected_row);
  362.    set_buttons_sensitivity(data);
  363. }
  364.  
  365. static void
  366. x_changed_cb(GtkWidget *widget, gpointer data)
  367. {
  368.    Object_t *obj = ((PolygonProperties_t*) data)->obj;
  369.    gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
  370.    _sash_point->x = x;
  371.    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
  372. }
  373.  
  374. static void
  375. y_changed_cb(GtkWidget *widget, gpointer data)
  376. {
  377.    Object_t *obj = ((PolygonProperties_t*) data)->obj;
  378.    gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
  379.    _sash_point->y = y;
  380.    edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
  381. }
  382.  
  383. static gpointer
  384. polygon_create_info_widget(GtkWidget *frame)
  385. {
  386.    PolygonProperties_t *props = g_new(PolygonProperties_t, 1);
  387.    GtkWidget *hbox, *swin, *table;
  388.    GtkWidget *list;
  389.    gint max_width = get_image_width();
  390.    gint max_height = get_image_height();
  391.    gchar *titles[] = {N_("x (pixels)"), N_("y (pixels)")};
  392.    gint i;
  393.  
  394.    hbox = gtk_hbox_new(FALSE, 1);
  395.    gtk_container_add(GTK_CONTAINER(frame), hbox);
  396.    gtk_widget_show(hbox);
  397.  
  398.    for (i = 0; i < 2; i++)
  399.      titles[i] = gettext(titles[i]);
  400.    props->list = list = gtk_clist_new_with_titles(2, titles);
  401.    gtk_clist_column_titles_passive(GTK_CLIST(list));
  402.  
  403.    swin = gtk_scrolled_window_new(NULL, NULL);
  404.    gtk_container_set_border_width(GTK_CONTAINER(swin), 10);
  405.  
  406.    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), 
  407.                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
  408.  
  409.    gtk_box_pack_start(GTK_BOX(hbox), swin, FALSE, FALSE, FALSE);
  410.    gtk_widget_show(swin);
  411.  
  412.    gtk_clist_set_shadow_type(GTK_CLIST(list), GTK_SHADOW_ETCHED_IN);
  413.    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swin), list);
  414.  
  415.    gtk_clist_set_column_width(GTK_CLIST(list), 0, 48);
  416.    gtk_clist_set_column_width(GTK_CLIST(list), 1, 48);
  417.    gtk_clist_set_column_justification(GTK_CLIST(list), 0, GTK_JUSTIFY_RIGHT);
  418.    gtk_clist_set_column_justification(GTK_CLIST(list), 1, GTK_JUSTIFY_RIGHT);
  419.  
  420.    gtk_signal_connect(GTK_OBJECT(list), "select_row",
  421.               GTK_SIGNAL_FUNC(select_row_cb), props);
  422.    gtk_clist_set_selection_mode(GTK_CLIST(list), GTK_SELECTION_SINGLE);
  423.    gtk_widget_show(list);
  424.  
  425.    table = gtk_table_new(6, 3, FALSE);
  426.    gtk_container_set_border_width(GTK_CONTAINER(table), 10);
  427.    gtk_table_set_row_spacings(GTK_TABLE(table), 10);
  428.    gtk_table_set_col_spacings(GTK_TABLE(table), 10);
  429.    gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, FALSE);
  430.    gtk_widget_show(table);
  431.  
  432.    create_label_in_table(table, 0, 0, "x:");
  433.    props->x = create_spin_button_in_table(table, 0, 1, 1, 0, max_width - 1);
  434.    gtk_signal_connect(GTK_OBJECT(props->x), "changed", 
  435.               (GtkSignalFunc) x_changed_cb, (gpointer) props);
  436.    gtk_widget_set_usize(props->x, 64, -1);
  437.    create_label_in_table(table, 0, 2, _("pixels"));
  438.  
  439.    create_label_in_table(table, 1, 0, "y:");
  440.    props->y = create_spin_button_in_table(table, 1, 1, 1, 0, max_height - 1);
  441.    gtk_signal_connect(GTK_OBJECT(props->y), "changed", 
  442.               (GtkSignalFunc) y_changed_cb, (gpointer) props);
  443.    gtk_widget_set_usize(props->y, 64, -1);
  444.    create_label_in_table(table, 1, 2, _("pixels"));
  445.  
  446.    props->update = gtk_button_new_with_label(_("Update"));
  447.    gtk_signal_connect(GTK_OBJECT(props->update), "clicked",
  448.               GTK_SIGNAL_FUNC(update_button_clicked), props);
  449.    gtk_table_attach_defaults(GTK_TABLE(table), props->update, 1, 2, 2, 3);
  450.    gtk_widget_show(props->update);
  451.  
  452.    props->insert = gtk_button_new_with_label(_("Insert"));
  453.    gtk_signal_connect(GTK_OBJECT(props->insert), "clicked",
  454.               GTK_SIGNAL_FUNC(insert_button_clicked), props);
  455.    gtk_table_attach_defaults(GTK_TABLE(table), props->insert, 1, 2, 3, 4);
  456.    gtk_widget_show(props->insert);
  457.  
  458.    props->append = gtk_button_new_with_label(_("Append"));
  459.    gtk_signal_connect(GTK_OBJECT(props->append), "clicked",
  460.               GTK_SIGNAL_FUNC(append_button_clicked), props);
  461.    gtk_table_attach_defaults(GTK_TABLE(table), props->append, 1, 2, 4, 5);
  462.    gtk_widget_show(props->append);
  463.  
  464.    props->remove = gtk_button_new_with_label(_("Remove"));
  465.    gtk_signal_connect(GTK_OBJECT(props->remove), "clicked",
  466.               GTK_SIGNAL_FUNC(remove_button_clicked), props);
  467.    gtk_table_attach_defaults(GTK_TABLE(table), props->remove, 1, 2, 5, 6);
  468.    gtk_widget_show(props->remove);
  469.  
  470.    props->timeout = 0;
  471.  
  472.    return props;
  473. }
  474.  
  475. static gint
  476. update_timeout(gpointer data)
  477. {
  478.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  479.    polygon_fill_info_tab(props->obj, data);
  480.    return FALSE;
  481. }
  482.  
  483. static void
  484. polygon_update_info_widget(Object_t *obj, gpointer data)
  485. {
  486.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  487.    
  488.    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->x), _sash_point->x);
  489.    gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), _sash_point->y);
  490.    
  491.    if (props->selected_row != _sash_index) {
  492.       props->selected_row = _sash_index;
  493.       gtk_clist_select_row(GTK_CLIST(props->list), _sash_index, -1);
  494.    }
  495.    if (props->timeout)
  496.       gtk_timeout_remove(props->timeout);
  497.    props->timeout = gtk_timeout_add(1000, update_timeout, data);
  498. }
  499.  
  500. static void
  501. polygon_fill_info_tab(Object_t *obj, gpointer data)
  502. {
  503.    Polygon_t *polygon = ObjectToPolygon(obj);
  504.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  505.    GList     *p;
  506.  
  507.    props->obj = obj;
  508.    gtk_clist_freeze(GTK_CLIST(props->list));
  509.    gtk_clist_clear(GTK_CLIST(props->list));
  510.    for (p = polygon->points; p; p = p->next) {
  511.       GdkPoint *point = (GdkPoint*) p->data;
  512.       char x[16], y[16];
  513.       char *text[2];
  514.  
  515.       text[0] = x;
  516.       text[1] = y;
  517.       sprintf(x, "%d", point->x);
  518.       sprintf(y, "%d", point->y);
  519.       gtk_clist_append(GTK_CLIST(props->list), text);
  520.    }
  521.    gtk_clist_select_row(GTK_CLIST(props->list), _sash_index, -1);
  522.    gtk_clist_thaw(GTK_CLIST(props->list));
  523.  
  524.    set_buttons_sensitivity(props);
  525. }
  526.  
  527. static void 
  528. polygon_set_initial_focus(Object_t *obj, gpointer data)
  529. {
  530.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  531.    gtk_widget_grab_focus(props->x);
  532. }
  533.  
  534. static void 
  535. polygon_update(Object_t* obj, gpointer data)
  536. {
  537.    Polygon_t *polygon = ObjectToPolygon(obj);
  538.    PolygonProperties_t *props = (PolygonProperties_t*) data;
  539.    gint rows = GTK_CLIST(props->list)->rows;
  540.    int i;
  541.  
  542.    g_list_free(polygon->points);
  543.    polygon->points = NULL;
  544.  
  545.    for (i = 0; i < rows; i++) {
  546.       gchar *text;
  547.       GdkPoint *point = g_new(GdkPoint, 1);
  548.  
  549.       gtk_clist_get_text(GTK_CLIST(props->list), i, 0, &text);
  550.       point->x = atoi(text);
  551.       gtk_clist_get_text(GTK_CLIST(props->list), i, 1, &text);
  552.       point->y = atoi(text);
  553.  
  554.       polygon->points = g_list_append(polygon->points, point);
  555.    }
  556. }
  557.  
  558. static void
  559. polygon_write_csim(Object_t *obj, gpointer param, OutputFunc_t output)
  560. {
  561.    Polygon_t *polygon = ObjectToPolygon(obj);
  562.    GList     *p;
  563.  
  564.    output(param, "\"POLY\" COORDS=\"");
  565.    for (p = polygon->points; p; p = p->next) {
  566.       GdkPoint *point = (GdkPoint*) p->data;
  567.       output(param, "%d,%d", point->x, point->y);
  568.       output(param, "%c", (p->next) ? ',' : '"');
  569.    }
  570. }
  571.  
  572. static void
  573. polygon_write_cern(Object_t *obj, gpointer param, OutputFunc_t output)
  574. {
  575.    Polygon_t *polygon = ObjectToPolygon(obj);
  576.    GList     *p;
  577.    GdkPoint  *first = (GdkPoint*) polygon->points->data;
  578.  
  579.    output(param, "poly ");
  580.    for (p = polygon->points; p; p = p->next) {
  581.       GdkPoint *point = (GdkPoint*) p->data;
  582.       output(param, "(%d,%d) ", point->x, point->y);
  583.    }
  584.    output(param, "(%d,%d)", first->x, first->y);
  585. }
  586.  
  587. static void
  588. polygon_write_ncsa(Object_t *obj, gpointer param, OutputFunc_t output)
  589. {
  590.    Polygon_t *polygon = ObjectToPolygon(obj);
  591.    GList     *p;
  592.    GdkPoint  *first = (GdkPoint*) polygon->points->data;
  593.  
  594.    output(param, "poly %s", obj->url);
  595.    for (p = polygon->points; p; p = p->next) {
  596.       GdkPoint *point = (GdkPoint*) p->data;
  597.       output(param, " %d,%d", point->x, point->y);
  598.    }
  599.    output(param, " %d,%d", first->x, first->y);
  600. }
  601.  
  602. static gint _insert_edge;
  603. static gint _insert_x;
  604. static gint _insert_y;
  605.  
  606. static void
  607. polygon_insert_point(GtkWidget *widget, gpointer data)
  608. {
  609.    Command_t *command = insert_point_command_new(get_popup_object(), _insert_x,
  610.                          _insert_y, _insert_edge);
  611.    command_execute(command);
  612. }
  613.  
  614. static void
  615. polygon_delete_point(GtkWidget *widget, gpointer data)
  616. {
  617.    Command_t *command = delete_point_command_new(get_popup_object(), 
  618.                          _sash_point);
  619.    command_execute(command);
  620. }
  621.  
  622. static gboolean
  623. point_near_edge(GdkPoint *first, GdkPoint *second, gint x, gint y)
  624. {
  625.    gint den, nom;
  626.    gdouble u;
  627.  
  628.    den = (first->x - x) * (first->x - second->x) +
  629.       (first->y - y) * (first->y - second->y);
  630.    nom = (second->x - first->x) * (second->x - first->x) +
  631.       (second->y - first->y) * (second->y - first->y);
  632.    u = (gdouble) den / nom;
  633.    if (u >= 0.0 && u <= 1.0) {
  634.       gint sx = first->x + (gint) (u * (second->x - first->x)) - x;
  635.       gint sy = first->y + (gint) (u * (second->y - first->y)) - y;
  636.       return sx * sx + sy * sy <= 25; /* Fix me! */
  637.    }
  638.    return 0;
  639. }
  640.  
  641. static gint
  642. polygon_near_edge(Object_t *obj, gint x, gint y)
  643. {
  644.    Polygon_t *polygon = ObjectToPolygon(obj);
  645.    GList     *p = polygon->points;
  646.    GdkPoint  *first = (GdkPoint*) p->data;
  647.    GdkPoint  *prev = first;
  648.    gint n = 1;
  649.  
  650.    for (p = p->next; p; p = p->next, n++) {
  651.       GdkPoint *next = (GdkPoint*) p->data;
  652.       if (point_near_edge(prev, next, x, y))
  653.      return n;
  654.       prev = next;
  655.    }
  656.    return (point_near_edge(prev, first, x, y)) ? n + 1 : 0;
  657. }
  658.  
  659. static void 
  660. polygon_do_popup(Object_t *obj, GdkEventButton *event)
  661. {
  662.    gint x = get_real_coord((gint) event->x);
  663.    gint y = get_real_coord((gint) event->y);
  664.  
  665.    if (polygon_near_sash(obj, x, y)) {
  666.       static ObjectPopup_t *delete_popup;
  667.       if (!delete_popup) {
  668.      delete_popup = make_object_popup();
  669.      object_popup_prepend_menu(delete_popup, _("Delete Point"), 
  670.                    polygon_delete_point, delete_popup);
  671.       }
  672.       object_handle_popup(delete_popup, obj, event);
  673.    } else {
  674.       _insert_edge = polygon_near_edge(obj, x, y);
  675.       if (_insert_edge) {
  676.      static ObjectPopup_t *insert_popup;
  677.  
  678.      _insert_x = x;
  679.      _insert_y = y;
  680.  
  681.      if (!insert_popup) {
  682.         insert_popup = make_object_popup();
  683.         object_popup_prepend_menu(insert_popup, _("Insert Point"), 
  684.                       polygon_insert_point, insert_popup);
  685.      }
  686.      object_handle_popup(insert_popup, obj, event);
  687.       } else {
  688.      object_do_popup(obj, event);
  689.       }
  690.    }
  691. }
  692.  
  693. static char** 
  694. polygon_get_icon_data(void)
  695. {
  696.    return polygon_xpm;
  697. }
  698.  
  699. static GList *_prev_link;
  700.  
  701. static gboolean
  702. polygon_factory_finish(Object_t *obj, gint x, gint y)
  703. {
  704.    Polygon_t *polygon = ObjectToPolygon(obj);
  705.    GdkPoint *prev_point = (GdkPoint*) _prev_link->data;
  706.  
  707.    if (x == prev_point->x && y == prev_point->y) {
  708.       polygon_remove_last_point(polygon);
  709.       return TRUE;
  710.    } else {
  711.       g_list_append(polygon->points, new_point(x, y));
  712.       _prev_link = _prev_link->next;
  713.    }
  714.    return FALSE;
  715. }
  716.  
  717. static gboolean
  718. polygon_factory_cancel(GdkEventButton *event, Object_t *obj)
  719. {
  720.    if (event->state & GDK_SHIFT_MASK) {
  721.       return TRUE;
  722.    } else {
  723.       Polygon_t *polygon = ObjectToPolygon(obj);
  724.       GList *link = _prev_link;
  725.         
  726.       _prev_link = _prev_link->prev;
  727.       g_free((GdkPoint*) link->data);
  728.       polygon->points = g_list_remove_link(polygon->points, link);
  729.    }
  730.    return _prev_link == NULL;
  731. }
  732.  
  733. static Object_t*
  734. polygon_factory_create_object(gint x, gint y)
  735. {
  736.    GList *points;
  737.  
  738.    points = _prev_link = g_list_append(NULL, new_point(x, y));
  739.    points = g_list_append(points, new_point(x, y));
  740.  
  741.    return create_polygon(points);
  742. }
  743.  
  744. static void
  745. polygon_factory_set_xy(Object_t *obj, guint state, gint x, gint y)
  746. {
  747.    Polygon_t *polygon = ObjectToPolygon(obj);
  748.    GList *last = g_list_last(polygon->points);
  749.    GdkPoint *point = (GdkPoint*) last->data;
  750.    GdkPoint *prev = (GdkPoint*) last->prev->data;
  751.  
  752.    point->x = x;
  753.    point->y = y;
  754.  
  755.    main_set_dimension(x - prev->x, y - prev->y);
  756. }
  757.  
  758. static ObjectFactory_t polygon_factory = {
  759.    NULL,            /* Object pointer */
  760.    polygon_factory_finish,
  761.    polygon_factory_cancel,
  762.    polygon_factory_create_object,
  763.    polygon_factory_set_xy
  764. };
  765.  
  766. ObjectFactory_t*
  767. get_polygon_factory(guint state)
  768. {
  769.    return &polygon_factory;
  770. }
  771.  
  772. void
  773. polygon_remove_last_point(Polygon_t *polygon)
  774. {
  775.    GList *last = g_list_last(polygon->points);
  776.    g_free((GdkPoint*) last->data);
  777.    polygon->points = g_list_remove_link(polygon->points, last);
  778. }
  779.  
  780. GdkPoint*
  781. new_point(gint x, gint y)
  782. {
  783.    GdkPoint *point = g_new(GdkPoint, 1);
  784.    point->x = x;
  785.    point->y = y;
  786.    return point;
  787. }
  788.