diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
commit | 3c57dd931145d43f2b0aef96c4d178135956bf91 (patch) | |
tree | 3de698981e9f0cc2c4f9569b19a5f3595e741f6b /plug-ins/imagemap/imap_polygon.c | |
parent | Initial commit. (diff) | |
download | gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip |
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plug-ins/imagemap/imap_polygon.c')
-rw-r--r-- | plug-ins/imagemap/imap_polygon.c | 854 |
1 files changed, 854 insertions, 0 deletions
diff --git a/plug-ins/imagemap/imap_polygon.c b/plug-ins/imagemap/imap_polygon.c new file mode 100644 index 0000000..6428cd3 --- /dev/null +++ b/plug-ins/imagemap/imap_polygon.c @@ -0,0 +1,854 @@ +/* + * This is a plug-in for GIMP. + * + * Generates clickable image maps. + * + * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> + +#include "libgimp/gimp.h" +#include "libgimp/gimpui.h" + +#include "imap_commands.h" +#include "imap_main.h" +#include "imap_misc.h" +#include "imap_menu.h" +#include "imap_object_popup.h" +#include "imap_polygon.h" +#include "imap_stock.h" +#include "imap_table.h" + +#include "libgimp/stdplugins-intl.h" + +#define MAX_POLYGON_POINTS 99 + +static gboolean polygon_is_valid(Object_t *obj); +static void polygon_destruct(Object_t *obj); +static Object_t *polygon_clone(Object_t *obj); +static void polygon_assign(Object_t *obj, Object_t *des); +static void polygon_draw(Object_t* obj, cairo_t *cr); +static void polygon_draw_sashes(Object_t* obj, cairo_t *cr); +static MoveSashFunc_t polygon_near_sash(Object_t *obj, gint x, gint y); +static gboolean polygon_point_is_on(Object_t *obj, gint x, gint y); +static void polygon_get_dimensions(Object_t *obj, gint *x, gint *y, + gint *width, gint *height); +static void polygon_resize(Object_t *obj, gint percentage_x, + gint percentage_y); +static void polygon_move(Object_t *obj, gint dx, gint dy); +static gpointer polygon_create_info_widget(GtkWidget *frame); +static void polygon_update_info_widget(Object_t *obj, gpointer data); +static void polygon_fill_info_tab(Object_t *obj, gpointer data); +static void polygon_set_initial_focus(Object_t *obj, gpointer data); +static void polygon_update(Object_t* obj, gpointer data); +static void polygon_write_csim(Object_t* obj, gpointer param, + OutputFunc_t output); +static void polygon_write_cern(Object_t* obj, gpointer param, + OutputFunc_t output); +static void polygon_write_ncsa(Object_t* obj, gpointer param, + OutputFunc_t output); +static void polygon_do_popup(Object_t *obj, GdkEventButton *event); +static const gchar* polygon_get_stock_icon_name(void); + +static ObjectClass_t polygon_class = { + N_("_Polygon"), + NULL, /* info_dialog */ + + polygon_is_valid, + polygon_destruct, + polygon_clone, + polygon_assign, + NULL, /* polygon_normalize */ + polygon_draw, + polygon_draw_sashes, + polygon_near_sash, + polygon_point_is_on, + polygon_get_dimensions, + polygon_resize, + polygon_move, + polygon_create_info_widget, + polygon_update_info_widget, + polygon_fill_info_tab, + polygon_set_initial_focus, + polygon_update, + polygon_write_csim, + polygon_write_cern, + polygon_write_ncsa, + polygon_do_popup, + polygon_get_stock_icon_name +}; + +Object_t* +create_polygon(GList *points) +{ + Polygon_t *polygon = g_new(Polygon_t, 1); + polygon->points = points; + return object_init(&polygon->obj, &polygon_class); +} + +static void +polygon_free_list (Polygon_t *polygon) +{ + g_list_free_full (polygon->points, (GDestroyNotify) g_free); + polygon->points = NULL; +} + +static void +polygon_destruct(Object_t *obj) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + polygon_free_list(polygon); +} + +static gboolean +polygon_is_valid(Object_t *obj) +{ + return g_list_length(ObjectToPolygon(obj)->points) > 2; +} + +static Object_t* +polygon_clone(Object_t *obj) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + Polygon_t *clone = g_new(Polygon_t, 1); + GList *p; + + clone->points = NULL; + for (p = polygon->points; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + polygon_append_point(clone, point->x, point->y); + } + return &clone->obj; +} + +static void +polygon_assign(Object_t *obj, Object_t *des) +{ + Polygon_t *src_polygon = ObjectToPolygon(obj); + Polygon_t *des_polygon = ObjectToPolygon(des); + GList *p; + + polygon_free_list(des_polygon); + for (p = src_polygon->points; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + polygon_append_point(des_polygon, point->x, point->y); + } +} + +static void +polygon_draw(Object_t *obj, cairo_t *cr) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + draw_polygon(cr, polygon->points); +} + +static void +polygon_draw_sashes(Object_t *obj, cairo_t *cr) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *p; + for (p = polygon->points; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + draw_sash(cr, point->x, point->y); + } +} + +static GdkPoint *_sash_point; +static gint _sash_index; + +static void +move_sash(Object_t *obj, gint dx, gint dy) +{ + _sash_point->x += dx; + _sash_point->y += dy; +} + +static MoveSashFunc_t +polygon_near_sash(Object_t *obj, gint x, gint y) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *p; + + _sash_index = 0; + for (p = polygon->points; p; p = p->next, _sash_index++) { + GdkPoint *point = (GdkPoint*) p->data; + if (near_sash(point->x, point->y, x, y)) { + _sash_point = point; + return move_sash; + } + } + return NULL; +} + +static gboolean +right_intersect(GdkPoint *p1, GdkPoint *p2, gint x, gint y) +{ + gint dx = p2->x - p1->x; + gint dy = p2->y - p1->y; + + if ((dy > 0 && y > p1->y && y < p2->y) || + (dy < 0 && y > p2->y && y < p1->y)) { + gint sx = p1->x + (y - p1->y) * dx / dy; + return sx > x; + } + return FALSE; +} + +static gboolean +polygon_point_is_on(Object_t *obj, gint x, gint y) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *p; + int count = 0; + GdkPoint *first, *prev; + + p = polygon->points; + first = prev = (GdkPoint*) p->data; + p = p->next; + + for (; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + if (right_intersect(prev, point, x, y)) + count++; + prev = point; + } + if (right_intersect(prev, first, x, y)) + count++; + + return count % 2; +} + +static void +polygon_get_dimensions(Object_t *obj, gint *x, gint *y, + gint *width, gint *height) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + gint min_x = G_MAXINT, min_y = G_MAXINT; + gint max_x = G_MININT, max_y = G_MININT; + GList *p; + + for (p = polygon->points; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + if (point->x < min_x) + min_x = point->x; + if (point->x > max_x) + max_x = point->x; + if (point->y < min_y) + min_y = point->y; + if (point->y > max_y) + max_y = point->y; + } + *x = min_x; + *y = min_y; + *width = max_x - min_x; + *height = max_y - min_y; +} + +static void +polygon_resize(Object_t *obj, gint percentage_x, gint percentage_y) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *p; + for (p = polygon->points; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + point->x = point->x * percentage_x / 100; + point->y = point->y * percentage_y / 100; + } +} + +static void +polygon_move(Object_t *obj, gint dx, gint dy) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *p; + for (p = polygon->points; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + point->x += dx; + point->y += dy; + } +} + +typedef struct { + Object_t *obj; + GtkListStore *store; + GtkTreeSelection *selection; + GtkWidget *x; + GtkWidget *y; + GtkWidget *update; + GtkWidget *insert; + GtkWidget *append; + GtkWidget *remove; + gint selected_row; + guint timeout; +} PolygonProperties_t; + +static void +select_row_cb(GtkTreeSelection *selection, PolygonProperties_t *data) +{ + GtkTreeIter iter; + GtkTreeModel *model; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + GdkPoint *point; + + gtk_tree_model_get (model, &iter, 0, &point, -1); + + _sash_point = point; + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->x), point->x); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->y), point->y); + } +} + +static void +update_button_clicked(GtkWidget *widget, PolygonProperties_t *data) +{ + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(data->store); + + if (gtk_tree_selection_get_selected (data->selection, &model, &iter)) { + GdkPoint *point; + gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->x)); + gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->y)); + + gtk_tree_model_get (model, &iter, 0, &point, -1); + point->x = x; + point->y = y; + gtk_list_store_set (data->store, &iter, 0, point, -1); + } +} + +static void +set_buttons_sensitivity(PolygonProperties_t *data) +{ + gint rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(data->store), + NULL); + gtk_widget_set_sensitive(data->insert, rows != MAX_POLYGON_POINTS); + gtk_widget_set_sensitive(data->append, rows != MAX_POLYGON_POINTS); + gtk_widget_set_sensitive(data->remove, rows > 2); +} + +static void +insert_button_clicked(GtkWidget *widget, PolygonProperties_t *data) +{ + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(data->store); + + if (gtk_tree_selection_get_selected (data->selection, &model, &iter)) { + Polygon_t *polygon = ObjectToPolygon(data->obj); + GdkPoint *point; + GList *here; + gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->x)); + gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->y)); + + gtk_tree_model_get (model, &iter, 0, &point, -1); + here = g_list_find(polygon->points, point); + polygon->points = g_list_insert_before(polygon->points, here, + new_point(x, y)); + polygon_fill_info_tab(data->obj, data); + } +} + +static void +append_button_clicked(GtkWidget *widget, PolygonProperties_t *data) +{ + gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->x)); + gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->y)); + + polygon_append_point(ObjectToPolygon(data->obj), x, y); + polygon_fill_info_tab(data->obj, data); +} + +static void +remove_button_clicked(GtkWidget *widget, PolygonProperties_t *data) +{ + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(data->store); + + if (gtk_tree_selection_get_selected (data->selection, &model, &iter)) { + Polygon_t *polygon = ObjectToPolygon(data->obj); + GdkPoint *point; + + gtk_tree_model_get (model, &iter, 0, &point, -1); + polygon->points = g_list_remove(polygon->points, point); + g_free(point); + + polygon_fill_info_tab(data->obj, data); + } +} + +static void +x_changed_cb(GtkWidget *widget, gpointer data) +{ + Object_t *obj = ((PolygonProperties_t*) data)->obj; + gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); + _sash_point->x = x; + edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog); +} + +static void +y_changed_cb(GtkWidget *widget, gpointer data) +{ + Object_t *obj = ((PolygonProperties_t*) data)->obj; + gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); + _sash_point->y = y; + edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog); +} + +static void +render_x(GtkTreeViewColumn *column, GtkCellRenderer *cell, + GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) +{ + GdkPoint *point; + gchar scratch[16]; + + gtk_tree_model_get(tree_model, iter, 0, &point, -1); + sprintf(scratch, "%d", point->x); + g_object_set(cell, "text", scratch, "xalign", 1.0, NULL); +} + +static void +render_y(GtkTreeViewColumn *column, GtkCellRenderer *cell, + GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) +{ + GdkPoint *point; + gchar scratch[16]; + + gtk_tree_model_get(tree_model, iter, 0, &point, -1); + sprintf(scratch, "%d", point->y); + g_object_set(cell, "text", scratch, "xalign", 1.0, NULL); +} + +static gpointer +polygon_create_info_widget(GtkWidget *frame) +{ + PolygonProperties_t *props = g_new(PolygonProperties_t, 1); + GtkWidget *hbox, *swin, *table, *label; + GtkWidget *view; + gint max_width = get_image_width(); + gint max_height = get_image_height(); + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); + gtk_container_add(GTK_CONTAINER(frame), hbox); + gtk_widget_show(hbox); + + swin = gtk_scrolled_window_new(NULL, NULL); + + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(swin), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(hbox), swin, FALSE, FALSE, FALSE); + gtk_widget_show(swin); + + props->store = gtk_list_store_new (1, G_TYPE_POINTER); + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (props->store)); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE); + g_object_unref (props->store); + gtk_widget_show (view); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("x (pixels)"), + renderer, + NULL); + gtk_tree_view_column_set_cell_data_func(column, renderer, + render_x, props, NULL); + gtk_tree_view_column_set_alignment(column, 0.5); + gtk_tree_view_append_column (GTK_TREE_VIEW (view), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("y (pixels)"), + renderer, + NULL); + gtk_tree_view_column_set_cell_data_func(column, renderer, + render_y, props, NULL); + gtk_tree_view_column_set_alignment(column, 0.5); + gtk_tree_view_append_column (GTK_TREE_VIEW (view), column); + + gtk_container_add (GTK_CONTAINER (swin), view); + + table = gtk_table_new(6, 3, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 6); + gtk_table_set_col_spacings(GTK_TABLE(table), 6); + gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, FALSE); + gtk_widget_show(table); + + label = create_label_in_table(table, 0, 0, "_x:"); + props->x = create_spin_button_in_table(table, label, 0, 1, 1, 0, + max_width - 1); + g_signal_connect(props->x, "changed", + G_CALLBACK(x_changed_cb), (gpointer) props); + gtk_widget_set_size_request(props->x, 64, -1); + create_label_in_table(table, 0, 2, _("pixels")); + + label = create_label_in_table(table, 1, 0, "_y:"); + props->y = create_spin_button_in_table(table, label, 1, 1, 1, 0, + max_height - 1); + g_signal_connect(props->y, "changed", + G_CALLBACK(y_changed_cb), (gpointer) props); + gtk_widget_set_size_request(props->y, 64, -1); + create_label_in_table(table, 1, 2, _("pixels")); + + props->update = gtk_button_new_with_mnemonic(_("_Update")); + g_signal_connect(props->update, "clicked", + G_CALLBACK(update_button_clicked), props); + gtk_table_attach_defaults(GTK_TABLE(table), props->update, 1, 2, 2, 3); + gtk_widget_show(props->update); + + props->insert = gtk_button_new_with_mnemonic(_("_Insert")); + g_signal_connect(props->insert, "clicked", + G_CALLBACK(insert_button_clicked), props); + gtk_table_attach_defaults(GTK_TABLE(table), props->insert, 1, 2, 3, 4); + gtk_widget_show(props->insert); + + props->append = gtk_button_new_with_mnemonic(_("A_ppend")); + g_signal_connect(props->append, "clicked", + G_CALLBACK(append_button_clicked), props); + gtk_table_attach_defaults(GTK_TABLE(table), props->append, 1, 2, 4, 5); + gtk_widget_show(props->append); + + props->remove = gtk_button_new_with_mnemonic(_("_Remove")); + g_signal_connect(props->remove, "clicked", + G_CALLBACK(remove_button_clicked), props); + gtk_table_attach_defaults(GTK_TABLE(table), props->remove, 1, 2, 5, 6); + gtk_widget_show(props->remove); + + props->timeout = 0; + + props->selection = gtk_tree_view_get_selection(GTK_TREE_VIEW (view)); + gtk_tree_selection_set_mode(props->selection, GTK_SELECTION_SINGLE); + g_signal_connect (props->selection, "changed", + G_CALLBACK (select_row_cb), props); + + return props; +} + +static gboolean +update_timeout(gpointer data) +{ + PolygonProperties_t *props = (PolygonProperties_t*) data; + polygon_fill_info_tab(props->obj, data); + return FALSE; +} + +static void +polygon_update_info_widget(Object_t *obj, gpointer data) +{ + PolygonProperties_t *props = (PolygonProperties_t*) data; + GtkTreeIter iter; + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->x), _sash_point->x); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), _sash_point->y); + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(props->store), &iter, + NULL, _sash_index)) { + gtk_tree_selection_select_iter(props->selection, &iter); + } + + if (props->timeout) + g_source_remove(props->timeout); + props->timeout = g_timeout_add(1000, update_timeout, data); +} + +static void +polygon_fill_info_tab(Object_t *obj, gpointer data) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + PolygonProperties_t *props = (PolygonProperties_t*) data; + GtkTreeIter iter; + GList *p; + + props->obj = obj; + + gtk_list_store_clear(props->store); + + for (p = polygon->points; p; p = p->next) { + gtk_list_store_append(props->store, &iter); + gtk_list_store_set(props->store, &iter, 0, p->data, -1); + } + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(props->store), &iter, + NULL, _sash_index)) { + gtk_tree_selection_select_iter(props->selection, &iter); + } + set_buttons_sensitivity(props); +} + +static void +polygon_set_initial_focus(Object_t *obj, gpointer data) +{ + PolygonProperties_t *props = (PolygonProperties_t*) data; + gtk_widget_grab_focus(props->x); +} + +static void +polygon_update(Object_t* obj, gpointer data) +{ + /* Nothing to be done! */ +} + +static void +polygon_write_csim(Object_t *obj, gpointer param, OutputFunc_t output) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *p; + + output(param, "\"poly\" coords=\""); + for (p = polygon->points; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + output(param, "%d,%d", point->x, point->y); + output(param, "%c", (p->next) ? ',' : '"'); + } +} + +static void +polygon_write_cern(Object_t *obj, gpointer param, OutputFunc_t output) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *p; + GdkPoint *first = (GdkPoint*) polygon->points->data; + + output(param, "poly "); + for (p = polygon->points; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + output(param, "(%d,%d) ", point->x, point->y); + } + output(param, "(%d,%d)", first->x, first->y); +} + +static void +polygon_write_ncsa(Object_t *obj, gpointer param, OutputFunc_t output) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *p; + GdkPoint *first = (GdkPoint*) polygon->points->data; + + output(param, "poly %s", obj->url); + for (p = polygon->points; p; p = p->next) { + GdkPoint *point = (GdkPoint*) p->data; + output(param, " %d,%d", point->x, point->y); + } + output(param, " %d,%d", first->x, first->y); +} + +static Object_t *_current_obj; +static gboolean _insert_edge; +static gint _insert_x; +static gint _insert_y; + +void +polygon_insert_point(void) +{ + Command_t *command = insert_point_command_new (_current_obj, _insert_x, + _insert_y, _insert_edge); + command_execute (command); +} + +void +polygon_delete_point(void) +{ + Command_t *command = delete_point_command_new(_current_obj, _sash_point); + command_execute (command); +} + +static gboolean +point_near_edge(GdkPoint *first, GdkPoint *second, gint x, gint y) +{ + gint den, nom; + gdouble u; + + den = (first->x - x) * (first->x - second->x) + + (first->y - y) * (first->y - second->y); + nom = (second->x - first->x) * (second->x - first->x) + + (second->y - first->y) * (second->y - first->y); + u = (gdouble) den / nom; + if (u >= 0.0 && u <= 1.0) { + gint sx = first->x + (gint) (u * (second->x - first->x)) - x; + gint sy = first->y + (gint) (u * (second->y - first->y)) - y; + return sx * sx + sy * sy <= 25; /* Fix me! */ + } + return FALSE; +} + +static gint +polygon_near_edge(Object_t *obj, gint x, gint y) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *p = polygon->points; + GdkPoint *first = (GdkPoint*) p->data; + GdkPoint *prev = first; + gint n = 1; + + for (p = p->next; p; p = p->next, n++) { + GdkPoint *next = (GdkPoint*) p->data; + if (point_near_edge(prev, next, x, y)) + return n; + prev = next; + } + return (point_near_edge(prev, first, x, y)) ? n + 1 : 0; +} + +static void +polygon_handle_popup (GdkEventButton *event, gboolean near_sash, + gboolean near_edge) +{ + GtkWidget *popup = menu_get_widget ("/PolygonPopupMenu"); + GtkWidget *delete = menu_get_widget ("/PolygonPopupMenu/DeletePoint"); + GtkWidget *insert = menu_get_widget ("/PolygonPopupMenu/InsertPoint"); + + gtk_widget_set_sensitive (delete, near_sash); + gtk_widget_set_sensitive (insert, near_edge); + + gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL, + event->button, event->time); +} + +static void +polygon_do_popup(Object_t *obj, GdkEventButton *event) +{ + gint x = get_real_coord ((gint) event->x); + gint y = get_real_coord ((gint) event->y); + + _current_obj = obj; + + if (polygon_near_sash (obj, x, y)) + { + polygon_handle_popup (event, TRUE, FALSE); + } + else + { + _insert_edge = polygon_near_edge (obj, x, y); + if (_insert_edge) + { + _insert_x = x; + _insert_y = y; + + polygon_handle_popup (event, FALSE, TRUE); + } + else { + object_do_popup (obj, event); + } + } +} + +static const gchar* +polygon_get_stock_icon_name(void) +{ + return IMAP_STOCK_POLYGON; +} + +static GList *_prev_link; + +static gboolean +polygon_factory_finish(Object_t *obj, gint x, gint y) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GdkPoint *prev_point = (GdkPoint*) _prev_link->data; + + if (x == prev_point->x && y == prev_point->y) { + polygon_remove_last_point(polygon); + return TRUE; + } else { + polygon_append_point(polygon, x, y); + _prev_link = _prev_link->next; + } + return FALSE; +} + +static gboolean +polygon_factory_cancel(GdkEventButton *event, Object_t *obj) +{ + if (event->state & GDK_SHIFT_MASK) { + return TRUE; + } else { + Polygon_t *polygon = ObjectToPolygon(obj); + GList *link = _prev_link; + + _prev_link = _prev_link->prev; + g_free((GdkPoint*) link->data); + polygon->points = g_list_remove_link(polygon->points, link); + } + return _prev_link == NULL; +} + +static Object_t* +polygon_factory_create_object(gint x, gint y) +{ + GList *points; + + points = _prev_link = g_list_append(NULL, new_point(x, y)); + points = g_list_append(points, new_point(x, y)); + + return create_polygon(points); +} + +static void +polygon_factory_set_xy(Object_t *obj, guint state, gint x, gint y) +{ + Polygon_t *polygon = ObjectToPolygon(obj); + GList *last = g_list_last(polygon->points); + GdkPoint *point = (GdkPoint*) last->data; + GdkPoint *prev = (GdkPoint*) last->prev->data; + + point->x = x; + point->y = y; + + main_set_dimension(x - prev->x, y - prev->y); +} + +static ObjectFactory_t polygon_factory = { + NULL, /* Object pointer */ + polygon_factory_finish, + polygon_factory_cancel, + polygon_factory_create_object, + polygon_factory_set_xy +}; + +ObjectFactory_t* +get_polygon_factory(guint state) +{ + return &polygon_factory; +} + +void +polygon_remove_last_point(Polygon_t *polygon) +{ + GList *last = g_list_last(polygon->points); + g_free((GdkPoint*) last->data); + polygon->points = g_list_remove_link(polygon->points, last); +} + +GdkPoint* +new_point(gint x, gint y) +{ + GdkPoint *point = g_new(GdkPoint, 1); + point->x = x; + point->y = y; + return point; +} + +void +polygon_append_point(Polygon_t *polygon, gint x, gint y) +{ + polygon->points = g_list_append(polygon->points, new_point(x, y)); +} |