- Remove GLIB1 code
[obnox/wireshark/wip.git] / gtk / uat_gui.c
1 /*
2  *  uat_gui.c
3  *
4  *  $Id$
5  *
6  *  User Accessible Tables GUI
7  *  Mantain an array of user accessible data strucures
8  *  
9  * (c) 2007, Luis E. Garcia Ontanon <luis.ontanon@gmail.com>
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
13  * Copyright 2001 Gerald Combs
14  * 
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  * 
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * 
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28  */
29
30 /*
31  * TO DO:
32  * + improvements
33  *   - field value check (red/green editbox)
34  *   - tooltips (add field descriptions)
35  */
36
37 #ifdef HAVE_CONFIG_H
38 # include "config.h"
39 #endif
40
41 #include <string.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <math.h>
45 #include <gtk/gtk.h>
46 #include <epan/dfilter/dfilter-macro.h>
47 #include <epan/emem.h>
48 #include <epan/report_err.h>
49 #include <gdk/gdkkeysyms.h>
50 #include "gtkglobals.h"
51 #include "gui_utils.h"
52 #include "dlg_utils.h"
53 #include "help_dlg.h"
54 #include "menu.h"
55 #include "compat_macros.h"
56 #include <epan/proto.h>
57 #include <epan/packet.h>
58 #include "../stat_menu.h"
59 #include "gui_stat_menu.h"
60 #include "main.h"
61
62 #include <epan/uat-int.h>
63 #include <epan/value_string.h>
64 #include "uat_gui.h"
65
66 #if GTK_MAJOR_VERSION >= 2
67 # undef GTK_MAJOR_VERSION
68 # define GTK_MAJOR_VERSION 1
69 # define BUTTON_SIZE_X -1
70 # define BUTTON_SIZE_Y -1
71 #else
72 # define BUTTON_SIZE_X 50
73 # define BUTTON_SIZE_Y 20
74 #endif
75
76 struct _uat_rep_t {
77         GtkWidget* window;
78         GtkWidget* vbox;
79         GtkWidget* scrolledwindow;
80         GtkWidget* clist;
81         GtkWidget* bbox;
82         GtkWidget* bt_new;
83         GtkWidget* bt_edit;
84         GtkWidget* bt_delete;
85         GtkWidget* bt_up;
86         GtkWidget* bt_down;
87         GtkWidget* bt_apply;
88         GtkWidget* bt_cancel;
89         GtkWidget* bt_ok;
90         GtkWidget* unsaved_window;
91
92         gint selected;
93         gboolean dont_save;
94 #if GTK_MAJOR_VERSION >= 2
95         GtkTreeSelection  *selection;
96 #endif
97         
98 };
99
100 struct _str_pair {
101         const char* ptr;
102         unsigned len;
103 };
104
105 struct _uat_dlg_data {
106         GtkWidget* win;
107         GPtrArray* entries;
108         uat_t* uat;
109         void* rec;
110         gboolean is_new;
111         gint row;
112         GPtrArray* tobe_freed;
113 };
114
115
116 static gboolean unsaved_dialog(GtkWindow *w, GdkEvent* e, gpointer u);
117 static gboolean uat_window_delete_event_cb(GtkWindow *w, GdkEvent* e, gpointer u);
118
119 static void set_buttons(uat_t* uat, gint row) {
120
121         if (!uat->rep) return;
122
123         if (row > 0) {
124                 gtk_widget_set_sensitive (uat->rep->bt_up, TRUE);
125         } else {
126                 gtk_widget_set_sensitive (uat->rep->bt_up, FALSE);
127         }
128
129         if (row < (gint)(*uat->nrows_p - 1) && row >= 0) {
130                 gtk_widget_set_sensitive (uat->rep->bt_down, TRUE);
131         } else {
132                 gtk_widget_set_sensitive (uat->rep->bt_down, FALSE);
133         }
134
135         if (uat->changed) {
136                 SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, uat_window_delete_event_cb, uat);
137                 SIGNAL_CONNECT(uat->rep->window, "delete_event", unsaved_dialog, uat);
138                 SIGNAL_CONNECT(uat->rep->window, "destroy", unsaved_dialog, uat);
139         } else {
140                 SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, unsaved_dialog, uat);
141                 SIGNAL_CONNECT(GTK_WINDOW(uat->rep->window), "delete_event", uat_window_delete_event_cb, uat);
142                 SIGNAL_CONNECT(GTK_WINDOW(uat->rep->window), "destroy", uat_window_delete_event_cb, uat);
143         }
144 }
145
146 static char* fld_tostr(void* rec, uat_field_t* f) {
147         guint len;
148         const char* ptr;
149         char* out;
150
151         f->cb.tostr(rec,&ptr,&len,f->cbdata.tostr,f->fld_data);
152
153         switch(f->mode) {
154                 case PT_TXTMOD_STRING:
155                 case PT_TXTMOD_ENUM:
156                         out = ep_strndup(ptr,len);
157                         break;
158                 case PT_TXTMOD_HEXBYTES: {
159                         GString* s = g_string_sized_new( len*2 + 1 );
160                         guint i;
161                         
162                         for (i=0; i<len;i++) g_string_sprintfa(s,"%.2X",((guint8*)ptr)[i]);
163                         
164                         out = ep_strdup_printf(s->str);
165                         
166                         g_string_free(s,TRUE);
167                         break;
168                 } 
169                 default:
170                         g_assert_not_reached();
171                         out = NULL;
172                         break;
173         }
174
175         return out;
176 }
177
178
179
180 static void append_row(uat_t* uat, guint idx) {
181         GPtrArray* a = g_ptr_array_new();
182         void* rec = UAT_INDEX_PTR(uat,idx);
183         uat_field_t* f = uat->fields;
184         guint rownum;
185         guint colnum;
186
187         if (! uat->rep) return;
188
189         gtk_clist_freeze(GTK_CLIST(uat->rep->clist));
190
191         for ( colnum = 0; colnum < uat->ncols; colnum++ )
192                 g_ptr_array_add(a,fld_tostr(rec,&(f[colnum])));
193
194         rownum = gtk_clist_append(GTK_CLIST(uat->rep->clist), (gchar**)a->pdata);
195         gtk_clist_set_row_data(GTK_CLIST(uat->rep->clist), rownum, rec);
196
197         gtk_clist_thaw(GTK_CLIST(uat->rep->clist));
198
199         g_ptr_array_free(a,TRUE);
200 }
201
202 static void reset_row(uat_t* uat, guint idx) {
203         void* rec = UAT_INDEX_PTR(uat,idx);
204         uat_field_t* f = uat->fields;
205         guint colnum;
206
207         if (! uat->rep) return;
208
209         gtk_clist_freeze(GTK_CLIST(uat->rep->clist));
210         
211         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
212                 gtk_clist_set_text(GTK_CLIST(uat->rep->clist), idx, colnum, fld_tostr(rec,&(f[colnum])));
213         }
214         
215         gtk_clist_thaw(GTK_CLIST(uat->rep->clist));
216
217 }
218
219 static guint8* unhexbytes(const char* si, guint len, guint* len_p, const char** err) {
220         guint8* buf;
221         guint8* p;
222         const guint8* s = (void*)si;
223         unsigned i;
224
225         if (len % 2) {
226                 *err = "Uneven number of chars hex string";
227                 return NULL;
228         }
229
230         buf = ep_alloc(len/2+1);
231         p = buf;
232
233         for (i = 0; i<len ; i += 2) {
234                 guint8 lo = s[i+1];
235                 guint8 hi = s[i];
236
237                 if (hi >= '0' && hi <= '9') {
238                         hi -= '0';
239                 } else if (hi >= 'a' && hi <= 'f') {
240                         hi -=  'a';
241                         hi += 0xa;
242                 } else if (hi >= 'A' && hi <= 'F') {
243                         hi -=  'A';
244                         hi += 0xa;
245                 } else {
246                         goto on_error;
247                 }
248
249                 if (lo >= '0' && lo <= '9') {
250                         lo -= '0';
251                 } else if (lo >= 'a' && lo <= 'f') {
252                         lo -=  'a';
253                         lo += 0xa;
254                 } else if (lo >= 'A' && lo <= 'F') {
255                         lo -=  'A';
256                         lo += 0xa;
257                 } else {
258                         goto on_error;
259                 }
260
261                 *(p++) = (hi*0x10) + lo;
262         }
263
264         len /= 2;
265
266         if (len_p) *len_p = len;
267
268         buf[len] = '\0';
269
270         *err = NULL;
271         return buf;
272         
273 on_error:
274         *err = "Error parsing hex string";
275         return NULL;
276 }
277
278
279 static gboolean uat_dlg_cb(GtkWidget *win _U_, gpointer user_data) {
280         struct _uat_dlg_data* dd = user_data;
281         guint ncols = dd->uat->ncols;
282         uat_field_t* f = dd->uat->fields;
283         const char* err = NULL;
284         guint colnum;
285
286         for ( colnum = 0; colnum < ncols; colnum++ ) {
287                 void* e = g_ptr_array_index(dd->entries,colnum);
288                 const char* text;
289                 unsigned len = 0;
290
291                 switch(f[colnum].mode) {
292                         case PT_TXTMOD_STRING:
293                                 text = gtk_entry_get_text(GTK_ENTRY(e));
294                                 len = strlen(text);
295                                 break;
296                         case PT_TXTMOD_HEXBYTES: {
297                                 text = gtk_entry_get_text(GTK_ENTRY(e));
298
299                                 text = (void*) unhexbytes(text, strlen(text), &len, &err);
300
301                                 if (err) {
302                                         err = ep_strdup_printf("error in field '%s': %s",f[colnum].name,err);
303                                         goto on_failure;
304                                 }
305
306                                 break;
307                         }
308                         case PT_TXTMOD_ENUM: {
309                                 text = *(char**)e;
310                                 text = text ? text : "";
311                                 len = strlen(text);
312                                 break;
313                         }
314                         default:
315                                 g_assert_not_reached();
316                                 return FALSE;
317                 }
318
319
320                 if (f[colnum].cb.chk) {
321                         if (! f[colnum].cb.chk(dd->rec, text, len, f[colnum].cbdata.chk, f[colnum].fld_data, &err)) {
322                                 err = ep_strdup_printf("error in field '%s': %s",f[colnum].name,err);
323                                 goto on_failure;
324                         }
325                 }
326
327                 f[colnum].cb.set(dd->rec,text,len, f[colnum].cbdata.set, f[colnum].fld_data);
328         }
329
330         if (dd->uat->update_cb) {
331                 dd->uat->update_cb(dd->rec,&err);
332
333                 if (err) {
334                         err = ep_strdup_printf("error updating record: %s",err);
335                         goto on_failure;
336                 }
337         }
338
339         if (dd->is_new) {
340                 void* rec_tmp = dd->rec;
341                 dd->rec = uat_add_record(dd->uat, dd->rec);
342
343                 if (dd->uat->free_cb) {
344                         dd->uat->free_cb(rec_tmp);
345                 }
346
347                 g_free(rec_tmp);
348         }
349         
350         dd->uat->changed = TRUE;
351
352         set_buttons(dd->uat,-1);
353
354         if (dd->is_new) {
355                 append_row(dd->uat, (*dd->uat->nrows_p) - 1 );
356         } else {
357                 reset_row(dd->uat,dd->row);
358         }
359
360         g_ptr_array_free(dd->entries,TRUE);
361         window_destroy(GTK_WIDGET(dd->win));
362
363         if (dd->uat->rep)
364                 window_present(GTK_WIDGET(dd->uat->rep->window));
365
366         while (dd->tobe_freed->len) g_free( g_ptr_array_remove_index_fast(dd->tobe_freed, dd->tobe_freed->len - 1 ) );
367
368         g_free(dd);
369
370         return TRUE;
371 on_failure:
372
373         report_failure("%s",err);
374         return FALSE;
375 }
376
377 static gboolean uat_cancel_dlg_cb(GtkWidget *win _U_, gpointer user_data) {
378         struct _uat_dlg_data* dd = user_data;
379
380         if (dd->uat->rep)
381                 window_present(GTK_WIDGET(dd->uat->rep->window));
382
383         if (dd->is_new) g_free(dd->rec);
384         g_ptr_array_free(dd->entries,TRUE);
385         window_destroy(GTK_WIDGET(dd->win));
386
387         while (dd->tobe_freed->len) g_free( g_ptr_array_remove_index_fast(dd->tobe_freed, dd->tobe_freed->len - 1 ) );
388
389         g_free(dd);
390
391         return TRUE;
392 }
393
394 struct _fld_menu_item_data_t {
395         const char* text;
396         char const** valptr;
397 };
398
399 static void fld_menu_item_cb(GtkMenuItem *menuitem _U_, gpointer user_data) {
400         struct _fld_menu_item_data_t* md = user_data;
401         
402         *(md->valptr) = md->text;
403 }
404
405 static void fld_menu_item_destroy_cb(GtkMenuItem *menuitem _U_, gpointer user_data) {
406         g_free(user_data);
407 }
408
409 static void uat_edit_dialog(uat_t* uat, gint row) {
410         GtkWidget *win, *main_tb, *main_vb, *bbox, *bt_cancel, *bt_ok;
411         struct _uat_dlg_data* dd = g_malloc(sizeof(struct _uat_dlg_data));
412         uat_field_t* f = uat->fields;
413         guint colnum;
414         GtkTooltips *tooltips;
415
416         tooltips = gtk_tooltips_new();
417         
418         dd->entries = g_ptr_array_new();
419         dd->win = dlg_window_new(ep_strdup_printf("%s: %s", uat->name, (row == -1 ? "New" : "Edit")));
420         dd->uat = uat;
421         dd->rec = row < 0 ? g_malloc0(uat->record_size) : UAT_INDEX_PTR(uat,row);
422         dd->is_new = row < 0 ? TRUE : FALSE;
423         dd->row = row;
424         dd->tobe_freed = g_ptr_array_new();
425
426         win = dd->win;
427
428 #if GTK_MAJOR_VERSION >= 2
429         gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
430         gtk_window_resize(GTK_WINDOW(win),400, 30*(uat->ncols+2));
431 #else
432         gtk_window_set_policy(GTK_WINDOW(win), FALSE, FALSE, TRUE);
433         gtk_window_set_default_size(GTK_WINDOW(win), 400, 30*(uat->ncols+2));
434         gtk_widget_set_usize(win, 400, 30*(uat->ncols+2));
435 #endif
436
437         main_vb = gtk_vbox_new(FALSE,5);
438         gtk_container_add(GTK_CONTAINER(win), main_vb);
439         gtk_container_border_width(GTK_CONTAINER(main_vb), 6);
440
441         main_tb = gtk_table_new(uat->ncols+1, 2, FALSE);
442         gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
443         gtk_table_set_row_spacings(GTK_TABLE(main_tb), 5);
444         gtk_table_set_col_spacings(GTK_TABLE(main_tb), 10);
445
446         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
447                 GtkWidget *entry, *label, *event_box;
448                 char* text = fld_tostr(dd->rec,&(f[colnum]));
449
450                 event_box = gtk_event_box_new();
451
452                 label = gtk_label_new(f[colnum].name);
453                 if (f[colnum].desc != NULL)
454                         gtk_tooltips_set_tip(tooltips, event_box, f[colnum].desc, NULL);
455
456                 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
457                 gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 0, 1, colnum+1, colnum + 2);
458                 gtk_container_add(GTK_CONTAINER(event_box), label);
459
460                 switch(f[colnum].mode) {
461                         case PT_TXTMOD_STRING:
462                         case PT_TXTMOD_HEXBYTES: {
463                                 entry = gtk_entry_new();
464                                 g_ptr_array_add(dd->entries,entry);
465                                 gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2, colnum+1, colnum + 2);
466                                 if (! dd->is_new) {
467                                         gtk_entry_set_text(GTK_ENTRY(entry),text);
468                                 }
469                                 break;
470                         }
471                         case PT_TXTMOD_ENUM: {
472                                 GtkWidget *menu, *option_menu;
473                                 int menu_index, index;
474                                 const value_string* enum_vals = f[colnum].fld_data;
475                                 void* valptr = g_malloc0(sizeof(void*));
476
477                                 menu = gtk_menu_new();
478                                 menu_index = -1;
479                                 for (index = 0; enum_vals[index].strptr != NULL; index++) {
480                                         struct _fld_menu_item_data_t* md = g_malloc(sizeof(struct _fld_menu_item_data_t));
481                                         const char* str = enum_vals[index].strptr;
482                                         GtkWidget* menu_item = gtk_menu_item_new_with_label(str);
483                                         
484                                         md->text = str;
485                                         md->valptr = valptr;
486                                         
487                                         gtk_menu_append(GTK_MENU(menu), menu_item);
488                                         
489                                         if ( g_str_equal(str, text) ) {
490                                                 menu_index = index;
491                                                 *((char const**)valptr) = str;
492                                         }
493
494                                         SIGNAL_CONNECT(menu_item, "activate", fld_menu_item_cb, md);
495                                         SIGNAL_CONNECT(menu_item, "destroy", fld_menu_item_destroy_cb, md);
496                                 }
497
498                                 g_ptr_array_add(dd->entries,valptr);
499                                 g_ptr_array_add(dd->tobe_freed,valptr);
500
501                                 /* Create the option menu from the menu */
502                                 option_menu = gtk_option_menu_new();
503                                 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
504
505                                 /* Set its current value to the variable's current value */
506                                 if (menu_index != -1)
507                                         gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), menu_index);
508
509                                 gtk_table_attach_defaults(GTK_TABLE(main_tb), option_menu, 1, 2, colnum+1, colnum + 2);
510
511                                 break;
512                         }
513                         default:
514                                 g_assert_not_reached();
515                                 return;
516                 }
517         }
518         
519         bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
520         gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
521
522         bt_ok = OBJECT_GET_DATA(bbox, GTK_STOCK_OK);
523         SIGNAL_CONNECT(bt_ok, "clicked", uat_dlg_cb, dd);
524
525         bt_cancel = OBJECT_GET_DATA(bbox, GTK_STOCK_CANCEL);
526         SIGNAL_CONNECT(bt_cancel, "clicked", uat_cancel_dlg_cb, dd);
527         window_set_cancel_button(win, bt_cancel, NULL);
528
529         gtk_widget_show_all(win);
530 }
531
532 struct _uat_del {
533         GtkWidget *win;
534         uat_t* uat;
535         gint idx;
536 };
537
538 static void uat_del_cb(GtkButton *button _U_, gpointer u) {
539         struct _uat_del* ud = u;
540
541         uat_remove_record_idx(ud->uat, ud->idx);
542
543         if (ud->uat->rep)
544                 gtk_clist_remove(GTK_CLIST(ud->uat->rep->clist),ud->idx);
545
546         ud->uat->changed = TRUE;
547         set_buttons(ud->uat,-1);
548
549         window_destroy(GTK_WIDGET(ud->win));
550
551         if (ud->uat->rep)
552                 window_present(GTK_WIDGET(ud->uat->rep->window));
553
554         g_free(ud);
555 }
556
557 static void uat_cancel_del_cb(GtkButton *button _U_, gpointer u) {
558         struct _uat_del* ud = u;
559         window_destroy(GTK_WIDGET(ud->win));
560
561         if (ud->uat->rep)
562                 window_present(GTK_WIDGET(ud->uat->rep->window));
563         g_free(ud);
564 }
565
566 static void uat_del_dlg(uat_t* uat, int idx) {
567         GtkWidget *win, *main_tb, *main_vb, *bbox, *bt_cancel, *bt_ok;
568         uat_field_t* f = uat->fields;
569         guint colnum;
570         void* rec = UAT_INDEX_PTR(uat,idx);
571         struct _uat_del* ud = g_malloc(sizeof(struct _uat_del));
572
573         ud->uat = uat;
574         ud->idx = idx;
575         ud->win = win = dlg_window_new(ep_strdup_printf("%s: Confirm Delete", uat->name));
576         
577 #if GTK_MAJOR_VERSION >= 2
578         gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
579         gtk_window_resize(GTK_WINDOW(win),400,25*(uat->ncols+2));
580 #else
581         gtk_window_set_policy(GTK_WINDOW(win), FALSE, FALSE, TRUE);
582         gtk_window_set_default_size(GTK_WINDOW(win), 400, 25*(uat->ncols+2));
583         gtk_widget_set_usize(win, 400, 25*(uat->ncols+2));
584 #endif
585
586         main_vb = gtk_vbox_new(FALSE,5);
587         gtk_container_add(GTK_CONTAINER(win), main_vb);
588         gtk_container_border_width(GTK_CONTAINER(main_vb), 6);
589
590         main_tb = gtk_table_new(uat->ncols+1, 2, FALSE);
591         gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
592         gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
593         gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
594
595         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
596                 GtkWidget *label;
597                 char* text = fld_tostr(rec,&(f[colnum]));
598
599                 label = gtk_label_new(f[colnum].name);
600                 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
601                 gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, colnum+1, colnum + 2);
602                 
603                 label = gtk_label_new(text);
604                 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
605                 gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 1, 2, colnum+1, colnum + 2);
606         }
607
608         bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_DELETE, NULL);
609         gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
610
611         bt_ok = OBJECT_GET_DATA(bbox,GTK_STOCK_DELETE);
612         SIGNAL_CONNECT(bt_ok, "clicked", uat_del_cb, ud);
613
614         bt_cancel = OBJECT_GET_DATA(bbox, GTK_STOCK_CANCEL);
615         SIGNAL_CONNECT(bt_cancel, "clicked", uat_cancel_del_cb, ud);
616         window_set_cancel_button( win, bt_cancel, NULL);
617
618         gtk_widget_show_all(win);
619 }
620
621 static void uat_new_cb(GtkButton *button _U_, gpointer u) {
622         uat_t* uat = u;
623
624         if (! uat->rep) return;
625
626         uat_edit_dialog(uat, -1);
627 }
628
629 static void uat_edit_cb(GtkButton *button _U_, gpointer u) {
630         uat_t* uat = u;
631
632         if (! uat->rep) return;
633
634         uat_edit_dialog(uat, uat->rep->selected);
635 }
636
637 static void uat_delete_cb(GtkButton *button _U_, gpointer u) {
638         uat_t* uat = u;
639
640         if (! uat->rep) return;
641
642         uat_del_dlg(uat,uat->rep->selected);
643 }
644
645 static gboolean uat_window_delete_event_cb(GtkWindow *w _U_, GdkEvent* e _U_, gpointer u) {
646         uat_t* uat = u;
647         
648         if (uat->rep) {
649                 void* rep = uat->rep;
650
651                 SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, uat_window_delete_event_cb, uat);
652                 SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, unsaved_dialog, uat);
653
654                 gtk_widget_destroy(uat->rep->window);
655
656                 uat->rep = NULL;
657                 if (rep) g_free(rep);
658         }
659         return TRUE;
660 }
661
662 static void uat_up_cb(GtkButton *button _U_, gpointer u) {
663         uat_t* uat = u;
664         guint row = uat->rep->selected;
665
666         g_assert(row > 0);
667
668         uat_swap(uat,row,row-1);
669         gtk_clist_swap_rows(GTK_CLIST(uat->rep->clist),row,row-1);
670
671         uat->changed = TRUE;
672
673         row -= 1;
674         uat->rep->selected = row;
675         set_buttons(uat,row);
676 }
677
678 static void uat_down_cb(GtkButton *button _U_, gpointer u) {
679         uat_t* uat = u;
680         guint row = uat->rep->selected;
681
682         g_assert(row < *uat->nrows_p - 1);
683
684         uat_swap(uat,row,row+1);
685         gtk_clist_swap_rows(GTK_CLIST(uat->rep->clist),row,row+1);
686
687         uat->changed = TRUE;
688
689         row += 1;
690         uat->rep->selected = row;
691         set_buttons(uat,row);
692 }
693
694 static void uat_cancel_cb(GtkWidget *button _U_, gpointer u) {
695         uat_t* uat = u;
696         gchar* err = NULL;
697
698         if (uat->changed) {
699                 uat_clear(uat);
700                 uat_load(uat,&err);
701
702                 if (err) {
703                         report_failure("Error while loading %s: %s",uat->name,err);
704                 }
705
706                 if (cfile.state == FILE_READ_DONE) cf_reload(&cfile);
707         }
708
709         SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, uat_window_delete_event_cb, uat);
710         SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, unsaved_dialog, uat);
711         gtk_widget_destroy(uat->rep->window);
712         g_free(uat->rep);
713         uat->rep = NULL;
714 }
715
716 static void uat_apply_cb(GtkButton *button _U_, gpointer u) {
717         uat_t* uat = u;
718
719         uat_window_delete_event_cb(NULL,NULL,uat);
720
721         if (uat->changed && cfile.state == FILE_READ_DONE)
722                 cf_reload(&cfile);
723 }
724
725 static void uat_ok_cb(GtkButton *button _U_, gpointer u) {
726         uat_t* uat = u;
727         gchar* err = NULL;
728
729         if (uat->changed) {
730                 uat_save(uat,&err);
731
732                 if (err) {
733                         report_failure("Error while saving %s: %s",uat->name,err);
734                 }
735
736                 if (cfile.state == FILE_READ_DONE) cf_reload(&cfile);
737         }
738
739         SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, uat_window_delete_event_cb, uat);
740         SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, unsaved_dialog, uat);
741         gtk_widget_destroy(uat->rep->window);
742         g_free(uat->rep);
743         uat->rep = NULL;
744 }
745
746
747
748 static void remember_selected_row(GtkCList *clist _U_, gint row, gint column _U_, GdkEvent *event _U_, gpointer u) {
749         uat_t* uat = u;
750
751         uat->rep->selected = row;
752
753         gtk_widget_set_sensitive (uat->rep->bt_edit, TRUE);
754         gtk_widget_set_sensitive(uat->rep->bt_delete, TRUE);
755
756         set_buttons(uat,row);
757 }
758
759 static void unremember_selected_row(GtkCList *clist _U_, gint row _U_, gint column _U_, GdkEvent *event _U_, gpointer u) {
760         uat_t* uat = u;
761
762         uat->rep->selected = -1;
763         gtk_widget_set_sensitive (uat->rep->bt_edit, FALSE);
764         gtk_widget_set_sensitive(uat->rep->bt_delete, FALSE);
765         gtk_widget_set_sensitive (uat->rep->bt_down, FALSE);
766         gtk_widget_set_sensitive (uat->rep->bt_up, FALSE);
767 }
768
769 static void uat_yessave_cb(GtkWindow *w _U_, void* u) {
770         uat_t* uat = u;
771         gchar* err = NULL;
772
773         window_delete_event_cb(uat->rep->unsaved_window,NULL,NULL);
774
775         uat_save(uat,&err);
776
777         if (err) {
778                 report_failure("Error while saving %s: %s",uat->name,err);
779         }
780
781         SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, uat_window_delete_event_cb, uat);
782         SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, unsaved_dialog, uat);
783         window_destroy(uat->rep->window);
784
785         g_free(uat->rep);
786         uat->rep = NULL;
787 }
788
789
790 static void uat_nosave_cb(GtkWindow *w _U_, void* u) {
791         uat_t* uat = u;
792         window_delete_event_cb(uat->rep->unsaved_window,NULL,NULL);
793         SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, uat_window_delete_event_cb, uat);
794         SIGNAL_DISCONNECT_BY_FUNC(uat->rep->window, unsaved_dialog, uat);
795         window_destroy(uat->rep->window);
796
797         g_free(uat->rep);
798         uat->rep = NULL;
799 }
800
801 static gboolean unsaved_dialog(GtkWindow *w _U_, GdkEvent* e _U_, gpointer u) {
802         GtkWidget *win, *vbox, *label, *bbox;
803         GtkWidget *yes_bt, *no_bt;
804         gchar* message;
805         uat_t* uat  = u;
806
807         if (uat->rep->unsaved_window) {
808                 window_present(uat->rep->unsaved_window);
809                 return TRUE;
810         }
811
812         uat->rep->unsaved_window = win = window_new(GTK_WINDOW_TOPLEVEL, "Discard Changes?");
813         gtk_window_set_default_size(GTK_WINDOW(win), 360, 140);
814
815 #if GTK_MAJOR_VERSION >= 2
816         gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);
817 #else
818         gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
819 #endif
820         vbox = gtk_vbox_new(FALSE, 12);
821         gtk_container_border_width(GTK_CONTAINER(vbox), 6);
822         gtk_container_add(GTK_CONTAINER(win), vbox);
823
824         message  = ep_strdup_printf("Changes to '%s' are not being saved!\n"
825                 "Do you want to save '%s'?", uat->name, uat->name);
826
827         label = gtk_label_new(message);
828
829         bbox = dlg_button_row_new(GTK_STOCK_YES,GTK_STOCK_NO, NULL);
830
831         yes_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_YES);
832         no_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_NO);
833
834         SIGNAL_CONNECT(no_bt, "clicked", uat_nosave_cb, uat);
835         SIGNAL_CONNECT(yes_bt, "clicked", uat_yessave_cb, uat);
836
837         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
838         gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
839
840         gtk_widget_show_all(win);
841         window_present(win);
842
843         return TRUE;
844 }
845
846 static void uat_help_cb(GtkWidget* w _U_, gpointer u) {
847         help_topic_html(ep_strdup_printf("%s.html",((uat_t*)u)->help));
848 }
849
850 static GtkWidget* uat_window(void* u) {
851         uat_t* uat = u;
852         uat_field_t* f = uat->fields;
853         uat_rep_t* rep;
854         guint i;
855         guint colnum;
856         GtkWidget *hbox, *vbox, *move_hbox, *edit_hbox;
857
858         if (uat->rep) {
859                 window_present(uat->rep->window);
860                 return uat->rep->window;
861         } else {
862                 uat->rep = rep = g_malloc0(sizeof(uat_rep_t));
863         }
864
865         rep->window = window_new(GTK_WINDOW_TOPLEVEL, uat->name);
866
867 #if GTK_MAJOR_VERSION >= 2
868         gtk_window_set_resizable(GTK_WINDOW(rep->window),FALSE);
869         gtk_window_resize(GTK_WINDOW(rep->window), 720, 512);
870 #else
871         gtk_window_set_policy(GTK_WINDOW(rep->window), FALSE, FALSE, TRUE);
872         gtk_window_set_default_size(GTK_WINDOW(rep->window), 720, 512);
873         gtk_widget_set_usize(rep->window, 720, 512);
874 #endif
875
876
877 #if GTK_MAJOR_VERSION >= 2
878         gtk_window_set_position(GTK_WINDOW(rep->window), GTK_WIN_POS_CENTER_ON_PARENT);
879 #else
880         gtk_window_set_position(GTK_WINDOW(rep->window), GTK_WIN_POS_CENTER);
881 #endif
882
883         gtk_container_border_width(GTK_CONTAINER(rep->window), 6);
884
885         rep->vbox = gtk_vbox_new(FALSE, 12);
886         gtk_container_border_width(GTK_CONTAINER(rep->vbox), 6);
887         gtk_container_add(GTK_CONTAINER(rep->window), rep->vbox);
888
889         hbox = gtk_hbox_new(FALSE,12);
890         gtk_container_border_width(GTK_CONTAINER(hbox), 6);
891         gtk_container_add(GTK_CONTAINER(rep->vbox), hbox);
892
893         vbox = gtk_vbox_new(FALSE, 12);
894         gtk_container_border_width(GTK_CONTAINER(vbox), 6);
895         gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
896
897         rep->scrolledwindow = scrolled_window_new(NULL, NULL);
898
899 #if GTK_MAJOR_VERSION >= 2
900         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(rep->scrolledwindow), GTK_SHADOW_IN);
901 #endif
902
903         rep->clist = gtk_clist_new(uat->ncols);
904         gtk_container_add(GTK_CONTAINER(rep->scrolledwindow), rep->clist);
905         gtk_box_pack_start(GTK_BOX(hbox), rep->scrolledwindow, TRUE, TRUE, 0);
906
907         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
908                 gtk_clist_set_column_title(GTK_CLIST(rep->clist), colnum, f[colnum].name);
909                 gtk_clist_set_column_auto_resize(GTK_CLIST(rep->clist), colnum, TRUE);
910         }
911
912         gtk_clist_column_titles_show(GTK_CLIST(rep->clist));
913         gtk_clist_freeze(GTK_CLIST(rep->clist));
914
915         for ( i = 0 ; i < *(uat->nrows_p); i++ ) {
916                 append_row(uat, i);
917         }
918
919         gtk_clist_thaw(GTK_CLIST(rep->clist));
920         
921 #if GTK_MAJOR_VERSION < 2
922         gtk_clist_set_selection_mode(GTK_CLIST(rep->clist),GTK_SELECTION_SINGLE);
923 #else
924         rep->selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rep->clist));
925         gtk_tree_selection_set_mode(rep->selection, GTK_SELECTION_SINGLE);
926 #endif
927
928         if(uat->help) {
929                 GtkWidget* help_btn;
930                 rep->bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, NULL);
931                 help_btn = OBJECT_GET_DATA(rep->bbox,GTK_STOCK_HELP);
932                 SIGNAL_CONNECT(help_btn, "clicked", uat_help_cb, uat);
933         } else {
934
935                 rep->bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, NULL);
936         }       
937
938         move_hbox = gtk_vbutton_box_new();
939         gtk_box_pack_start(GTK_BOX(vbox), move_hbox, TRUE, FALSE, 0);
940
941         edit_hbox = gtk_vbutton_box_new();
942         gtk_box_pack_end(GTK_BOX(vbox), edit_hbox, TRUE, FALSE, 0);
943
944
945         rep->bt_down = BUTTON_NEW_FROM_STOCK(GTK_STOCK_GO_DOWN);
946         rep->bt_up = BUTTON_NEW_FROM_STOCK(GTK_STOCK_GO_UP);
947
948         gtk_box_pack_start(GTK_BOX(move_hbox), rep->bt_up, TRUE, FALSE, 5);
949         gtk_box_pack_start(GTK_BOX(move_hbox), rep->bt_down, TRUE, FALSE, 5);
950
951
952         rep->bt_new = BUTTON_NEW_FROM_STOCK(GTK_STOCK_NEW);
953         rep->bt_edit = BUTTON_NEW_FROM_STOCK(WIRESHARK_STOCK_EDIT);
954         rep->bt_delete = BUTTON_NEW_FROM_STOCK(GTK_STOCK_DELETE);
955
956         gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_new, TRUE, FALSE, 5);
957         gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_edit, TRUE, FALSE, 5);
958         gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_delete, TRUE, FALSE, 5);
959
960
961         rep->bt_apply = OBJECT_GET_DATA(rep->bbox,GTK_STOCK_APPLY);
962         rep->bt_cancel = OBJECT_GET_DATA(rep->bbox,GTK_STOCK_CANCEL);
963         rep->bt_ok = OBJECT_GET_DATA(rep->bbox,GTK_STOCK_OK);
964
965         gtk_box_pack_end(GTK_BOX(rep->vbox), rep->bbox, FALSE, FALSE, 0);
966
967         gtk_widget_set_sensitive (rep->bt_up, FALSE);
968         gtk_widget_set_sensitive (rep->bt_down, FALSE);
969         gtk_widget_set_sensitive (rep->bt_edit, FALSE);
970         gtk_widget_set_sensitive (rep->bt_delete, FALSE);
971
972
973 #if GTK_MAJOR_VERSION < 2
974         SIGNAL_CONNECT(rep->clist, "select_row", remember_selected_row, uat);
975         SIGNAL_CONNECT(rep->clist, "unselect_row", unremember_selected_row, uat);
976 #else
977         SIGNAL_CONNECT(selection, "changed", remember_selected_row, uat);
978 #endif
979
980         SIGNAL_CONNECT(rep->bt_new, "clicked", uat_new_cb, uat);
981         SIGNAL_CONNECT(rep->bt_edit, "clicked", uat_edit_cb, uat);
982         SIGNAL_CONNECT(rep->bt_delete, "clicked", uat_delete_cb, uat);
983
984         SIGNAL_CONNECT(rep->bt_up, "clicked", uat_up_cb, uat);
985         SIGNAL_CONNECT(rep->bt_down, "clicked", uat_down_cb, uat);
986
987         SIGNAL_CONNECT(rep->bt_apply, "clicked", uat_apply_cb, uat);
988         SIGNAL_CONNECT(rep->bt_cancel, "clicked", uat_cancel_cb, uat);
989         SIGNAL_CONNECT(rep->bt_ok, "clicked", uat_ok_cb, uat);
990
991         window_set_cancel_button(rep->window, rep->bt_cancel, NULL);  /* set esc to activate cancel button */
992
993         if (uat->changed) {
994                 SIGNAL_CONNECT(GTK_WINDOW(rep->window), "delete_event", unsaved_dialog, uat);
995                 SIGNAL_CONNECT(GTK_WINDOW(rep->window), "destroy", unsaved_dialog, uat);
996         } else {
997                 SIGNAL_CONNECT(GTK_WINDOW(rep->window), "delete_event", uat_window_delete_event_cb, uat);
998                 SIGNAL_CONNECT(GTK_WINDOW(rep->window), "destroy", uat_window_delete_event_cb, uat);
999         }
1000         
1001         gtk_widget_grab_focus(rep->clist);
1002
1003         gtk_widget_show_all(rep->window);
1004         window_present(rep->window);
1005
1006         return rep->window;
1007 }
1008
1009 void uat_window_cb(GtkWidget* u _U_, void* uat) {
1010         uat_window(uat);
1011 }
1012