~ubuntu-branches/ubuntu/hoary/gimp/hoary

« back to all changes in this revision

Viewing changes to plug-ins/imagemap/imap_polygon.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2005-04-04 14:51:23 UTC
  • Revision ID: james.westby@ubuntu.com-20050404145123-9py049eeelfymur8
Tags: upstream-2.2.2
ImportĀ upstreamĀ versionĀ 2.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This is a plug-in for the GIMP.
 
3
 *
 
4
 * Generates clickable image maps.
 
5
 *
 
6
 * Copyright (C) 1998-2004 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 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 "config.h"
 
25
 
 
26
#include <stdlib.h>
 
27
#include <stdio.h>
 
28
 
 
29
#include <gtk/gtk.h>
 
30
 
 
31
#include "imap_commands.h"
 
32
#include "imap_main.h"
 
33
#include "imap_misc.h"
 
34
#include "imap_object_popup.h"
 
35
#include "imap_polygon.h"
 
36
#include "imap_stock.h"
 
37
#include "imap_table.h"
 
38
 
 
39
#include "libgimp/stdplugins-intl.h"
 
40
 
 
41
#define MAX_POLYGON_POINTS 99
 
42
 
 
43
static gboolean polygon_is_valid(Object_t *obj);
 
44
static void polygon_destruct(Object_t *obj);
 
45
static Object_t *polygon_clone(Object_t *obj);
 
46
static void polygon_assign(Object_t *obj, Object_t *des);
 
47
static void polygon_draw(Object_t* obj, GdkWindow *window, GdkGC* gc);
 
48
static void polygon_draw_sashes(Object_t* obj, GdkWindow *window, GdkGC* gc);
 
49
static MoveSashFunc_t polygon_near_sash(Object_t *obj, gint x, gint y);
 
50
static gboolean polygon_point_is_on(Object_t *obj, gint x, gint y);
 
51
static void polygon_get_dimensions(Object_t *obj, gint *x, gint *y,
 
52
                                   gint *width, gint *height);
 
53
static void polygon_resize(Object_t *obj, gint percentage_x,
 
54
                           gint percentage_y);
 
55
static void polygon_move(Object_t *obj, gint dx, gint dy);
 
56
static gpointer polygon_create_info_widget(GtkWidget *frame);
 
57
static void polygon_update_info_widget(Object_t *obj, gpointer data);
 
58
static void polygon_fill_info_tab(Object_t *obj, gpointer data);
 
59
static void polygon_set_initial_focus(Object_t *obj, gpointer data);
 
60
static void polygon_update(Object_t* obj, gpointer data);
 
61
static void polygon_write_csim(Object_t* obj, gpointer param,
 
62
                               OutputFunc_t output);
 
63
static void polygon_write_cern(Object_t* obj, gpointer param,
 
64
                               OutputFunc_t output);
 
65
static void polygon_write_ncsa(Object_t* obj, gpointer param,
 
66
                               OutputFunc_t output);
 
67
static void polygon_do_popup(Object_t *obj, GdkEventButton *event);
 
68
static const gchar* polygon_get_stock_icon_name(void);
 
69
 
 
70
static ObjectClass_t polygon_class = {
 
71
   N_("_Polygon"),
 
72
   NULL,                        /* info_dialog */
 
73
   NULL,                        /* icon */
 
74
   NULL,                        /* mask */
 
75
 
 
76
   polygon_is_valid,
 
77
   polygon_destruct,
 
78
   polygon_clone,
 
79
   polygon_assign,
 
80
   NULL,                        /* polygon_normalize */
 
81
   polygon_draw,
 
82
   polygon_draw_sashes,
 
83
   polygon_near_sash,
 
84
   polygon_point_is_on,
 
85
   polygon_get_dimensions,
 
86
   polygon_resize,
 
87
   polygon_move,
 
88
   polygon_create_info_widget,
 
89
   polygon_update_info_widget,
 
90
   polygon_fill_info_tab,
 
91
   polygon_set_initial_focus,
 
92
   polygon_update,
 
93
   polygon_write_csim,
 
94
   polygon_write_cern,
 
95
   polygon_write_ncsa,
 
96
   polygon_do_popup,
 
97
   polygon_get_stock_icon_name
 
98
};
 
99
 
 
100
Object_t*
 
101
create_polygon(GList *points)
 
102
{
 
103
   Polygon_t *polygon = g_new(Polygon_t, 1);
 
104
   polygon->points = points;
 
105
   return object_init(&polygon->obj, &polygon_class);
 
106
}
 
107
 
 
108
static void
 
109
polygon_free_list(Polygon_t *polygon)
 
110
{
 
111
   g_list_foreach(polygon->points, (GFunc) g_free, NULL);
 
112
   g_list_free(polygon->points);
 
113
   polygon->points = NULL;
 
114
}
 
115
 
 
116
static void
 
117
polygon_destruct(Object_t *obj)
 
118
{
 
119
   Polygon_t *polygon = ObjectToPolygon(obj);
 
120
   polygon_free_list(polygon);
 
121
}
 
122
 
 
123
static gboolean
 
124
polygon_is_valid(Object_t *obj)
 
125
{
 
126
   return g_list_length(ObjectToPolygon(obj)->points) > 2;
 
127
}
 
128
 
 
129
static Object_t*
 
130
polygon_clone(Object_t *obj)
 
131
{
 
132
   Polygon_t *polygon = ObjectToPolygon(obj);
 
133
   Polygon_t *clone = g_new(Polygon_t, 1);
 
134
   GList     *p;
 
135
 
 
136
   clone->points = NULL;
 
137
   for (p = polygon->points; p; p = p->next) {
 
138
      GdkPoint *point = (GdkPoint*) p->data;
 
139
      polygon_append_point(clone, point->x, point->y);
 
140
   }
 
141
   return &clone->obj;
 
142
}
 
143
 
 
144
static void
 
145
polygon_assign(Object_t *obj, Object_t *des)
 
146
{
 
147
   Polygon_t *src_polygon = ObjectToPolygon(obj);
 
148
   Polygon_t *des_polygon = ObjectToPolygon(des);
 
149
   GList     *p;
 
150
 
 
151
   polygon_free_list(des_polygon);
 
152
   for (p = src_polygon->points; p; p = p->next) {
 
153
      GdkPoint *point = (GdkPoint*) p->data;
 
154
      polygon_append_point(des_polygon, 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
   GtkListStore *store;
 
294
   GtkTreeSelection *selection;
 
295
   GtkWidget *x;
 
296
   GtkWidget *y;
 
297
   GtkWidget *update;
 
298
   GtkWidget *insert;
 
299
   GtkWidget *append;
 
300
   GtkWidget *remove;
 
301
   gint       selected_row;
 
302
   guint      timeout;
 
303
} PolygonProperties_t;
 
304
 
 
305
static void
 
306
select_row_cb(GtkTreeSelection *selection, PolygonProperties_t *data)
 
307
{
 
308
  GtkTreeIter iter;
 
309
  GtkTreeModel *model;
 
310
 
 
311
  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
 
312
    GdkPoint *point;
 
313
 
 
314
    gtk_tree_model_get (model, &iter, 0, &point, -1);
 
315
 
 
316
    _sash_point = point;
 
317
 
 
318
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->x), point->x);
 
319
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->y), point->y);
 
320
  }
 
321
}
 
322
 
 
323
static void
 
324
update_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
 
325
{
 
326
   GtkTreeIter iter;
 
327
   GtkTreeModel *model = GTK_TREE_MODEL(data->store);
 
328
 
 
329
   if (gtk_tree_selection_get_selected (data->selection, &model, &iter)) {
 
330
     GdkPoint *point;
 
331
     gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->x));
 
332
     gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->y));
 
333
 
 
334
     gtk_tree_model_get (model, &iter, 0, &point, -1);
 
335
     point->x = x;
 
336
     point->y = y;
 
337
     gtk_list_store_set (data->store, &iter, 0, point, -1);
 
338
   }
 
339
}
 
340
 
 
341
static void
 
342
set_buttons_sensitivity(PolygonProperties_t *data)
 
343
{
 
344
   gint rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(data->store),
 
345
                                               NULL);
 
346
   gtk_widget_set_sensitive(data->insert, rows != MAX_POLYGON_POINTS);
 
347
   gtk_widget_set_sensitive(data->append, rows != MAX_POLYGON_POINTS);
 
348
   gtk_widget_set_sensitive(data->remove, rows > 2);
 
349
}
 
350
 
 
351
static void
 
352
insert_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
 
353
{
 
354
   GtkTreeIter iter;
 
355
   GtkTreeModel *model = GTK_TREE_MODEL(data->store);
 
356
 
 
357
   if (gtk_tree_selection_get_selected (data->selection, &model, &iter)) {
 
358
     Polygon_t *polygon = ObjectToPolygon(data->obj);
 
359
     GdkPoint *point;
 
360
     GList *here;
 
361
     gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->x));
 
362
     gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->y));
 
363
 
 
364
     gtk_tree_model_get (model, &iter, 0, &point, -1);
 
365
     here = g_list_find(polygon->points, point);
 
366
     polygon->points = g_list_insert_before(polygon->points, here,
 
367
                                            new_point(x, y));
 
368
     polygon_fill_info_tab(data->obj, data);
 
369
   }
 
370
}
 
371
 
 
372
static void
 
373
append_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
 
374
{
 
375
   gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->x));
 
376
   gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->y));
 
377
 
 
378
   polygon_append_point(ObjectToPolygon(data->obj), x, y);
 
379
   polygon_fill_info_tab(data->obj, data);
 
380
}
 
381
 
 
382
static void
 
383
remove_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
 
384
{
 
385
  GtkTreeIter iter;
 
386
  GtkTreeModel *model = GTK_TREE_MODEL(data->store);
 
387
 
 
388
  if (gtk_tree_selection_get_selected (data->selection, &model, &iter)) {
 
389
    Polygon_t *polygon = ObjectToPolygon(data->obj);
 
390
    GdkPoint *point;
 
391
 
 
392
    gtk_tree_model_get (model, &iter, 0, &point, -1);
 
393
    polygon->points = g_list_remove(polygon->points, point);
 
394
    g_free(point);
 
395
 
 
396
    polygon_fill_info_tab(data->obj, data);
 
397
  }
 
398
}
 
399
 
 
400
static void
 
401
x_changed_cb(GtkWidget *widget, gpointer data)
 
402
{
 
403
   Object_t *obj = ((PolygonProperties_t*) data)->obj;
 
404
   gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
 
405
   _sash_point->x = x;
 
406
   edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
 
407
}
 
408
 
 
409
static void
 
410
y_changed_cb(GtkWidget *widget, gpointer data)
 
411
{
 
412
   Object_t *obj = ((PolygonProperties_t*) data)->obj;
 
413
   gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
 
414
   _sash_point->y = y;
 
415
   edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
 
416
}
 
417
 
 
418
static void
 
419
render_x(GtkTreeViewColumn *column, GtkCellRenderer *cell,
 
420
         GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
 
421
{
 
422
  GdkPoint *point;
 
423
  gchar scratch[16];
 
424
 
 
425
  gtk_tree_model_get(tree_model, iter, 0, &point, -1);
 
426
  sprintf(scratch, "%d", point->x);
 
427
  g_object_set(cell, "text", scratch, "xalign", 1.0, NULL);
 
428
}
 
429
 
 
430
static void
 
431
render_y(GtkTreeViewColumn *column, GtkCellRenderer *cell,
 
432
         GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
 
433
{
 
434
  GdkPoint *point;
 
435
  gchar scratch[16];
 
436
 
 
437
  gtk_tree_model_get(tree_model, iter, 0, &point, -1);
 
438
  sprintf(scratch, "%d", point->y);
 
439
  g_object_set(cell, "text", scratch, "xalign", 1.0, NULL);
 
440
}
 
441
 
 
442
static gpointer
 
443
polygon_create_info_widget(GtkWidget *frame)
 
444
{
 
445
   PolygonProperties_t *props = g_new(PolygonProperties_t, 1);
 
446
   GtkWidget *hbox, *swin, *table, *label;
 
447
   GtkWidget *view;
 
448
   gint max_width = get_image_width();
 
449
   gint max_height = get_image_height();
 
450
   GtkCellRenderer *renderer;
 
451
   GtkTreeViewColumn *column;
 
452
 
 
453
   hbox = gtk_hbox_new(FALSE, 12);
 
454
   gtk_container_add(GTK_CONTAINER(frame), hbox);
 
455
   gtk_widget_show(hbox);
 
456
 
 
457
   swin = gtk_scrolled_window_new(NULL, NULL);
 
458
 
 
459
   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
 
460
                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
 
461
   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(swin),
 
462
                                        GTK_SHADOW_IN);
 
463
   gtk_box_pack_start(GTK_BOX(hbox), swin, FALSE, FALSE, FALSE);
 
464
   gtk_widget_show(swin);
 
465
 
 
466
   props->store = gtk_list_store_new (1, G_TYPE_POINTER);
 
467
   view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (props->store));
 
468
   gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
 
469
   g_object_unref (props->store);
 
470
   gtk_widget_show (view);
 
471
 
 
472
   renderer = gtk_cell_renderer_text_new ();
 
473
   column = gtk_tree_view_column_new_with_attributes (_("x (pixels)"),
 
474
                                                      renderer,
 
475
                                                      NULL);
 
476
   gtk_tree_view_column_set_cell_data_func(column, renderer,
 
477
                                           render_x, props, NULL);
 
478
   gtk_tree_view_column_set_alignment(column, 0.5);
 
479
   gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
 
480
 
 
481
   renderer = gtk_cell_renderer_text_new ();
 
482
   column = gtk_tree_view_column_new_with_attributes (_("y (pixels)"),
 
483
                                                      renderer,
 
484
                                                      NULL);
 
485
   gtk_tree_view_column_set_cell_data_func(column, renderer,
 
486
                                           render_y, props, NULL);
 
487
   gtk_tree_view_column_set_alignment(column, 0.5);
 
488
   gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
 
489
 
 
490
   gtk_container_add (GTK_CONTAINER (swin), view);
 
491
 
 
492
   table = gtk_table_new(6, 3, FALSE);
 
493
   gtk_table_set_row_spacings(GTK_TABLE(table), 6);
 
494
   gtk_table_set_col_spacings(GTK_TABLE(table), 6);
 
495
   gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, FALSE);
 
496
   gtk_widget_show(table);
 
497
 
 
498
   label = create_label_in_table(table, 0, 0, "_x:");
 
499
   props->x = create_spin_button_in_table(table, label, 0, 1, 1, 0,
 
500
                                          max_width - 1);
 
501
   g_signal_connect(props->x, "changed",
 
502
                    G_CALLBACK(x_changed_cb), (gpointer) props);
 
503
   gtk_widget_set_size_request(props->x, 64, -1);
 
504
   create_label_in_table(table, 0, 2, _("pixels"));
 
505
 
 
506
   label = create_label_in_table(table, 1, 0, "_y:");
 
507
   props->y = create_spin_button_in_table(table, label, 1, 1, 1, 0,
 
508
                                          max_height - 1);
 
509
   g_signal_connect(props->y, "changed",
 
510
                    G_CALLBACK(y_changed_cb), (gpointer) props);
 
511
   gtk_widget_set_size_request(props->y, 64, -1);
 
512
   create_label_in_table(table, 1, 2, _("pixels"));
 
513
 
 
514
   props->update = gtk_button_new_with_mnemonic(_("_Update"));
 
515
   g_signal_connect(props->update, "clicked",
 
516
                    G_CALLBACK(update_button_clicked), props);
 
517
   gtk_table_attach_defaults(GTK_TABLE(table), props->update, 1, 2, 2, 3);
 
518
   gtk_widget_show(props->update);
 
519
 
 
520
   props->insert = gtk_button_new_with_mnemonic(_("_Insert"));
 
521
   g_signal_connect(props->insert, "clicked",
 
522
                    G_CALLBACK(insert_button_clicked), props);
 
523
   gtk_table_attach_defaults(GTK_TABLE(table), props->insert, 1, 2, 3, 4);
 
524
   gtk_widget_show(props->insert);
 
525
 
 
526
   props->append = gtk_button_new_with_mnemonic(_("A_ppend"));
 
527
   g_signal_connect(props->append, "clicked",
 
528
                    G_CALLBACK(append_button_clicked), props);
 
529
   gtk_table_attach_defaults(GTK_TABLE(table), props->append, 1, 2, 4, 5);
 
530
   gtk_widget_show(props->append);
 
531
 
 
532
   props->remove = gtk_button_new_with_mnemonic(_("_Remove"));
 
533
   g_signal_connect(props->remove, "clicked",
 
534
                    G_CALLBACK(remove_button_clicked), props);
 
535
   gtk_table_attach_defaults(GTK_TABLE(table), props->remove, 1, 2, 5, 6);
 
536
   gtk_widget_show(props->remove);
 
537
 
 
538
   props->timeout = 0;
 
539
 
 
540
   props->selection = gtk_tree_view_get_selection(GTK_TREE_VIEW (view));
 
541
   gtk_tree_selection_set_mode(props->selection, GTK_SELECTION_SINGLE);
 
542
   g_signal_connect (props->selection, "changed",
 
543
                     G_CALLBACK (select_row_cb), props);
 
544
 
 
545
   return props;
 
546
}
 
547
 
 
548
static gboolean
 
549
update_timeout(gpointer data)
 
550
{
 
551
   PolygonProperties_t *props = (PolygonProperties_t*) data;
 
552
   polygon_fill_info_tab(props->obj, data);
 
553
   return FALSE;
 
554
}
 
555
 
 
556
static void
 
557
polygon_update_info_widget(Object_t *obj, gpointer data)
 
558
{
 
559
   PolygonProperties_t *props = (PolygonProperties_t*) data;
 
560
   GtkTreeIter iter;
 
561
 
 
562
   gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->x), _sash_point->x);
 
563
   gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), _sash_point->y);
 
564
 
 
565
   if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(props->store), &iter,
 
566
                                     NULL, _sash_index)) {
 
567
     gtk_tree_selection_select_iter(props->selection, &iter);
 
568
   }
 
569
 
 
570
   if (props->timeout)
 
571
      g_source_remove(props->timeout);
 
572
   props->timeout = g_timeout_add(1000, update_timeout, data);
 
573
}
 
574
 
 
575
static void
 
576
polygon_fill_info_tab(Object_t *obj, gpointer data)
 
577
{
 
578
   Polygon_t *polygon = ObjectToPolygon(obj);
 
579
   PolygonProperties_t *props = (PolygonProperties_t*) data;
 
580
   GtkTreeIter iter;
 
581
   GList     *p;
 
582
 
 
583
   props->obj = obj;
 
584
 
 
585
   gtk_list_store_clear(props->store);
 
586
 
 
587
   for (p = polygon->points; p; p = p->next) {
 
588
     gtk_list_store_append(props->store, &iter);
 
589
     gtk_list_store_set(props->store, &iter, 0, p->data, -1);
 
590
   }
 
591
 
 
592
   if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(props->store), &iter,
 
593
                                     NULL, _sash_index)) {
 
594
     gtk_tree_selection_select_iter(props->selection, &iter);
 
595
   }
 
596
   set_buttons_sensitivity(props);
 
597
}
 
598
 
 
599
static void
 
600
polygon_set_initial_focus(Object_t *obj, gpointer data)
 
601
{
 
602
   PolygonProperties_t *props = (PolygonProperties_t*) data;
 
603
   gtk_widget_grab_focus(props->x);
 
604
}
 
605
 
 
606
static void
 
607
polygon_update(Object_t* obj, gpointer data)
 
608
{
 
609
  /* Nothing to be done! */
 
610
}
 
611
 
 
612
static void
 
613
polygon_write_csim(Object_t *obj, gpointer param, OutputFunc_t output)
 
614
{
 
615
   Polygon_t *polygon = ObjectToPolygon(obj);
 
616
   GList     *p;
 
617
 
 
618
   output(param, "\"poly\" coords=\"");
 
619
   for (p = polygon->points; p; p = p->next) {
 
620
      GdkPoint *point = (GdkPoint*) p->data;
 
621
      output(param, "%d,%d", point->x, point->y);
 
622
      output(param, "%c", (p->next) ? ',' : '"');
 
623
   }
 
624
}
 
625
 
 
626
static void
 
627
polygon_write_cern(Object_t *obj, gpointer param, OutputFunc_t output)
 
628
{
 
629
   Polygon_t *polygon = ObjectToPolygon(obj);
 
630
   GList     *p;
 
631
   GdkPoint  *first = (GdkPoint*) polygon->points->data;
 
632
 
 
633
   output(param, "poly ");
 
634
   for (p = polygon->points; p; p = p->next) {
 
635
      GdkPoint *point = (GdkPoint*) p->data;
 
636
      output(param, "(%d,%d) ", point->x, point->y);
 
637
   }
 
638
   output(param, "(%d,%d)", first->x, first->y);
 
639
}
 
640
 
 
641
static void
 
642
polygon_write_ncsa(Object_t *obj, gpointer param, OutputFunc_t output)
 
643
{
 
644
   Polygon_t *polygon = ObjectToPolygon(obj);
 
645
   GList     *p;
 
646
   GdkPoint  *first = (GdkPoint*) polygon->points->data;
 
647
 
 
648
   output(param, "poly %s", obj->url);
 
649
   for (p = polygon->points; p; p = p->next) {
 
650
      GdkPoint *point = (GdkPoint*) p->data;
 
651
      output(param, " %d,%d", point->x, point->y);
 
652
   }
 
653
   output(param, " %d,%d", first->x, first->y);
 
654
}
 
655
 
 
656
static gboolean _insert_edge;
 
657
static gint _insert_x;
 
658
static gint _insert_y;
 
659
 
 
660
static void
 
661
polygon_insert_point(GtkWidget *widget, gpointer data)
 
662
{
 
663
   Command_t *command = insert_point_command_new(get_popup_object(), _insert_x,
 
664
                                                 _insert_y, _insert_edge);
 
665
   command_execute(command);
 
666
}
 
667
 
 
668
static void
 
669
polygon_delete_point(GtkWidget *widget, gpointer data)
 
670
{
 
671
   Command_t *command = delete_point_command_new(get_popup_object(),
 
672
                                                 _sash_point);
 
673
   command_execute(command);
 
674
}
 
675
 
 
676
static gboolean
 
677
point_near_edge(GdkPoint *first, GdkPoint *second, gint x, gint y)
 
678
{
 
679
   gint den, nom;
 
680
   gdouble u;
 
681
 
 
682
   den = (first->x - x) * (first->x - second->x) +
 
683
      (first->y - y) * (first->y - second->y);
 
684
   nom = (second->x - first->x) * (second->x - first->x) +
 
685
      (second->y - first->y) * (second->y - first->y);
 
686
   u = (gdouble) den / nom;
 
687
   if (u >= 0.0 && u <= 1.0) {
 
688
      gint sx = first->x + (gint) (u * (second->x - first->x)) - x;
 
689
      gint sy = first->y + (gint) (u * (second->y - first->y)) - y;
 
690
      return sx * sx + sy * sy <= 25; /* Fix me! */
 
691
   }
 
692
   return FALSE;
 
693
}
 
694
 
 
695
static gint
 
696
polygon_near_edge(Object_t *obj, gint x, gint y)
 
697
{
 
698
   Polygon_t *polygon = ObjectToPolygon(obj);
 
699
   GList     *p = polygon->points;
 
700
   GdkPoint  *first = (GdkPoint*) p->data;
 
701
   GdkPoint  *prev = first;
 
702
   gint n = 1;
 
703
 
 
704
   for (p = p->next; p; p = p->next, n++) {
 
705
      GdkPoint *next = (GdkPoint*) p->data;
 
706
      if (point_near_edge(prev, next, x, y))
 
707
         return n;
 
708
      prev = next;
 
709
   }
 
710
   return (point_near_edge(prev, first, x, y)) ? n + 1 : 0;
 
711
}
 
712
 
 
713
static void
 
714
polygon_do_popup(Object_t *obj, GdkEventButton *event)
 
715
{
 
716
   gint x = get_real_coord((gint) event->x);
 
717
   gint y = get_real_coord((gint) event->y);
 
718
 
 
719
   if (polygon_near_sash(obj, x, y)) {
 
720
      static ObjectPopup_t *delete_popup;
 
721
      if (!delete_popup) {
 
722
         delete_popup = make_object_popup();
 
723
         object_popup_prepend_menu(delete_popup, _("Delete Point"),
 
724
                                   polygon_delete_point, delete_popup);
 
725
      }
 
726
      object_handle_popup(delete_popup, obj, event);
 
727
   } else {
 
728
      _insert_edge = polygon_near_edge(obj, x, y);
 
729
      if (_insert_edge) {
 
730
         static ObjectPopup_t *insert_popup;
 
731
 
 
732
         _insert_x = x;
 
733
         _insert_y = y;
 
734
 
 
735
         if (!insert_popup) {
 
736
            insert_popup = make_object_popup();
 
737
            object_popup_prepend_menu(insert_popup, _("Insert Point"),
 
738
                                      polygon_insert_point, insert_popup);
 
739
         }
 
740
         object_handle_popup(insert_popup, obj, event);
 
741
      } else {
 
742
         object_do_popup(obj, event);
 
743
      }
 
744
   }
 
745
}
 
746
 
 
747
static const gchar*
 
748
polygon_get_stock_icon_name(void)
 
749
{
 
750
   return IMAP_STOCK_POLYGON;
 
751
}
 
752
 
 
753
static GList *_prev_link;
 
754
 
 
755
static gboolean
 
756
polygon_factory_finish(Object_t *obj, gint x, gint y)
 
757
{
 
758
   Polygon_t *polygon = ObjectToPolygon(obj);
 
759
   GdkPoint *prev_point = (GdkPoint*) _prev_link->data;
 
760
 
 
761
   if (x == prev_point->x && y == prev_point->y) {
 
762
      polygon_remove_last_point(polygon);
 
763
      return TRUE;
 
764
   } else {
 
765
      polygon_append_point(polygon, x, y);
 
766
      _prev_link = _prev_link->next;
 
767
   }
 
768
   return FALSE;
 
769
}
 
770
 
 
771
static gboolean
 
772
polygon_factory_cancel(GdkEventButton *event, Object_t *obj)
 
773
{
 
774
   if (event->state & GDK_SHIFT_MASK) {
 
775
      return TRUE;
 
776
   } else {
 
777
      Polygon_t *polygon = ObjectToPolygon(obj);
 
778
      GList *link = _prev_link;
 
779
 
 
780
      _prev_link = _prev_link->prev;
 
781
      g_free((GdkPoint*) link->data);
 
782
      polygon->points = g_list_remove_link(polygon->points, link);
 
783
   }
 
784
   return _prev_link == NULL;
 
785
}
 
786
 
 
787
static Object_t*
 
788
polygon_factory_create_object(gint x, gint y)
 
789
{
 
790
   GList *points;
 
791
 
 
792
   points = _prev_link = g_list_append(NULL, new_point(x, y));
 
793
   points = g_list_append(points, new_point(x, y));
 
794
 
 
795
   return create_polygon(points);
 
796
}
 
797
 
 
798
static void
 
799
polygon_factory_set_xy(Object_t *obj, guint state, gint x, gint y)
 
800
{
 
801
   Polygon_t *polygon = ObjectToPolygon(obj);
 
802
   GList *last = g_list_last(polygon->points);
 
803
   GdkPoint *point = (GdkPoint*) last->data;
 
804
   GdkPoint *prev = (GdkPoint*) last->prev->data;
 
805
 
 
806
   point->x = x;
 
807
   point->y = y;
 
808
 
 
809
   main_set_dimension(x - prev->x, y - prev->y);
 
810
}
 
811
 
 
812
static ObjectFactory_t polygon_factory = {
 
813
   NULL,                        /* Object pointer */
 
814
   polygon_factory_finish,
 
815
   polygon_factory_cancel,
 
816
   polygon_factory_create_object,
 
817
   polygon_factory_set_xy
 
818
};
 
819
 
 
820
ObjectFactory_t*
 
821
get_polygon_factory(guint state)
 
822
{
 
823
   return &polygon_factory;
 
824
}
 
825
 
 
826
void
 
827
polygon_remove_last_point(Polygon_t *polygon)
 
828
{
 
829
   GList *last = g_list_last(polygon->points);
 
830
   g_free((GdkPoint*) last->data);
 
831
   polygon->points = g_list_remove_link(polygon->points, last);
 
832
}
 
833
 
 
834
GdkPoint*
 
835
new_point(gint x, gint y)
 
836
{
 
837
   GdkPoint *point = g_new(GdkPoint, 1);
 
838
   point->x = x;
 
839
   point->y = y;
 
840
   return point;
 
841
}
 
842
 
 
843
void
 
844
polygon_append_point(Polygon_t *polygon, gint x, gint y)
 
845
{
 
846
  polygon->points = g_list_append(polygon->points, new_point(x, y));
 
847
}