imap_polygon.c 24.3 KB
Newer Older
Sven Neumann's avatar
Sven Neumann committed
1
/*
2
 * This is a plug-in for GIMP.
Sven Neumann's avatar
Sven Neumann committed
3 4 5
 *
 * Generates clickable image maps.
 *
6
 * Copyright (C) 1998-2005 Maurits Rijk  m.rijk@chello.nl
Sven Neumann's avatar
Sven Neumann committed
7
 *
8
 * This program is free software: you can redistribute it and/or modify
Sven Neumann's avatar
Sven Neumann committed
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
Sven Neumann's avatar
Sven Neumann committed
11 12 13 14 15 16 17 18
 * (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
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Sven Neumann's avatar
Sven Neumann committed
20 21 22
 *
 */

23 24
#include "config.h"

Sven Neumann's avatar
Sven Neumann committed
25 26 27
#include <stdlib.h>
#include <stdio.h>

28 29
#include <gtk/gtk.h>

30 31
#include <libgimpwidgets/gimpwidgets.h>

32
#include "imap_commands.h"
Sven Neumann's avatar
Sven Neumann committed
33 34
#include "imap_main.h"
#include "imap_misc.h"
35
#include "imap_menu.h"
Sven Neumann's avatar
Sven Neumann committed
36 37
#include "imap_object_popup.h"
#include "imap_polygon.h"
38
#include "imap_stock.h"
Sven Neumann's avatar
Sven Neumann committed
39 40
#include "imap_table.h"

41 42
#include "libgimp/stdplugins-intl.h"

Sven Neumann's avatar
Sven Neumann committed
43 44 45 46 47
#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);
48
static void polygon_assign(Object_t *obj, Object_t *des);
49 50
static void polygon_draw(Object_t* obj, cairo_t *cr);
static void polygon_draw_sashes(Object_t* obj, cairo_t *cr);
Sven Neumann's avatar
Sven Neumann committed
51 52 53
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,
54
                                   gint *width, gint *height);
Sven Neumann's avatar
Sven Neumann committed
55
static void polygon_resize(Object_t *obj, gint percentage_x,
56
                           gint percentage_y);
Sven Neumann's avatar
Sven Neumann committed
57
static void polygon_move(Object_t *obj, gint dx, gint dy);
58 59
static gpointer polygon_create_info_widget(GtkWidget *frame);
static void polygon_update_info_widget(Object_t *obj, gpointer data);
Sven Neumann's avatar
Sven Neumann committed
60 61 62
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);
63
static void polygon_write_csim(Object_t* obj, gpointer param,
64
                               OutputFunc_t output);
65
static void polygon_write_cern(Object_t* obj, gpointer param,
66
                               OutputFunc_t output);
67
static void polygon_write_ncsa(Object_t* obj, gpointer param,
68
                               OutputFunc_t output);
Sven Neumann's avatar
Sven Neumann committed
69
static void polygon_do_popup(Object_t *obj, GdkEventButton *event);
70
static const gchar* polygon_get_stock_icon_name(void);
Sven Neumann's avatar
Sven Neumann committed
71 72

static ObjectClass_t polygon_class = {
73
   N_("_Polygon"),
74 75 76
   NULL,                        /* info_dialog */
   NULL,                        /* icon */
   NULL,                        /* mask */
Sven Neumann's avatar
Sven Neumann committed
77 78 79 80 81

   polygon_is_valid,
   polygon_destruct,
   polygon_clone,
   polygon_assign,
82
   NULL,                        /* polygon_normalize */
Sven Neumann's avatar
Sven Neumann committed
83 84 85 86 87 88 89
   polygon_draw,
   polygon_draw_sashes,
   polygon_near_sash,
   polygon_point_is_on,
   polygon_get_dimensions,
   polygon_resize,
   polygon_move,
90 91
   polygon_create_info_widget,
   polygon_update_info_widget,
Sven Neumann's avatar
Sven Neumann committed
92 93 94 95 96 97 98
   polygon_fill_info_tab,
   polygon_set_initial_focus,
   polygon_update,
   polygon_write_csim,
   polygon_write_cern,
   polygon_write_ncsa,
   polygon_do_popup,
99
   polygon_get_stock_icon_name
Sven Neumann's avatar
Sven Neumann committed
100 101 102 103 104 105 106 107 108 109 110
};

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
111
polygon_free_list (Polygon_t *polygon)
Sven Neumann's avatar
Sven Neumann committed
112
{
113 114
  g_list_free_full (polygon->points, (GDestroyNotify) g_free);
  polygon->points = NULL;
115 116 117 118 119 120 121
}

static void
polygon_destruct(Object_t *obj)
{
   Polygon_t *polygon = ObjectToPolygon(obj);
   polygon_free_list(polygon);
Sven Neumann's avatar
Sven Neumann committed
122 123
}

124
static gboolean
Sven Neumann's avatar
Sven Neumann committed
125 126 127 128 129 130 131 132 133 134 135
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;
136

Sven Neumann's avatar
Sven Neumann committed
137 138 139
   clone->points = NULL;
   for (p = polygon->points; p; p = p->next) {
      GdkPoint *point = (GdkPoint*) p->data;
140
      polygon_append_point(clone, point->x, point->y);
Sven Neumann's avatar
Sven Neumann committed
141 142 143 144
   }
   return &clone->obj;
}

145
static void
Sven Neumann's avatar
Sven Neumann committed
146 147 148 149 150 151
polygon_assign(Object_t *obj, Object_t *des)
{
   Polygon_t *src_polygon = ObjectToPolygon(obj);
   Polygon_t *des_polygon = ObjectToPolygon(des);
   GList     *p;

152
   polygon_free_list(des_polygon);
Sven Neumann's avatar
Sven Neumann committed
153 154
   for (p = src_polygon->points; p; p = p->next) {
      GdkPoint *point = (GdkPoint*) p->data;
155
      polygon_append_point(des_polygon, point->x, point->y);
Sven Neumann's avatar
Sven Neumann committed
156 157 158 159
   }
}

static void
160
polygon_draw(Object_t *obj, cairo_t *cr)
Sven Neumann's avatar
Sven Neumann committed
161 162
{
   Polygon_t *polygon = ObjectToPolygon(obj);
163
   draw_polygon(cr, polygon->points);
Sven Neumann's avatar
Sven Neumann committed
164 165 166
}

static void
167
polygon_draw_sashes(Object_t *obj, cairo_t *cr)
Sven Neumann's avatar
Sven Neumann committed
168 169 170 171 172
{
   Polygon_t *polygon = ObjectToPolygon(obj);
   GList     *p;
   for (p = polygon->points; p; p = p->next) {
      GdkPoint *point = (GdkPoint*) p->data;
173
      draw_sash(cr, point->x, point->y);
Sven Neumann's avatar
Sven Neumann committed
174 175 176
   }
}

177 178
static GdkPoint *_sash_point;
static gint _sash_index;
Sven Neumann's avatar
Sven Neumann committed
179 180 181 182

static void
move_sash(Object_t *obj, gint dx, gint dy)
{
183 184
   _sash_point->x += dx;
   _sash_point->y += dy;
Sven Neumann's avatar
Sven Neumann committed
185 186 187 188 189 190 191
}

static MoveSashFunc_t
polygon_near_sash(Object_t *obj, gint x, gint y)
{
   Polygon_t *polygon = ObjectToPolygon(obj);
   GList     *p;
192 193 194

   _sash_index = 0;
   for (p = polygon->points; p; p = p->next, _sash_index++) {
Sven Neumann's avatar
Sven Neumann committed
195 196
      GdkPoint *point = (GdkPoint*) p->data;
      if (near_sash(point->x, point->y, x, y)) {
197 198
         _sash_point = point;
         return move_sash;
Sven Neumann's avatar
Sven Neumann committed
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
      }
   }
   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 < y && 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))
233
         count++;
Sven Neumann's avatar
Sven Neumann committed
234 235 236 237 238 239 240 241
      prev = point;
   }
   if (right_intersect(prev, first, x, y))
       count++;

   return count % 2;
}

242
static void
Sven Neumann's avatar
Sven Neumann committed
243
polygon_get_dimensions(Object_t *obj, gint *x, gint *y,
244
                       gint *width, gint *height)
Sven Neumann's avatar
Sven Neumann committed
245 246 247 248 249 250 251 252 253
{
   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)
254
         min_x = point->x;
Sven Neumann's avatar
Sven Neumann committed
255
      if (point->x > max_x)
256
         max_x = point->x;
Sven Neumann's avatar
Sven Neumann committed
257
      if (point->y < min_y)
258
         min_y = point->y;
Sven Neumann's avatar
Sven Neumann committed
259
      if (point->y > max_y)
260
         max_y = point->y;
Sven Neumann's avatar
Sven Neumann committed
261 262 263 264 265 266 267
   }
   *x = min_x;
   *y = min_y;
   *width = max_x - min_x;
   *height = max_y - min_y;
}

268
static void
Sven Neumann's avatar
Sven Neumann committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
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 {
293
   Object_t  *obj;
294 295
   GtkListStore *store;
   GtkTreeSelection *selection;
Sven Neumann's avatar
Sven Neumann committed
296 297 298 299 300 301
   GtkWidget *x;
   GtkWidget *y;
   GtkWidget *update;
   GtkWidget *insert;
   GtkWidget *append;
   GtkWidget *remove;
302
   gint       selected_row;
303
   guint      timeout;
Sven Neumann's avatar
Sven Neumann committed
304 305 306
} PolygonProperties_t;

static void
307
select_row_cb(GtkTreeSelection *selection, PolygonProperties_t *data)
Sven Neumann's avatar
Sven Neumann committed
308
{
309 310 311 312 313
  GtkTreeIter iter;
  GtkTreeModel *model;

  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
    GdkPoint *point;
Sven Neumann's avatar
Sven Neumann committed
314

315
    gtk_tree_model_get (model, &iter, 0, &point, -1);
316

317
    _sash_point = point;
Sven Neumann's avatar
Sven Neumann committed
318

319 320 321
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->x), point->x);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->y), point->y);
  }
Sven Neumann's avatar
Sven Neumann committed
322 323 324 325 326
}

static void
update_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
{
327 328 329 330 331 332 333 334 335 336 337 338 339
   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);
   }
Sven Neumann's avatar
Sven Neumann committed
340 341 342 343 344
}

static void
set_buttons_sensitivity(PolygonProperties_t *data)
{
345
   gint rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(data->store),
346
                                               NULL);
347 348 349
   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);
Sven Neumann's avatar
Sven Neumann committed
350 351 352 353 354
}

static void
insert_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
{
355 356 357 358 359 360 361 362 363 364 365 366
   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);
367
     polygon->points = g_list_insert_before(polygon->points, here,
368
                                            new_point(x, y));
369 370
     polygon_fill_info_tab(data->obj, data);
   }
Sven Neumann's avatar
Sven Neumann committed
371 372 373 374 375
}

static void
append_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
{
376 377 378 379 380
   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);
Sven Neumann's avatar
Sven Neumann committed
381 382 383 384 385
}

static void
remove_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
{
386 387 388 389 390 391 392 393 394 395
  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);
396

397 398
    polygon_fill_info_tab(data->obj, data);
  }
Sven Neumann's avatar
Sven Neumann committed
399 400
}

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
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);
}

419
static void
420
render_x(GtkTreeViewColumn *column, GtkCellRenderer *cell,
421
         GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
422 423 424 425 426 427
{
  GdkPoint *point;
  gchar scratch[16];

  gtk_tree_model_get(tree_model, iter, 0, &point, -1);
  sprintf(scratch, "%d", point->x);
428
  g_object_set(cell, "text", scratch, "xalign", 1.0, NULL);
429 430 431
}

static void
432
render_y(GtkTreeViewColumn *column, GtkCellRenderer *cell,
433
         GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
434 435 436 437 438 439
{
  GdkPoint *point;
  gchar scratch[16];

  gtk_tree_model_get(tree_model, iter, 0, &point, -1);
  sprintf(scratch, "%d", point->y);
440
  g_object_set(cell, "text", scratch, "xalign", 1.0, NULL);
441 442
}

Sven Neumann's avatar
Sven Neumann committed
443
static gpointer
444
polygon_create_info_widget(GtkWidget *frame)
Sven Neumann's avatar
Sven Neumann committed
445
{
446
   PolygonProperties_t *props = g_new(PolygonProperties_t, 1);
447
   GtkWidget *hbox, *swin, *table, *label;
448
   GtkWidget *view;
Sven Neumann's avatar
Sven Neumann committed
449 450
   gint max_width = get_image_width();
   gint max_height = get_image_height();
451 452
   GtkCellRenderer *renderer;
   GtkTreeViewColumn *column;
Sven Neumann's avatar
Sven Neumann committed
453

454
   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
455
   gtk_container_add(GTK_CONTAINER(frame), hbox);
Sven Neumann's avatar
Sven Neumann committed
456 457 458 459
   gtk_widget_show(hbox);

   swin = gtk_scrolled_window_new(NULL, NULL);

460
   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
461
                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
462
   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(swin),
463
                                        GTK_SHADOW_IN);
Sven Neumann's avatar
Sven Neumann committed
464 465 466
   gtk_box_pack_start(GTK_BOX(hbox), swin, FALSE, FALSE, FALSE);
   gtk_widget_show(swin);

467 468 469
   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);
470
   g_object_unref (props->store);
471 472 473
   gtk_widget_show (view);

   renderer = gtk_cell_renderer_text_new ();
474
   column = gtk_tree_view_column_new_with_attributes (_("x (pixels)"),
475 476
                                                      renderer,
                                                      NULL);
477
   gtk_tree_view_column_set_cell_data_func(column, renderer,
478
                                           render_x, props, NULL);
479 480 481 482
   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 ();
483
   column = gtk_tree_view_column_new_with_attributes (_("y (pixels)"),
484 485
                                                      renderer,
                                                      NULL);
486
   gtk_tree_view_column_set_cell_data_func(column, renderer,
487
                                           render_y, props, NULL);
488 489 490 491
   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);
Sven Neumann's avatar
Sven Neumann committed
492 493

   table = gtk_table_new(6, 3, FALSE);
494 495
   gtk_table_set_row_spacings(GTK_TABLE(table), 6);
   gtk_table_set_col_spacings(GTK_TABLE(table), 6);
Sven Neumann's avatar
Sven Neumann committed
496 497 498
   gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, FALSE);
   gtk_widget_show(table);

499
   label = create_label_in_table(table, 0, 0, "_x:");
500
   props->x = create_spin_button_in_table(table, label, 0, 1, 1, 0,
501
                                          max_width - 1);
502
   g_signal_connect(props->x, "changed",
503
                    G_CALLBACK(x_changed_cb), (gpointer) props);
504
   gtk_widget_set_size_request(props->x, 64, -1);
505
   create_label_in_table(table, 0, 2, _("pixels"));
Sven Neumann's avatar
Sven Neumann committed
506

507
   label = create_label_in_table(table, 1, 0, "_y:");
508
   props->y = create_spin_button_in_table(table, label, 1, 1, 1, 0,
509
                                          max_height - 1);
510
   g_signal_connect(props->y, "changed",
511
                    G_CALLBACK(y_changed_cb), (gpointer) props);
512
   gtk_widget_set_size_request(props->y, 64, -1);
513 514
   create_label_in_table(table, 1, 2, _("pixels"));

515
   props->update = gtk_button_new_with_mnemonic(_("_Update"));
516
   g_signal_connect(props->update, "clicked",
517
                    G_CALLBACK(update_button_clicked), props);
518 519
   gtk_table_attach_defaults(GTK_TABLE(table), props->update, 1, 2, 2, 3);
   gtk_widget_show(props->update);
Sven Neumann's avatar
Sven Neumann committed
520

521
   props->insert = gtk_button_new_with_mnemonic(_("_Insert"));
522
   g_signal_connect(props->insert, "clicked",
523
                    G_CALLBACK(insert_button_clicked), props);
524 525
   gtk_table_attach_defaults(GTK_TABLE(table), props->insert, 1, 2, 3, 4);
   gtk_widget_show(props->insert);
Sven Neumann's avatar
Sven Neumann committed
526

527
   props->append = gtk_button_new_with_mnemonic(_("A_ppend"));
528
   g_signal_connect(props->append, "clicked",
529
                    G_CALLBACK(append_button_clicked), props);
530 531
   gtk_table_attach_defaults(GTK_TABLE(table), props->append, 1, 2, 4, 5);
   gtk_widget_show(props->append);
Sven Neumann's avatar
Sven Neumann committed
532

533
   props->remove = gtk_button_new_with_mnemonic(_("_Remove"));
534
   g_signal_connect(props->remove, "clicked",
535
                    G_CALLBACK(remove_button_clicked), props);
536 537
   gtk_table_attach_defaults(GTK_TABLE(table), props->remove, 1, 2, 5, 6);
   gtk_widget_show(props->remove);
Sven Neumann's avatar
Sven Neumann committed
538

539
   props->timeout = 0;
Sven Neumann's avatar
Sven Neumann committed
540

541 542
   props->selection = gtk_tree_view_get_selection(GTK_TREE_VIEW (view));
   gtk_tree_selection_set_mode(props->selection, GTK_SELECTION_SINGLE);
543
   g_signal_connect (props->selection, "changed",
544
                     G_CALLBACK (select_row_cb), props);
545

546 547
   return props;
}
Sven Neumann's avatar
Sven Neumann committed
548

549
static gboolean
550 551 552 553 554 555 556 557 558 559 560
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;
561
   GtkTreeIter iter;
562

563 564
   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);
565 566

   if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(props->store), &iter,
567
                                     NULL, _sash_index)) {
568
     gtk_tree_selection_select_iter(props->selection, &iter);
569
   }
570

571
   if (props->timeout)
572 573
      g_source_remove(props->timeout);
   props->timeout = g_timeout_add(1000, update_timeout, data);
Sven Neumann's avatar
Sven Neumann committed
574 575 576 577 578 579 580
}

static void
polygon_fill_info_tab(Object_t *obj, gpointer data)
{
   Polygon_t *polygon = ObjectToPolygon(obj);
   PolygonProperties_t *props = (PolygonProperties_t*) data;
581
   GtkTreeIter iter;
Sven Neumann's avatar
Sven Neumann committed
582 583
   GList     *p;

584
   props->obj = obj;
585 586 587

   gtk_list_store_clear(props->store);

588
   for (p = polygon->points; p; p = p->next) {
589 590
     gtk_list_store_append(props->store, &iter);
     gtk_list_store_set(props->store, &iter, 0, p->data, -1);
Sven Neumann's avatar
Sven Neumann committed
591 592
   }

593
   if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(props->store), &iter,
594
                                     NULL, _sash_index)) {
595 596
     gtk_tree_selection_select_iter(props->selection, &iter);
   }
Sven Neumann's avatar
Sven Neumann committed
597 598 599
   set_buttons_sensitivity(props);
}

600
static void
Sven Neumann's avatar
Sven Neumann committed
601 602 603 604 605 606
polygon_set_initial_focus(Object_t *obj, gpointer data)
{
   PolygonProperties_t *props = (PolygonProperties_t*) data;
   gtk_widget_grab_focus(props->x);
}

607
static void
Sven Neumann's avatar
Sven Neumann committed
608 609
polygon_update(Object_t* obj, gpointer data)
{
610
  /* Nothing to be done! */
Sven Neumann's avatar
Sven Neumann committed
611 612 613 614 615 616 617 618
}

static void
polygon_write_csim(Object_t *obj, gpointer param, OutputFunc_t output)
{
   Polygon_t *polygon = ObjectToPolygon(obj);
   GList     *p;

619
   output(param, "\"poly\" coords=\"");
Sven Neumann's avatar
Sven Neumann committed
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
   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);
}

657
static Object_t *_current_obj;
658
static gboolean _insert_edge;
Sven Neumann's avatar
Sven Neumann committed
659 660 661
static gint _insert_x;
static gint _insert_y;

662 663
void
polygon_insert_point(void)
Sven Neumann's avatar
Sven Neumann committed
664
{
665 666 667
  Command_t *command = insert_point_command_new (_current_obj, _insert_x,
                                                 _insert_y, _insert_edge);
  command_execute (command);
Sven Neumann's avatar
Sven Neumann committed
668 669
}

670 671
void
polygon_delete_point(void)
Sven Neumann's avatar
Sven Neumann committed
672
{
673 674
  Command_t *command = delete_point_command_new(_current_obj, _sash_point);
  command_execute (command);
Sven Neumann's avatar
Sven Neumann committed
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
}

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! */
   }
693
   return FALSE;
Sven Neumann's avatar
Sven Neumann committed
694 695 696 697 698 699 700 701 702 703 704 705 706 707
}

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))
708
         return n;
Sven Neumann's avatar
Sven Neumann committed
709 710 711 712 713
      prev = next;
   }
   return (point_near_edge(prev, first, x, y)) ? n + 1 : 0;
}

714
static void
715 716
polygon_handle_popup (GdkEventButton *event, gboolean near_sash,
                      gboolean near_edge)
Sven Neumann's avatar
Sven Neumann committed
717
{
718 719 720
  GtkWidget *popup = menu_get_widget ("/PolygonPopupMenu");
  GtkWidget *delete = menu_get_widget ("/PolygonPopupMenu/DeletePoint");
  GtkWidget *insert = menu_get_widget ("/PolygonPopupMenu/InsertPoint");
Sven Neumann's avatar
Sven Neumann committed
721

722 723 724 725 726 727 728 729 730 731 732 733
  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);
734

735
  _current_obj = obj;
736 737

  if (polygon_near_sash (obj, x, y))
738 739
    {
      polygon_handle_popup (event, TRUE, FALSE);
740 741
    }
  else
742 743
    {
      _insert_edge = polygon_near_edge (obj, x, y);
744
      if (_insert_edge)
745 746 747
        {
          _insert_x = x;
           _insert_y = y;
748

749
           polygon_handle_popup (event, FALSE, TRUE);
750
        }
751 752
      else {
        object_do_popup (obj, event);
Sven Neumann's avatar
Sven Neumann committed
753
      }
754
    }
Sven Neumann's avatar
Sven Neumann committed
755 756
}

757
static const gchar*
758
polygon_get_stock_icon_name(void)
Sven Neumann's avatar
Sven Neumann committed
759
{
760
   return IMAP_STOCK_POLYGON;
Sven Neumann's avatar
Sven Neumann committed
761 762 763 764 765 766 767 768 769 770 771 772 773 774
}

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 {
775
      polygon_append_point(polygon, x, y);
Sven Neumann's avatar
Sven Neumann committed
776 777 778 779 780 781 782 783 784 785 786 787 788
      _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;
789

Sven Neumann's avatar
Sven Neumann committed
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
      _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 = {
823
   NULL,                        /* Object pointer */
Sven Neumann's avatar
Sven Neumann committed
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
   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;
}
852 853 854 855 856 857

void
polygon_append_point(Polygon_t *polygon, gint x, gint y)
{
  polygon->points = g_list_append(polygon->points, new_point(x, y));
}